From 5cc43898256b130c2576f34a3d4e7ce6a3940d3e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 24 Jul 2024 15:44:10 +0200 Subject: [PATCH 01/53] fix(api): save new password when reset password of local account (#886) --- server/routes/auth.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/routes/auth.ts b/server/routes/auth.ts index 3b0d7e38..a473522f 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -730,6 +730,7 @@ authRoutes.post('/reset-password/:guid', async (req, res, next) => { }); } user.recoveryLinkExpirationDate = null; + await user.setPassword(req.body.password); userRepository.save(user); logger.info('Successfully reset password', { label: 'API', From 2be9c7dcc1f418726a19e99cfdb3933257a03c6f Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:34:54 +0500 Subject: [PATCH 02/53] fix: add missing content-type header (#887) * fix: add missing headers when commenting on an issue * fix: more missing content-type headers in post requests --- src/components/IssueDetails/IssueComment/index.tsx | 3 +++ src/components/IssueDetails/index.tsx | 6 ++++++ src/components/Login/AddEmailModal.tsx | 3 +++ src/components/Login/index.tsx | 3 +++ 4 files changed, 15 insertions(+) diff --git a/src/components/IssueDetails/IssueComment/index.tsx b/src/components/IssueDetails/IssueComment/index.tsx index aa5a65dc..0c36ca66 100644 --- a/src/components/IssueDetails/IssueComment/index.tsx +++ b/src/components/IssueDetails/IssueComment/index.tsx @@ -181,6 +181,9 @@ const IssueComment = ({ `/api/v1/issueComment/${comment.id}`, { method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ message: values.newMessage }), } ); diff --git a/src/components/IssueDetails/index.tsx b/src/components/IssueDetails/index.tsx index e4b952e2..a5ec6391 100644 --- a/src/components/IssueDetails/index.tsx +++ b/src/components/IssueDetails/index.tsx @@ -126,6 +126,9 @@ const IssueDetails = () => { try { const res = await fetch(`/api/v1/issueComment/${firstComment.id}`, { method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ message: newMessage }), }); if (!res.ok) throw new Error(); @@ -501,6 +504,9 @@ const IssueDetails = () => { `/api/v1/issue/${issueData?.id}/comment`, { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ message: values.message }), } ); diff --git a/src/components/Login/AddEmailModal.tsx b/src/components/Login/AddEmailModal.tsx index a6ce794a..f5dafa7c 100644 --- a/src/components/Login/AddEmailModal.tsx +++ b/src/components/Login/AddEmailModal.tsx @@ -59,6 +59,9 @@ const AddEmailModal: React.FC = ({ try { const res = await fetch('/api/v1/auth/jellyfin', { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ username: username, password: password, diff --git a/src/components/Login/index.tsx b/src/components/Login/index.tsx index f0e9ab42..27695d19 100644 --- a/src/components/Login/index.tsx +++ b/src/components/Login/index.tsx @@ -45,6 +45,9 @@ const Login = () => { try { const res = await fetch('/api/v1/auth/plex', { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ authToken }), }); if (!res.ok) throw new Error(); From 6cea8bba592b8db566b4d8147630385f5c377f1b Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 24 Jul 2024 19:14:04 +0200 Subject: [PATCH 03/53] fix: add missing brackets (#888) --- src/components/Settings/SettingsJellyfin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index dfc147a1..a5fb3498 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -252,7 +252,7 @@ const SettingsJellyfin: React.FC = ({ const searchParams = new URLSearchParams(params.enable ? params : {}); const res = await fetch( - `/api/v1/settings/jellyfin/library?${searchParams.toString}` + `/api/v1/settings/jellyfin/library?${searchParams.toString()}` ); if (!res.ok) throw new Error(); } else { From c80d9a853a2a3451293a5382ef183c18add0c040 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 24 Jul 2024 20:10:31 +0200 Subject: [PATCH 04/53] fix: remove protocol-relative URLs from next/image (#889) Next.js image component doesn't support protocol-relative URLs, so this PR replaces them to https URLs --- .../Discover/DiscoverNetwork/index.tsx | 2 +- .../Discover/DiscoverStudio/index.tsx | 2 +- .../MediaSlider/ShowMoreCard/index.tsx | 38 ++++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/components/Discover/DiscoverNetwork/index.tsx b/src/components/Discover/DiscoverNetwork/index.tsx index 2721f949..af3f9ed5 100644 --- a/src/components/Discover/DiscoverNetwork/index.tsx +++ b/src/components/Discover/DiscoverNetwork/index.tsx @@ -50,7 +50,7 @@ const DiscoverTvNetwork = () => { {firstResultData?.network.logoPath ? (
{firstResultData.network.name} { {firstResultData?.studio.logoPath ? (
{firstResultData.studio.name} { >
-
+
{posters[0] && ( -
+
)} {posters[1] && ( -
+
)} {posters[2] && ( -
+
)} {posters[3] && ( -
+
)} From c96ca6742e0a6d5685319c52f995fe06e439a450 Mon Sep 17 00:00:00 2001 From: Nir Israel Hen <35529491+mobihen@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:51:55 +0300 Subject: [PATCH 05/53] feat(translation): added full Hebrew translation (#871) * feat(translation): added full Hebrew translation * Update he.json fixed missing translations --- src/i18n/locale/he.json | 1461 ++++++++++++++++++++++++++++++++++----- 1 file changed, 1305 insertions(+), 156 deletions(-) diff --git a/src/i18n/locale/he.json b/src/i18n/locale/he.json index b4048b68..3dfa734b 100644 --- a/src/i18n/locale/he.json +++ b/src/i18n/locale/he.json @@ -1,175 +1,1324 @@ { - "components.ManageSlideOver.alltime": "כל הזמנים", - "components.Login.validationemailrequired": "חובה לספק כתובת מייל חוקית", - "components.NotificationTypeSelector.userissuereopenedDescription": "קבל התראה כשבעיות שפתחת נפתחות מחדש.", - "components.AppDataWarning.dockerVolumeMissingDescription": "רכיב האחסון {appDataPath} לא הוגדר כראוי. כל המידע יוסר כאשר הקונטיינר יעצור או יותחל מחדש.", - "components.CollectionDetails.overview": "תצוגה כללית", - "components.CollectionDetails.numberofmovies": "{כמות} סרטים", - "components.CollectionDetails.requestcollection": "אוסף בקשות", - "components.CollectionDetails.requestcollection4k": "אוסף בקשות ב4K", - "components.Discover.DiscoverMovieGenre.genreMovies": "סרטי {genre}", - "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} סרטים", - "components.Discover.DiscoverNetwork.networkSeries": "{network} סדרות", - "components.Discover.DiscoverStudio.studioMovies": "{studio} סרטים", - "components.Discover.DiscoverTvGenre.genreSeries": "{genre} סדרות", - "components.Discover.DiscoverTvLanguage.languageSeries": "{language} סדרות", - "components.IssueDetails.commentplaceholder": "הוסף תגובה …", - "components.IssueDetails.comments": "תגובות", - "components.IssueDetails.deleteissue": "מחק מקרה", - "components.IssueDetails.deleteissueconfirm": "האם אתה בטוח שאתה רוצה למחוק את המקרה?", - "components.AirDateBadge.airsrelative": "ישודר בעוד {relativeTime}", + "components.AirDateBadge.airedrelative": "שודר {relativeTime}", + "components.AirDateBadge.airsrelative": "ישודר {relativeTime}", + "components.AppDataWarning.dockerVolumeMissingDescription": ".בנתיב בדיקה אינה מוגדרת באופן תקין. כל המידע יימחק כאשר הקונטיינר ייעצר או יופעל מחדש {appDataPath} המחיצה", + "components.CollectionDetails.numberofmovies": "{count} סרטים", + "components.CollectionDetails.overview": "תקציר", + "components.CollectionDetails.requestcollection": "בקשת האוסף", + "components.CollectionDetails.requestcollection4k": "4K בקשת האוסף ב", + "components.Discover.CreateSlider.addSlider": "הוספת סליידר", + "components.Discover.CreateSlider.addcustomslider": "יצירת סליידר מותאם", + "components.Discover.CreateSlider.addfail": ".אין אפשרות ליצור סליידר", + "components.Discover.CreateSlider.addsuccess": ".הסליידר חדש נוצר בהצלחה", + "components.Discover.CreateSlider.editSlider": "עריכת שורת תצוגה", + "components.Discover.CreateSlider.editfail": ".אירעה שגיאה בעריכת הסליידר", + "components.Discover.CreateSlider.editsuccess": ".הסליידר נערך בהצלחה", + "components.Discover.CreateSlider.needresults": ".חייבת להיות תוצאה אחת לפחות", + "components.Discover.CreateSlider.nooptions": "אין תוצאות", + "components.Discover.CreateSlider.providetmdbgenreid": "TMDB יש לרשום מזהה ג׳אנר של", + "components.Discover.CreateSlider.providetmdbkeywordid": "TMDB יש לרשום מזהה מילת מפתח של", + "components.Discover.CreateSlider.providetmdbnetwork": "TMDB יש לרשום מזהה רשת של", + "components.Discover.CreateSlider.providetmdbsearch": "יש לרשום שאילתת חיפוש", + "components.Discover.CreateSlider.providetmdbstudio": "TMDB יש לרשום מזהה אולפן של", + "components.Discover.CreateSlider.searchGenres": "…חיפוש ג׳אנרים", + "components.Discover.CreateSlider.searchKeywords": "…מילות מפתח לחיפוש", + "components.Discover.CreateSlider.searchStudios": "…חיפוש אולפנים", + "components.Discover.CreateSlider.slidernameplaceholder": "שם הסליידר", + "components.Discover.CreateSlider.starttyping": ".יש להקליד כדי להתחיל חיפוש", + "components.Discover.CreateSlider.validationDatarequired": ".יש לרשום ערך", + "components.Discover.CreateSlider.validationTitlerequired": ".יש לרשום כותרת", + "components.Discover.DiscoverMovieGenre.genreMovies": "{genre} סרטי", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} סרטי", + "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} סרטי", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", + "components.Discover.DiscoverMovies.discovermovies": "סרטים", + "components.Discover.DiscoverMovies.sortPopularityAsc": "פופולריות בסדר עולה", + "components.Discover.DiscoverMovies.sortPopularityDesc": "פופולריות בסדר יורד", + "components.Discover.DiscoverMovies.sortTitleAsc": "כותרת (א-ת) בסדר עולה", + "components.Discover.DiscoverMovies.sortTitleDesc": "כותרת (א-ת) בסדר יורד", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "בסדר עולה TMDB דירוג", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "בסדר יורד TMDB דירוג", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "תאריך שיחרור בסדר עולה", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "תאריך שיחרור בסדר יורד", + "components.Discover.DiscoverNetwork.networkSeries": "{network} סדרות ברשת", + "components.Discover.DiscoverSliderEdit.deletefail": ".מחיקת הסליידר נכשלה", + "components.Discover.DiscoverSliderEdit.deletesuccess": ".הסליידר נמחק בהצלחה", + "components.Discover.DiscoverSliderEdit.enable": "הצגה או הסתרה", + "components.Discover.DiscoverSliderEdit.remove": "הסרה", + "components.Discover.DiscoverStudio.studioMovies": "{studio} סרטים מאופלני", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", + "components.Discover.DiscoverTv.discovertv": "סדרות", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "שודר לראשונה בסדר עולה", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "שודר לראשונה בסדר יורד", + "components.Discover.DiscoverTv.sortPopularityAsc": "פופולריות בסדר עולה", + "components.Discover.DiscoverTv.sortPopularityDesc": "פופולריות בסדר יורד", + "components.Discover.DiscoverTv.sortTitleAsc": "כותרת (א-ת) בסדר עולה", + "components.Discover.DiscoverTv.sortTitleDesc": "כותרת (א-ת) בסדר יורד", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "בסדר עולה TMDB דירוג", + "components.Discover.DiscoverTv.sortTmdbRatingDesc": "בסדר יורד TMDB דירוג", + "components.Discover.DiscoverTvGenre.genreSeries": "{genre} סדרת", + "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} סדרות", + "components.Discover.DiscoverTvLanguage.languageSeries": "{language}סדרות ב", "components.Discover.DiscoverWatchlist.discoverwatchlist": "רשימת הצפייה שלך", - "components.Discover.MovieGenreList.moviegenres": "סוגי סרטים", + "components.Discover.DiscoverWatchlist.watchlist": "Plex רשימת הצפייה של", + "components.Discover.FilterSlideover.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", + "components.Discover.FilterSlideover.clearfilters": "ניקוי מסננים פעילים", + "components.Discover.FilterSlideover.filters": "מסננים", + "components.Discover.FilterSlideover.firstAirDate": "שודר לראשונה", + "components.Discover.FilterSlideover.from": "מאת", + "components.Discover.FilterSlideover.genres": "ג׳אנרים", + "components.Discover.FilterSlideover.keywords": "מילות מפתח", + "components.Discover.FilterSlideover.originalLanguage": "שפת מקור", + "components.Discover.FilterSlideover.ratingText": "{maxValue} לבין {minValue} דירוגים בין", + "components.Discover.FilterSlideover.releaseDate": "תאריך יציאה", + "components.Discover.FilterSlideover.runtime": "זמן ריצה", + "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} זמן ריצה בין", + "components.Discover.FilterSlideover.streamingservices": "שירותי הזרמה", + "components.Discover.FilterSlideover.studio": "אולפן", + "components.Discover.FilterSlideover.tmdbuserscore": "TMDB ניקוד משתמשי", + "components.Discover.FilterSlideover.tmdbuservotecount": "TMDB הצבעות", + "components.Discover.FilterSlideover.to": "אל", + "components.Discover.FilterSlideover.voteCount": "{maxValue} ל {minValue} מספר הצבעות בין", + "components.Discover.MovieGenreList.moviegenres": "סרטים לפי ג׳אנר", + "components.Discover.MovieGenreSlider.moviegenres": "ג׳אנר סרטים", + "components.Discover.NetworkSlider.networks": "רשתות", + "components.Discover.PlexWatchlistSlider.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "רשימת הצפייה שלך", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "התווסף לאחרונה", "components.Discover.StudioSlider.studios": "אולפנים", - "components.Discover.TvGenreList.seriesgenres": "סוגי סדרות", - "components.Discover.TvGenreSlider.tvgenres": "סוגי סדרות", - "components.Discover.recentlyAdded": "נוספו לאחרונה", - "components.Discover.recentrequests": "בקשות אחרונות", - "components.Discover.trending": "חמים", + "components.Discover.TvGenreList.seriesgenres": "ג׳אנר סדרות", + "components.Discover.TvGenreSlider.tvgenres": "ג׳אנרים סדרות", + "components.Discover.createnewslider": "יצירת סליידר", + "components.Discover.customizediscover": "התאמת מה חדש", + "components.Discover.discover": "מה חדש", + "components.Discover.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", + "components.Discover.moviegenres": "סרטים לפי ג׳אנר", + "components.Discover.networks": "רשתות", + "components.Discover.plexwatchlist": "רשימת הצפייה שלך", + "components.Discover.popularmovies": "סרטים פופולריים", + "components.Discover.populartv": "הסדרות הפופולריות", + "components.Discover.recentlyAdded": "התווסף לאחרונה", + "components.Discover.recentrequests": "התבקשו לאחרונה", + "components.Discover.resetfailed": ".משהו השתבש בעת איפוס ההגדרות עבור מה חדש", + "components.Discover.resetsuccess": ".הגדרות מה חדש אופסו בהצלחה", + "components.Discover.resettodefault": "איפוס לברירת מחדל", + "components.Discover.resetwarning": "!איפוס כל הסליידרים לברירת מחדל. הדבר יוביל למחיקת סליידרים מותאמים אישית", + "components.Discover.stopediting": "הפסקת עריכה", + "components.Discover.studios": "לפי אולפנים", + "components.Discover.tmdbmoviegenre": "(TMDB) ג׳אנר סרט", + "components.Discover.tmdbmoviekeyword": "(TMDB) מילות חיפוש לסרט", + "components.Discover.tmdbmoviestreamingservices": "(TMDB) שירות הזרמת סרטים", + "components.Discover.tmdbnetwork": "(TMDB) רשת", + "components.Discover.tmdbsearch": "(TMDB) חיפוש", + "components.Discover.tmdbstudio": "(TMDB) אולפנים", + "components.Discover.tmdbtvgenre": "(TMDB) ג׳אנר של סדרה", + "components.Discover.tmdbtvkeyword": "(TMDB) מילות חיפוש לסדרה", + "components.Discover.tmdbtvstreamingservices": "TMDB שירות הזרדמת מדיה של טלויזיה", + "components.Discover.trending": "הכי נצפה", + "components.Discover.tvgenres": "סדרות לפי ג׳אנר", "components.Discover.upcoming": "סרטים שיצאו בקרוב", - "components.Discover.upcomingmovies": "סרטים שיצאו בקרוב", + "components.Discover.upcomingmovies": "סרטים בקרוב", "components.Discover.upcomingtv": "סדרות שיצאו בקרוב", - "components.DownloadBlock.estimatedtime": "{time} משוער", + "components.Discover.updatefailed": ".משהו השתבש בעדכון ההגדרות וההתאמות של מה חדש", + "components.Discover.updatesuccess": ".הגדרות מה חדש נשמרו בהצלחה", + "components.DownloadBlock.estimatedtime": "{time} בערך", + "components.DownloadBlock.formattedTitle": "{title}: עונה {seasonNumber} פרק {episodeNumber}", + "components.IssueDetails.IssueComment.areyousuredelete": "?האם למחוק את התגובה", "components.IssueDetails.IssueComment.delete": "מחיקת תגובה", - "components.IssueDetails.IssueComment.areyousuredelete": "האם תרצה למחוק את התגובה?", - "components.IssueDetails.IssueComment.edit": "לערוך תגובה", - "components.IssueDetails.IssueDescription.edit": "ערוך תיאור", + "components.IssueDetails.IssueComment.edit": "עריכת תגובה", + "components.IssueDetails.IssueComment.postedby": "{username} ע״י {relativeTime} נוצר", + "components.IssueDetails.IssueComment.postedbyedited": "(נערך) {username} ע״י {relativeTime} נוצר", + "components.IssueDetails.IssueComment.validationComment": "יש לרשום הודעה", + "components.IssueDetails.IssueDescription.deleteissue": "מחיקת תקלה", + "components.IssueDetails.IssueDescription.description": "תיאור", + "components.IssueDetails.IssueDescription.edit": "עריכת תיאור", "components.IssueDetails.allepisodes": "כל הפרקים", "components.IssueDetails.allseasons": "כל העונות", - "components.IssueDetails.closeissue": "סגור מקרה", - "components.IssueDetails.closeissueandcomment": "סגור עם תגובה", - "components.IssueDetails.episode": "פרק {episodeNumber}", - "components.IssueDetails.issuepagetitle": "מקרה", - "components.IssueDetails.playonplex": "הפעל בפלקס", - "components.IssueDetails.play4konplex": "הפעל 4K בפלקס", - "components.IssueDetails.problemepisode": "פרק מושפע", - "components.IssueDetails.toastissuedeleted": "מקרה נמחק בהצלחה!", - "components.IssueList.IssueItem.issuetype": "סוג", - "components.IssueList.IssueItem.opened": "נפתח", - "components.IssueList.IssueItem.openeduserdate": "{date} ע\"י {user}", - "components.IssueModal.issueSubtitles": "כתוביות", - "components.IssueModal.issueVideo": "וידאו", - "components.Layout.Sidebar.dashboard": "לגלות", - "components.Login.signingin": "התחברות…", - "components.Login.signinheader": "התחבר בשביל להמשיך", - "components.Login.signinwithoverseerr": "השתמש בחשבון {applicationTitle} שלך", - "components.Login.signinwithplex": "השתמש בחשבון הפלקס שלך", - "components.ManageSlideOver.downloadstatus": "הורדות", - "components.Discover.DiscoverWatchlist.watchlist": "רשימת צפייה", - "components.Discover.MovieGenreSlider.moviegenres": "סוגי סרטים", - "components.Discover.populartv": "סדרות פופולריות", - "components.IssueDetails.IssueComment.postedby": "פורסם לפני {relativeTime} ע\"י {username}", - "components.IssueDetails.IssueComment.postedbyedited": "פורסם לפני {relativeTime} ע\"י {username} (נערך)", - "components.IssueDetails.IssueDescription.description": "תיאור", - "components.IssueDetails.openedby": "#{issueId} נפתח לפני {relativeTime} ע\"י {username}", - "components.IssueDetails.openin4karr": "נפתח ב4K {arr}", - "components.IssueDetails.openinarr": "פתח ב {arr}", - "components.IssueList.IssueItem.problemepisode": "פרק מושפע", - "components.IssueList.sortAdded": "הכי עדכני", - "components.IssueList.sortModified": "עודכן לאחרונה", - "components.IssueModal.CreateIssueModal.allepisodes": "כל הפרקים", - "components.IssueModal.CreateIssueModal.providedetail": "אנא תפרט אודות המקרה שחווית.", - "components.IssueModal.CreateIssueModal.submitissue": "הגש מקרה", - "components.LanguageSelector.originalLanguageDefault": "כל השפות", - "components.Layout.Sidebar.requests": "בקשות", - "components.Layout.Sidebar.settings": "הגדרות", - "components.Layout.Sidebar.users": "משתמשים", - "components.Layout.UserDropdown.myprofile": "פרופיל", - "components.Layout.UserDropdown.settings": "הגדרות", - "components.Layout.VersionStatus.streamdevelop": "Jellyseerr פיתוח", - "components.AirDateBadge.airedrelative": "שודר ב-{relativeTime}", - "components.Discover.NetworkSlider.networks": "רשתות שידור", - "components.Discover.discover": "לגלות", - "components.Discover.plexwatchlist": "רשימת הצפייה שלך", - "components.Discover.popularmovies": "סרטים פופולרים", - "components.IssueDetails.IssueComment.validationComment": "אנא הכנס הודעה", - "components.IssueDetails.IssueDescription.deleteissue": "מחק מקרה", + "components.IssueDetails.closeissue": "סגירת תקלה", + "components.IssueDetails.closeissueandcomment": "סגירה עם תגובה", + "components.IssueDetails.commentplaceholder": "…הוספת תגובה", + "components.IssueDetails.comments": "תגובות", + "components.IssueDetails.deleteissue": "מחיקת תקלה", + "components.IssueDetails.deleteissueconfirm": "?למחוק את התקלה", + "components.IssueDetails.episode": "{episodeNumber} פרק", + "components.IssueDetails.issuepagetitle": "תקלה", "components.IssueDetails.issuetype": "סוג", "components.IssueDetails.lastupdated": "עודכן לאחרונה", "components.IssueDetails.leavecomment": "תגובה", - "components.IssueDetails.nocomments": "אין תגובות.", - "components.IssueDetails.problemseason": "עונה מושפעת", - "components.IssueDetails.reopenissue": "פתח מקרה מחדש", - "components.IssueDetails.reopenissueandcomment": "פתח מחדש עם תגובה", - "components.IssueDetails.season": "עונה {seasonNumber}", - "components.IssueDetails.toasteditdescriptionfailed": "משהו השתבש בזמן עריכת תיאור המקרה.", - "components.IssueDetails.toasteditdescriptionsuccess": "תיאור המקרה נערך בהצלחה!", - "components.IssueDetails.toastissuedeletefailed": "משהו השתבש בזמן מחיקת המקרה.", - "components.IssueDetails.toaststatusupdated": "סטאטוס המקרה עודכן בהצלחה!", - "components.IssueDetails.toaststatusupdatefailed": "משהו השתבש בזמן עדכון סטאטוס המקרה.", - "components.IssueDetails.unknownissuetype": "לא ידוע", + "components.IssueDetails.nocomments": "אין תגובות", + "components.IssueDetails.openedby": "{username} ע״י {relativeTime} נפתח {issueId}", + "components.IssueDetails.openin4karr": "{arr} 4K פתיחה ב", + "components.IssueDetails.openinarr": "{arr} פתיחה ב", + "components.IssueDetails.play4konplex": "{mediaServerName} 4K ניגון ב", + "components.IssueDetails.playonplex": "{mediaServerName} ניגון ב", + "components.IssueDetails.problemepisode": "בעיה בפרק", + "components.IssueDetails.problemseason": "בעיה בעונה", + "components.IssueDetails.reopenissue": "פתיחת תקלה מחדש", + "components.IssueDetails.reopenissueandcomment": "פתיחה מחדש עם הערה", + "components.IssueDetails.season": "{seasonNumber} עונה", + "components.IssueDetails.toasteditdescriptionfailed": ".משהו השתבש בעת עדכון תיאור התקלה", + "components.IssueDetails.toasteditdescriptionsuccess": "!תיאור התקלה עודכן בהצלחה", + "components.IssueDetails.toastissuedeleted": "!התקלה נמחקה בהצלחה", + "components.IssueDetails.toastissuedeletefailed": ".משהו השתבש בעת מחיקת התקלה", + "components.IssueDetails.toaststatusupdated": "!סטאטוס התקלה עודכן בהצלחה", + "components.IssueDetails.toaststatusupdatefailed": ".משהו השתבש בעת עדכון סטאטוס התקלה", + "components.IssueDetails.unknownissuetype": "ללא", + "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {פרק} other {פרקים}}", "components.IssueList.IssueItem.issuestatus": "סטאטוס", - "components.IssueList.IssueItem.unknownissuetype": "לא ידוע", - "components.IssueList.IssueItem.viewissue": "צפה במקרה", - "components.IssueList.issues": "מקרים", - "components.IssueList.showallissues": "הצג את כל המקרים", + "components.IssueList.IssueItem.issuetype": "סוג", + "components.IssueList.IssueItem.opened": "נפתחה", + "components.IssueList.IssueItem.openeduserdate": "ע״י {user} לפני {time}", + "components.IssueList.IssueItem.problemepisode": "משפיע על פרק", + "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", + "components.IssueList.IssueItem.unknownissuetype": "ללא", + "components.IssueList.IssueItem.viewissue": "הצגת התקלה", + "components.IssueList.issues": "תקלות", + "components.IssueList.showallissues": "הצג את כל התקלות", + "components.IssueList.sortAdded": "הכי עדכני", + "components.IssueList.sortModified": "השתנה לאחרונה", + "components.IssueModal.CreateIssueModal.allepisodes": "כל הפרקים", "components.IssueModal.CreateIssueModal.allseasons": "כל העונות", - "components.IssueModal.CreateIssueModal.episode": "פרק {episodeNumber}", - "components.IssueModal.CreateIssueModal.extras": "תוספות", - "components.IssueModal.CreateIssueModal.problemepisode": "פרק מושפע", - "components.IssueModal.CreateIssueModal.season": "עונה {seasonNumber}", - "components.IssueModal.CreateIssueModal.problemseason": "עונה מושפעת", - "components.IssueModal.CreateIssueModal.toastFailedCreate": "משהו השתבש בזמן הגשת מקרה.", - "components.IssueModal.CreateIssueModal.reportissue": "דווח על מקרה", - "components.IssueModal.CreateIssueModal.whatswrong": "מה השתבש?", - "components.IssueModal.issueAudio": "שמע/אודיו", + "components.IssueModal.CreateIssueModal.episode": "{episodeNumber} פרק", + "components.IssueModal.CreateIssueModal.extras": "אקסטה", + "components.IssueModal.CreateIssueModal.problemepisode": "בעיה בפרק", + "components.IssueModal.CreateIssueModal.problemseason": "בעיה בעונה", + "components.IssueModal.CreateIssueModal.providedetail": ".יש לספק תיאור מפורט אודות התקלה שאירעה", + "components.IssueModal.CreateIssueModal.reportissue": "דיווח על תקלה", + "components.IssueModal.CreateIssueModal.season": "{seasonNumber} עונה", + "components.IssueModal.CreateIssueModal.submitissue": "יצירת תקלה", + "components.IssueModal.CreateIssueModal.toastFailedCreate": ".משהו השתבש בעת יצירת התקלה", + "components.IssueModal.CreateIssueModal.toastSuccessCreate": "!התקלה נוצרה בהצלחה עבור {title}", + "components.IssueModal.CreateIssueModal.toastviewissue": "צפייה בתקלה", + "components.IssueModal.CreateIssueModal.validationMessageRequired": "יש לרשום תיאור", + "components.IssueModal.CreateIssueModal.whatswrong": "?מהי התקלה", + "components.IssueModal.issueAudio": "אודיו", "components.IssueModal.issueOther": "אחר", - "components.IssueModal.CreateIssueModal.toastviewissue": "צפה במקרה", - "components.IssueModal.CreateIssueModal.validationMessageRequired": "אנא כתוב תיאור", - "components.Layout.LanguagePicker.displaylanguage": "שפת תצוגה", + "components.IssueModal.issueSubtitles": "כתוביות", + "components.IssueModal.issueVideo": "וידאו", + "components.LanguageSelector.languageServerDefault": "({language}) ברירת מחדל", + "components.LanguageSelector.originalLanguageDefault": "כל השפות", + "components.Layout.LanguagePicker.displaylanguage": "שפת התצוגה", "components.Layout.SearchInput.searchPlaceholder": "חיפוש סרטים וסדרות", - "components.LanguageSelector.languageServerDefault": "ברירת מחדל({language})", - "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "בקשות סרטים", - "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "בקשות סדרות", - "components.Login.forgotpassword": "שכחת סיסמה?", - "components.Layout.VersionStatus.streamstable": "Jellyseerr יציבה", - "components.Login.email": "כתובת אימייל", - "components.ManageSlideOver.manageModalAdvanced": "מתקדם", - "components.ManageSlideOver.manageModalClearMedia": "מחק מידע", - "components.Discover.emptywatchlist": "מדיה נוספה לתוך רשימת צפייה תוצג פה.", - "components.IssueModal.CreateIssueModal.toastSuccessCreate": "דווח מקרה של {title} הוגש בהצלחה!", - "components.Layout.Sidebar.issues": "מקרים", + "components.Layout.Sidebar.browsemovies": "סרטים", + "components.Layout.Sidebar.browsetv": "סדרות", + "components.Layout.Sidebar.dashboard": "מה חדש", + "components.Layout.Sidebar.issues": "תקלות", + "components.Layout.Sidebar.requests": "בקשות", + "components.Layout.Sidebar.settings": "הגדרות", + "components.Layout.Sidebar.users": "משתמשים", + "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "בקשת סרטים", + "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "בקשת סדרות", + "components.Layout.UserDropdown.myprofile": "פרופיל", "components.Layout.UserDropdown.requests": "בקשות", - "components.Layout.UserDropdown.signout": "התנתק", - "components.Layout.VersionStatus.outofdate": "לא מעודכן", - "components.Login.loginerror": "משהו השתבש בזמן ההתחברות.", + "components.Layout.UserDropdown.settings": "הגדרות", + "components.Layout.UserDropdown.signout": "התנתקות", + "components.Layout.UserWarnings.emailInvalid": ".כתובת דואר אינה חוקית", + "components.Layout.UserWarnings.emailRequired": ".יש לרשום כתובת מייל", + "components.Layout.UserWarnings.passwordRequired": "נדרשת סיסמה.", + "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} behind", + "components.Layout.VersionStatus.outofdate": "נדרש עידכון", + "components.Layout.VersionStatus.streamdevelop": "Jellyseerr פיתוח", + "components.Layout.VersionStatus.streamstable": "Jellyseerr יציב", + "components.Login.adminerror": "נדרש להתחבר עם חשבון מנהל", + "components.Login.credentialerror": ".שם המשתמש או הסיסמה שגויים", + "components.Login.description": "בחיבור ראשוני אל {applicationName} יש להוסיף כתובת מייל תקינה.", + "components.Login.email": "מייל", + "components.Login.emailtooltip": ".{mediaServerName} אין צורך לשייך את הכתובת עם שרת", + "components.Login.enablessl": "SSL שימוש ב", + "components.Login.forgotpassword": "שכחת סיסמה?", + "components.Login.hostname": "{mediaServerName} כתובת", + "components.Login.initialsignin": "התחברות", + "components.Login.initialsigningin": "…מתחבר", + "components.Login.invalidurlerror": ".{mediaServerName} אין אפשרות להתחבר אל", + "components.Login.loginerror": "משהו השתבש בעת ההחברות.", "components.Login.password": "סיסמה", + "components.Login.port": "פורט", + "components.Login.save": "הוספה", + "components.Login.saving": "…מוסיף", "components.Login.signin": "התחברות", - "components.Login.validationpasswordrequired": "חובה לכתוב סיסמה", - "components.Discover.CreateSlider.editsuccess": "ערוך סליידרהגדרות התאמה אישית שמורות.", - "components.Discover.CreateSlider.slidernameplaceholder": "שם הסליידר", - "components.Discover.DiscoverMovies.sortPopularityDesc": "פופולאריות יורדץ", - "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "דירוג TMDB עולה", - "components.Discover.CreateSlider.searchStudios": "חפש אולפנים…", - "components.Discover.DiscoverMovies.sortReleaseDateDesc": "תאריך שחרור יורד", - "components.Discover.CreateSlider.providetmdbnetwork": "הזמן מזהה רשת TMDB", - "components.Discover.CreateSlider.addfail": "שגיאה ביצירת סליידר.", - "components.Discover.DiscoverMovies.sortPopularityAsc": "פופולאריות עולה", - "components.Discover.CreateSlider.needresults": "חייב שתהיה לפחות תוצאה אחת.", - "components.Discover.CreateSlider.addcustomslider": "צור סליידר מותאם אישית", - "components.Discover.CreateSlider.editSlider": "ערוך סליידר", - "components.Discover.CreateSlider.validationDatarequired": "עליך לספק ערך.", - "components.Discover.DiscoverTv.discovertv": "סדרה", - "components.Discover.DiscoverSliderEdit.deletefail": "שגיאה במחיקת סליידר.", - "components.Discover.CreateSlider.providetmdbstudio": "הזן מזהה אולפן TMDB", - "components.Discover.DiscoverMovies.sortTitleDesc": "כותר (ת-א) יורד", - "components.Discover.CreateSlider.searchGenres": "חפש ז'אנרים…", - "components.Discover.CreateSlider.editfail": "שגיאה בעריכת סליידר.", - "components.Discover.CreateSlider.starttyping": "התחל לכתוב כדי לחפש.", - "components.Discover.DiscoverSliderEdit.enable": "שנה נראות", - "components.Discover.CreateSlider.addSlider": "הוסף סליידר", - "components.Discover.CreateSlider.providetmdbsearch": "הזן מילת חיפוש", - "components.Discover.CreateSlider.providetmdbkeywordid": "הזן מילת חיפוש TMDB", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "סרטי {keywordTitle}", - "components.Discover.CreateSlider.validationTitlerequired": "עליך לספק כותרת.", - "components.Discover.DiscoverMovies.sortReleaseDateAsc": "תאריך שחרור עולה", - "components.Discover.CreateSlider.nooptions": "אין תוצאות.", - "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "דירוג TMDB יורד", - "components.Discover.CreateSlider.searchKeywords": "חפש מילות מפתח…", - "components.Discover.CreateSlider.addsuccess": "צור סליידר חדש והגדרות התאמה אישית שמורות.", - "components.Discover.DiscoverSliderEdit.deletesuccess": "סליידר נמחק בהצלחה.", - "components.Discover.DiscoverMovies.discovermovies": "סרטים", - "components.Discover.DiscoverMovies.sortTitleAsc": "כותר (א-ת) עולה", - "components.Discover.CreateSlider.providetmdbgenreid": "הזן מזהה ז'אנר TMDB", - "components.Discover.DiscoverSliderEdit.remove": "הסר" + "components.Login.signingin": "…מתחבר", + "components.Login.signinheader": "יש להתחבר בכדי להמשיך", + "components.Login.signinwithjellyfin": "{mediaServerName} שימוש בחשבון", + "components.Login.signinwithoverseerr": "{applicationTitle} שימוש בחשבון", + "components.Login.signinwithplex": "שימוש בחשבון Plex", + "components.Login.title": "הוסף מייל", + "components.Login.urlBase": "תחילית לכתובת", + "components.Login.username": "משתמש", + "components.Login.validationEmailFormat": "מייל שגוי", + "components.Login.validationEmailRequired": "יש לרשום מייל", + "components.Login.validationHostnameRequired": "יש לרשום שם מארח או כתובת חוקיים", + "components.Login.validationPortRequired": "יש לרשום מספר פורט תקין", + "components.Login.validationUrlBaseLeadingSlash": "יש להתחיל עם סלאש בתחילית לכתובת", + "components.Login.validationUrlBaseTrailingSlash": "אין לסיים את תחילית הכתובת עם סלאש", + "components.Login.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", + "components.Login.validationemailformat": "יש לרשום כתובת מייל", + "components.Login.validationemailrequired": "יש לרשום כתובת מייל תקינה", + "components.Login.validationhostformat": "נדרשת כתובת שרת תקינה", + "components.Login.validationhostrequired": "יש לרשום כתובת שרת", + "components.Login.validationpasswordrequired": "יש לרשום סיסמה", + "components.Login.validationusernamerequired": "יש לרשום שם משתמש", + "components.ManageSlideOver.alltime": "כל הזמנים", + "components.ManageSlideOver.downloadstatus": "הורדות", + "components.ManageSlideOver.manageModalAdvanced": "מתקדם", + "components.ManageSlideOver.manageModalClearMedia": "נקה מידע", + "components.ManageSlideOver.manageModalClearMediaWarning": "* {mediaServerName} הדבר יוביל למחיקה בלתי הפיכה של ה{mediaType} כל האינפורמציה תיווצר מחדש בסריקה הבאה {mediaServerName} אם זה קיים ב", + "components.ManageSlideOver.manageModalIssues": "תקלות פתוחות", + "components.ManageSlideOver.manageModalMedia": "מדיה", + "components.ManageSlideOver.manageModalMedia4k": "4K מדיה", + "components.ManageSlideOver.manageModalNoRequests": "אין בקשות.", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* {arr} הדבר יוביל למחיקה לצמיתות של ה{mediaType} כולל כל הקבצים ב", + "components.ManageSlideOver.manageModalRequests": "בקשות", + "components.ManageSlideOver.manageModalTitle": "ניהול ה{mediaType}", + "components.ManageSlideOver.mark4kavailable": "סימון 4K כזמין", + "components.ManageSlideOver.markallseasons4kavailable": "סימון כל העונות זמינות 4K", + "components.ManageSlideOver.markallseasonsavailable": "סימון כל העונות כזמינות", + "components.ManageSlideOver.markavailable": "סימון כזמין", + "components.ManageSlideOver.movie": "סרט", + "components.ManageSlideOver.openarr": "{arr} - פתיחה ב", + "components.ManageSlideOver.openarr4k": "פתיחה ב 4K {arr}", + "components.ManageSlideOver.opentautulli": "Tautulli - פתיחה ב", + "components.ManageSlideOver.pastdays": "ימים האחרונים {days, number} ב", + "components.ManageSlideOver.playedby": "נוגן ע״י", + "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {נוגן} other {נוגנו}}", + "components.ManageSlideOver.removearr": "{arr} - הסרה מ", + "components.ManageSlideOver.removearr4k": "4K {arr} - הסרה מ", + "components.ManageSlideOver.tvshow": "סדרה", + "components.MediaSlider.ShowMoreCard.seemore": "הצג עוד", + "components.MovieDetails.MovieCast.fullcast": "כל הליהוק", + "components.MovieDetails.MovieCrew.fullcrew": "הצוות המלא", + "components.MovieDetails.budget": "תקציב", + "components.MovieDetails.cast": "ליהוק", + "components.MovieDetails.digitalrelease": "שיחרור דיגטילי", + "components.MovieDetails.downloadstatus": "סטאטוס הורדה", + "components.MovieDetails.imdbuserscore": "IMDB ציון צופים", + "components.MovieDetails.managemovie": "נהל סרט", + "components.MovieDetails.mark4kavailable": "4K סימון כזמין ב", + "components.MovieDetails.markavailable": "סימון כזמין", + "components.MovieDetails.openradarr": "Radarr פתח סרט ב", + "components.MovieDetails.openradarr4k": "4K Radarr פתח סרט ב", + "components.MovieDetails.originallanguage": "שפת מקור", + "components.MovieDetails.originaltitle": "שם מקורי", + "components.MovieDetails.overview": "תקציר", + "components.MovieDetails.overviewunavailable": "תקציר אינו זמין", + "components.MovieDetails.physicalrelease": "שיחרור מדיה", + "components.MovieDetails.play": "{mediaServerName} ניגון ב", + "components.MovieDetails.play4k": "{mediaServerName} 4K ניגון ב", + "components.MovieDetails.productioncountries": "מפיקה {countryCount, plural, one {מדינה} other {מדינות}}", + "components.MovieDetails.recommendations": "דומים לסרט", + "components.MovieDetails.releasedate": "{releaseCount, plural, one {תאריך שיחרור} other {תאריכי שיחרור}}", + "components.MovieDetails.reportissue": "דיווח על תקלה", + "components.MovieDetails.revenue": "מחזור הכנסות", + "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes דירוג קהל", + "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes טומטומטר של", + "components.MovieDetails.runtime": "{minutes} דקות", + "components.MovieDetails.showless": "הצג פחות", + "components.MovieDetails.showmore": "עוד", + "components.MovieDetails.similar": "סרטים דומים", + "components.MovieDetails.streamingproviders": "מנוגן ב", + "components.MovieDetails.studio": "{studioCount, plural, one {אולפן} other {אולפנים}}", + "components.MovieDetails.theatricalrelease": "שיחרור תיאטרלי", + "components.MovieDetails.tmdbuserscore": "TMDB דירוג", + "components.MovieDetails.viewfullcrew": "הצגת הצוות המלא", + "components.MovieDetails.watchtrailer": "צפייה בטריילר", + "components.NotificationTypeSelector.adminissuecommentDescription": "קבלת התראות כשמשתמשים מוסיפים תגובה על התקלה", + "components.NotificationTypeSelector.adminissuereopenedDescription": ".קבלת התראות כשתקלות נפתחות מחדש ע״י משתמשים", + "components.NotificationTypeSelector.adminissueresolvedDescription": ".קבלת התראות כשמשתמש אחר פותר את התקלה", + "components.NotificationTypeSelector.issuecomment": "תגובה על תקלה", + "components.NotificationTypeSelector.issuecommentDescription": ".קבלת התראות כשיש תגובות חדשות לתקלה", + "components.NotificationTypeSelector.issuecreated": "דיווח על תקלה", + "components.NotificationTypeSelector.issuecreatedDescription": ".קבלת התראות כשמדווחים על תקלות", + "components.NotificationTypeSelector.issuereopened": "תקלה נפתחה מחדש", + "components.NotificationTypeSelector.issuereopenedDescription": ".קבלת התראות כשפותחים תקלות מחדש", + "components.NotificationTypeSelector.issueresolved": "תקלה נפתרה", + "components.NotificationTypeSelector.issueresolvedDescription": ".קבלת התראות כשתקלות נפתרות", + "components.NotificationTypeSelector.mediaAutoApproved": "בקשה אושרה אוטומטית", + "components.NotificationTypeSelector.mediaAutoApprovedDescription": ".קבלת התראות כשמשתמשים יוצרים בקשת מדיה שמאושרת אוטומטית", + "components.NotificationTypeSelector.mediaapproved": "בקשה אושרה", + "components.NotificationTypeSelector.mediaapprovedDescription": ".קבלת התראות כשבקשות מדיה מאושרות ידנית", + "components.NotificationTypeSelector.mediaautorequested": "בקשה נוצרת אוטומטית", + "components.NotificationTypeSelector.mediaautorequestedDescription": ".קבלת התראות כשבקשה נוצרת אוטומטית מרשימת הצפייה שלך", + "components.NotificationTypeSelector.mediaavailable": "ניתן ליצור בקשות", + "components.NotificationTypeSelector.mediaavailableDescription": ".קבלת התראות כשניתן ליצור בקשות", + "components.NotificationTypeSelector.mediadeclined": "בקשה נדחתה", + "components.NotificationTypeSelector.mediadeclinedDescription": ".קבלת הראות כשבקשות מדיה נדחות", + "components.NotificationTypeSelector.mediafailed": "עיבוד הבקשה נכשל", + "components.NotificationTypeSelector.mediafailedDescription": ".Radarr או Sonarr קבלת התראות כשבקשות המדיה אינן מתווספות אל ", + "components.NotificationTypeSelector.mediarequested": "בקשה ממתינה לאישור", + "components.NotificationTypeSelector.mediarequestedDescription": ".קבלת התראות כשמשתמשים יוצרים בקשות מדיה שדורשות אישור", + "components.NotificationTypeSelector.notificationTypes": "סוגי התראות", + "components.NotificationTypeSelector.userissuecommentDescription": ".קבלת התראות כשתקלות שדיווחת מקבלות תגובות חדשות", + "components.NotificationTypeSelector.userissuecreatedDescription": ".קבלת התראות כשמשתמשים מדווחים על תקלות", + "components.NotificationTypeSelector.userissuereopenedDescription": ".קבלת התראות שתקלות שדיווחת נפתחות מחדש", + "components.NotificationTypeSelector.userissueresolvedDescription": ".קבלת התראות שתקלות שדיווחת נפתרות", + "components.NotificationTypeSelector.usermediaAutoApprovedDescription": ".קבלת התראות כשמשתמשים אחרים מבקשים מדיה שאושרה אוטומטית", + "components.NotificationTypeSelector.usermediaapprovedDescription": ".קבלת התראות כשבקשות המדיה שביקשת אושרו", + "components.NotificationTypeSelector.usermediaavailableDescription": ".קבלת התראות כשניתן ליצור בקשות חדשות", + "components.NotificationTypeSelector.usermediadeclinedDescription": ".קבלת התראות כשבקשות המדיה שביקשת נדחו", + "components.NotificationTypeSelector.usermediafailedDescription": ".Radarr או Sonarr קבלת התראות כשבקשות המדיה אינן מתווספות אל ", + "components.NotificationTypeSelector.usermediarequestedDescription": ".קבלת התראות כשמשתמשים אחרים מבקשים מדיה שזקוקה לאישור", + "components.PermissionEdit.admin": "אדמין", + "components.PermissionEdit.adminDescription": ".גישת מנהל מלאה. עוקף את כל ההרשאות האחרות", + "components.PermissionEdit.advancedrequest": "בקשות מתקדמות", + "components.PermissionEdit.advancedrequestDescription": ".הענקת הרשאות לשינוי מתקדם של אפשרויות מדיה", + "components.PermissionEdit.autoapprove": "אישור בקשות אוטומטי", + "components.PermissionEdit.autoapprove4k": "4K אישור בקשות אוטומטי", + "components.PermissionEdit.autoapprove4kDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של מדיה", + "components.PermissionEdit.autoapprove4kMovies": "4K אישור בקשות אוטומטי לסרטים", + "components.PermissionEdit.autoapprove4kMoviesDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של סרטים", + "components.PermissionEdit.autoapprove4kSeries": "4K אישור בקשות אוטומטי לסדרות", + "components.PermissionEdit.autoapprove4kSeriesDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של סדרות", + "components.PermissionEdit.autoapproveDescription": ".הענקת הרשאה לאישור אוטומטי של כל בקשות המדיה באיכות גבוהה", + "components.PermissionEdit.autoapproveMovies": "אישור בקשה אוטומטי של סרטים", + "components.PermissionEdit.autoapproveMoviesDescription": ".הענקת הרשאה לאישור בקשות אוטומטי של סרטים באיכות גבוהה", + "components.PermissionEdit.autoapproveSeries": "אישור בקשה אוטומטי של סדרות", + "components.PermissionEdit.autoapproveSeriesDescription": "אישור בקשות אוטומטי עבור סדרות באיכות גבוהה", + "components.PermissionEdit.autorequest": "בקשות אוטומטיות", + "components.PermissionEdit.autorequestDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות עבור מדיה באיכות גבוהה באמצעות רשימת צפייה של", + "components.PermissionEdit.autorequestMovies": "בקשת סרטים אוטומטית", + "components.PermissionEdit.autorequestMoviesDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות סרטים באיכות גבוהה באמצעות רשימת צפייה של", + "components.PermissionEdit.autorequestSeries": "בקשת סדרות אוטומטית", + "components.PermissionEdit.autorequestSeriesDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות סדרות באיכות גבוהה באמצעות רשימת צפייה של", + "components.PermissionEdit.createissues": "דיווח על תקלות", + "components.PermissionEdit.createissuesDescription": ".הענקת הרשאות לדיווח על תקלות מדיה", + "components.PermissionEdit.manageissues": "ניהול תקלות", + "components.PermissionEdit.manageissuesDescription": ".הענקת הרשאות לניהול תקלות מדיה", + "components.PermissionEdit.managerequests": "ניהול בקשות", + "components.PermissionEdit.managerequestsDescription": ".הענקת הרשאות לניהול בקשות מדיה. כל הבקשות שנוצרות ע|י המשתמש עם הרשאה זו יאושרו אוטומטית", + "components.PermissionEdit.request": "בקשות", + "components.PermissionEdit.request4k": "4K בקשות", + "components.PermissionEdit.request4kDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של", + "components.PermissionEdit.request4kMovies": "4K בקשת סרטים", + "components.PermissionEdit.request4kMoviesDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של סרטים", + "components.PermissionEdit.request4kTv": "4K בקשת סדרות", + "components.PermissionEdit.request4kTvDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של סדרות", + "components.PermissionEdit.requestDescription": ".הענקת הרשאות ליצירת בקשות מדיה באיכות גבוהה", + "components.PermissionEdit.requestMovies": "בקשת סרטים", + "components.PermissionEdit.requestMoviesDescription": ".הענקת הרשאות ליצירת בקשות סרטים באיכות גבוהה", + "components.PermissionEdit.requestTv": "בקשת סדרות", + "components.PermissionEdit.requestTvDescription": ".הענקת הרשאות ליצירת בקשות סדרות באיכות גבוהה", + "components.PermissionEdit.users": "ניהול משתמשים", + "components.PermissionEdit.usersDescription": ".הענקת הרשאות לניהול משתמשים. משתמשים עם ההרשאה הזו אינם יכולים להעניק הראשת מנהל", + "components.PermissionEdit.viewissues": "צפייה בתקלות", + "components.PermissionEdit.viewissuesDescription": ".הענקת הרשאות לצפייה בתקלות מדיה שדווחו ע״י משתמשים", + "components.PermissionEdit.viewrecent": "צפייה בהתווסף לאחרונה", + "components.PermissionEdit.viewrecentDescription": ".הענקת הרשאות לצפייה ברשימה של המדיה שהתווספה לאחרונה", + "components.PermissionEdit.viewrequests": "הצגת הבקשות", + "components.PermissionEdit.viewrequestsDescription": ".הענקת הרשאות להצגת בקשות מדיה שנוצרו ע״י משתמשים", + "components.PermissionEdit.viewwatchlists": "{mediaServerName} צפייה ברשימות צפייה של", + "components.PermissionEdit.viewwatchlistsDescription": ".{mediaServerName} הענקת הרשאה לצפייה ברשימות צפייה של", + "components.PersonDetails.alsoknownas": "{names} מוכר גם כ", + "components.PersonDetails.appearsin": "הופעות", + "components.PersonDetails.ascharacter": "{character} בתור", + "components.PersonDetails.birthdate": "{birthdate} תאריך לידה", + "components.PersonDetails.crewmember": "צוות", + "components.PersonDetails.lifespan": "{birthdate} – {deathdate}", + "components.PlexLoginButton.signingin": "…מתחבר", + "components.PlexLoginButton.signinwithplex": "החברות", + "components.QuotaSelector.days": "{count, plural, one {יום} other {ימים}}", + "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} per {quotaDays} {days}", + "components.QuotaSelector.movies": "{count, plural, one {סרט} other {סרטים}}", + "components.QuotaSelector.seasons": "{count, plural, one {עונה} other {seasonעונותs}}", + "components.QuotaSelector.tvRequests": "{quotaLimit} {seasons} per {quotaDays} {days}", + "components.QuotaSelector.unlimited": "ללא הגבלה", + "components.RegionSelector.regionDefault": "כל האיזורים", + "components.RegionSelector.regionServerDefault": "({region}) ברירת מחדל", + "components.RequestBlock.approve": "אישור בקשה", + "components.RequestBlock.decline": "דחיית בקשה", + "components.RequestBlock.delete": "מחיקת בקשה", + "components.RequestBlock.edit": "עריכת בקשה", + "components.RequestBlock.languageprofile": "פרופיל שפה", + "components.RequestBlock.lastmodifiedby": "שונה לאחרונה ע״י", + "components.RequestBlock.profilechanged": "פרופיל איכות", + "components.RequestBlock.requestdate": "תאריך הבקשה", + "components.RequestBlock.requestedby": "בקשה ע״י", + "components.RequestBlock.requestoverrides": "עקיפות שיש לבקשה", + "components.RequestBlock.rootfolder": "תיקיית מדיה", + "components.RequestBlock.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", + "components.RequestBlock.server": "שרת יעד", + "components.RequestButton.approve4krequests": "אישור {requestCount, plural, one {4K בקשת} other {{requestCount} 4K בקשות}}", + "components.RequestButton.approverequest": "אישור בקשה", + "components.RequestButton.approverequest4k": "4K אישור בקשת", + "components.RequestButton.approverequests": "אישור {requestCount, plural, one {בקשה} other {{requestCount} בקשות}}", + "components.RequestButton.decline4krequests": "דחיית {requestCount, plural, one {4K בקשת} other {{requestCount} 4K בקשות}}", + "components.RequestButton.declinerequest": "דחיית בקשה", + "components.RequestButton.declinerequest4k": "4K דחיית בקשת", + "components.RequestButton.declinerequests": "דחיית {requestCount, plural, one {בקשה} other {{requestCount} בקשות}}", + "components.RequestButton.requestmore": "הוספה לבקשה", + "components.RequestButton.requestmore4k": "4K הוספה לבקשת", + "components.RequestButton.viewrequest": "הצגת הבקשה", + "components.RequestButton.viewrequest4k": "4K הצגת בקשת", + "components.RequestCard.approverequest": "אישור בקשה", + "components.RequestCard.cancelrequest": "ביטול בקשה", + "components.RequestCard.declinerequest": "דחיית בקשה", + "components.RequestCard.deleterequest": "מחיקת בקשה", + "components.RequestCard.editrequest": "עריכת בקשה", + "components.RequestCard.failedretry": ".משהו השתשבש בבקשה החוזרת", + "components.RequestCard.mediaerror": "חסר {mediaType}", + "components.RequestCard.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", + "components.RequestCard.tmdbid": "TMDB ID", + "components.RequestCard.tvdbid": "TheTVDB ID", + "components.RequestCard.unknowntitle": "כותר חסר", + "components.RequestList.RequestItem.cancelRequest": "ביטול בקשה", + "components.RequestList.RequestItem.deleterequest": "מחיקת בקשה", + "components.RequestList.RequestItem.editrequest": "עריכת בקשה", + "components.RequestList.RequestItem.failedretry": ".משהו השתשבש בבקשה החוזרת", + "components.RequestList.RequestItem.mediaerror": "חסר {mediaType}", + "components.RequestList.RequestItem.modified": "השתנה", + "components.RequestList.RequestItem.modifieduserdate": "ע״י {date} {user} ", + "components.RequestList.RequestItem.requested": "התבקש", + "components.RequestList.RequestItem.requesteddate": "התבקש", + "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", + "components.RequestList.RequestItem.tmdbid": "TMDB ID", + "components.RequestList.RequestItem.tvdbid": "TheTVDB ID", + "components.RequestList.RequestItem.unknowntitle": "כותר חסר", + "components.RequestList.requests": "בקשות", + "components.RequestList.showallrequests": "הצגת כל הבקשות", + "components.RequestList.sortAdded": "הכי עדכני", + "components.RequestList.sortModified": "השתנה לאחרונה", + "components.RequestModal.AdvancedRequester.advancedoptions": "מתקדם", + "components.RequestModal.AdvancedRequester.animenote": ". זוהי סדרת אנימה *", + "components.RequestModal.AdvancedRequester.default": "{name} (Default)", + "components.RequestModal.AdvancedRequester.destinationserver": "שרת יעד", + "components.RequestModal.AdvancedRequester.folder": "{path} ({space})", + "components.RequestModal.AdvancedRequester.languageprofile": "פרופיל שפה", + "components.RequestModal.AdvancedRequester.notagoptions": ".אין תגיות", + "components.RequestModal.AdvancedRequester.qualityprofile": "פרופיל איכות", + "components.RequestModal.AdvancedRequester.requestas": "בקשה בתור", + "components.RequestModal.AdvancedRequester.rootfolder": "ספריית מדיה", + "components.RequestModal.AdvancedRequester.selecttags": "בחירת תגיות", + "components.RequestModal.AdvancedRequester.tags": "תגיות", + "components.RequestModal.QuotaDisplay.allowedRequests": ".ימים {days} כל {limit} {type} מותר לבקש", + "components.RequestModal.QuotaDisplay.allowedRequestsUser": ".ימים {days} כל {limit} {type} למשתמש מותר לבקש", + "components.RequestModal.QuotaDisplay.movie": "סרט", + "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {סרט} other {סרטים}}", + "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "אין בקשות עונות שנותרו", + "components.RequestModal.QuotaDisplay.quotaLink": ".בעמוד הפרופיל ניתן לצפות בתקציר מגבלת בקשות", + "components.RequestModal.QuotaDisplay.quotaLinkUser": "You can view a summary of this user's request limits on their profile page.", + "components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {No} other {#}} {type} {remaining, plural, one {בקשה} other {בקשות}} remaining", + "components.RequestModal.QuotaDisplay.requiredquota": ".בכדי לבצע בקשה עבור סדרה זו {seasons} {seasons, plural, one {בקשת סדרה} other {בקשות סדרה}} צריך להיות ברשותך לפחות", + "components.RequestModal.QuotaDisplay.requiredquotaUser": ".בכדי לבצע בקשה עבור סדרה זו {seasons} {seasons, plural, one {בקשת סדרה} other {בקשות סדרה}} למשתמש זה צריך להיות לפחות", + "components.RequestModal.QuotaDisplay.season": "עונה", + "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {עונה} other {עונות}}", + "components.RequestModal.SearchByNameModal.nomatches": ".אין התאמות עבור סדרה זו", + "components.RequestModal.SearchByNameModal.notvdbiddescription": ".אין אפשרות למצוא התאמה אוטומטית. יש לבחור את הסדרה הנכונה ברשימה מטה", + "components.RequestModal.alreadyrequested": "כבר התבקש", + "components.RequestModal.approve": "אישור הבקשה", + "components.RequestModal.autoapproval": "אישור אוטומטי", + "components.RequestModal.cancel": "ביטול הבקשה", + "components.RequestModal.edit": "עריכת הבקשה", + "components.RequestModal.errorediting": ".משהו השתבש בעת עריכת הבקשה", + "components.RequestModal.extras": "אקסטרה", + "components.RequestModal.numberofepisodes": "פרקים #", + "components.RequestModal.pending4krequest": "בקשת 4K ממתינה", + "components.RequestModal.pendingapproval": ".הבקשה ממתינה לאישור", + "components.RequestModal.pendingrequest": "בקשה ממתינה", + "components.RequestModal.requestApproved": "!אושרה {title} הבקשה עבור", + "components.RequestModal.requestCancel": ".בוטלה {title} הבקשה עבור", + "components.RequestModal.requestSuccess": "!{title} התבקשה בהצלחה", + "components.RequestModal.requestadmin": ".בקשה זו תאושר אוטומטית", + "components.RequestModal.requestcancelled": ".בוטלה {title} הבקשה עבור", + "components.RequestModal.requestcollection4ktitle": "4K בקשת האוסף ב", + "components.RequestModal.requestcollectiontitle": "בקשת האוסף", + "components.RequestModal.requestedited": "!נערכה בהצלחה {title} הבקשה עבור", + "components.RequestModal.requesterror": ".משהו השתבש בהגשת הבקשה", + "components.RequestModal.requestfrom": ".ממתינה לאישור {username} הבקשה של", + "components.RequestModal.requestmovie4ktitle": "4K בקשת סרט", + "components.RequestModal.requestmovies": "בקשת {count} {count, plural, one {סרט} other {סרטים}}", + "components.RequestModal.requestmovies4k": "בקשת {count} {count, plural, one {סרט} other {סרטים}} 4K ב", + "components.RequestModal.requestmovietitle": "בקשת סרט", + "components.RequestModal.requestseasons": "בקשה של {seasonCount} {seasonCount, plural, one {עונה} other {עונות}}", + "components.RequestModal.requestseasons4k": "בקשה של {seasonCount} {seasonCount, plural, one {עונה} other {עונות}} in 4K", + "components.RequestModal.requestseries4ktitle": "4K בקשת סדרה", + "components.RequestModal.requestseriestitle": "בקשת סדרה", + "components.RequestModal.season": "עונה", + "components.RequestModal.seasonnumber": "עונה {number}", + "components.RequestModal.selectmovies": "(ים)בחירת סרט", + "components.RequestModal.selectseason": "(ות)בחירת עונה", + "components.ResetPassword.confirmpassword": "אימות סיסמה", + "components.ResetPassword.email": "כתובת מייל", + "components.ResetPassword.emailresetlink": "שליחת מייל עם קישור לשיחזור", + "components.ResetPassword.gobacklogin": "חזרה לעמוד לוגאין", + "components.ResetPassword.password": "סיסמה", + "components.ResetPassword.passwordreset": "איפוס סיסמה", + "components.ResetPassword.requestresetlinksuccessmessage": ".קישור לאיפוס הסיסמה יישלח לכתובת המייל המסופקת אם היא משוייכת למשתמש", + "components.ResetPassword.resetpassword": "איפוס סיסמה", + "components.ResetPassword.resetpasswordsuccessmessage": "!הסיסמה אופסה בהצלחה", + "components.ResetPassword.validationemailrequired": "יש לרשום כתובת מייל חוקית", + "components.ResetPassword.validationpasswordmatch": "הסיסמאות צריכות להיות זהות", + "components.ResetPassword.validationpasswordminchars": "הסיסמה צריכה להיות באורך 8 תווים", + "components.ResetPassword.validationpasswordrequired": "יש לרשום סיסמה", + "components.Search.search": "חיפוש", + "components.Search.searchresults": "תוצאות חיפוש", + "components.Selector.nooptions": "אין תוצאות", + "components.Selector.searchGenres": "…בחירת ג׳אנרים", + "components.Selector.searchKeywords": "…מילות מפתח לחיפוש", + "components.Selector.searchStudios": "…חיפוש אולפנים", + "components.Selector.showless": "הצג פחות", + "components.Selector.showmore": "עוד", + "components.Selector.starttyping": ".יש להקליד כדי להתחיל חיפוש", + "components.Settings.Notifications.NotificationsGotify.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": ".Gotify אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "!נשמרו בהצלחה Gotify הגדרות התראות", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": ".נכשלה Gotify שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "…בתהליך שליחה Gotify הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "!נשלחה בהצלחה Gotify הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsGotify.token": "יישום Token", + "components.Settings.Notifications.NotificationsGotify.url": "כתובת השרת", + "components.Settings.Notifications.NotificationsGotify.validationTokenRequired": "עבור היישום Token יש לרשום", + "components.Settings.Notifications.NotificationsGotify.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "יש לרשום כתובת תקינה", + "components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", + "components.Settings.Notifications.NotificationsLunaSea.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsLunaSea.profileName": "שם הפרופיל", + "components.Settings.Notifications.NotificationsLunaSea.profileNameTip": "default נדרש רק אם משתמשים בפרופיל", + "components.Settings.Notifications.NotificationsLunaSea.settingsFailed": ".LunaSea אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsLunaSea.settingsSaved": "!נשמרו בהצלחה LunaSea ההגדרות עבור", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestFailed": ".נכשלה LunaSea שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSending": "…בתהליך שליחה LunaSea הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSuccess": "!נשלחה בהצלחה LunaSea הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsLunaSea.validationTypes": "יש לבחור לפחות סוג התראה אחת", + "components.Settings.Notifications.NotificationsLunaSea.validationWebhookUrl": "יש לרשום כתובת תקינה", + "components.Settings.Notifications.NotificationsLunaSea.webhookUrl": "Webhook כתובת", + "components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Webhook כתובת התראות", + "components.Settings.Notifications.NotificationsPushbullet.accessToken": "Access Token", + "components.Settings.Notifications.NotificationsPushbullet.accessTokenTip": "בהגדרות החשבון Token ניתן ליצור", + "components.Settings.Notifications.NotificationsPushbullet.agentEnabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsPushbullet.channelTag": "תגית ערוץ", + "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsFailed": ".Pushbullet אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsSaved": "!נשמרו בהצלחה Pushbullet הגדרות התראות", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": ".נכשלה Pushbullet שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSending": "…בתהליך שליחה Pushbullet הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSuccess": "!נשלחה בהצלחה Pushbullet הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsPushbullet.validationAccessTokenRequired": "access token יש לרשום", + "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.NotificationsPushover.accessToken": "של יישום API Token", + "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Jellyseerr רישום יישום לשימוש עם", + "components.Settings.Notifications.NotificationsPushover.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "ברירת המחדל של המכשיר", + "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": ".Pushover אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "!נשמרו בהצלחה Pushover הגדרות התראות", + "components.Settings.Notifications.NotificationsPushover.sound": "צליל התראה", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": ".נכשלה Pushover שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "…בתהליך שליחה Pushover הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSuccess": "!נשלחה בהצלחה Pushover הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsPushover.userToken": "מפתח משתמש או קבוצה", + "components.Settings.Notifications.NotificationsPushover.userTokenTip": "מזהה מפתח או קבוצה באורך 30 תווים", + "components.Settings.Notifications.NotificationsPushover.validationAccessTokenRequired": "access token יש לרשום", + "components.Settings.Notifications.NotificationsPushover.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "יש לרשום מפתח משתמש או קבוצה תקין", + "components.Settings.Notifications.NotificationsSlack.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": ".Slack אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "!נשמרו בהצלחה Slack הגדרות התראות", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": ".נכשלה Slack שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestSending": "…בתהליך שליחה Slack הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestSuccess": "!נשלחה בהצלחה Slack הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsSlack.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.NotificationsSlack.validationWebhookUrl": "יש לרשום כתובת תקינה", + "components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook כתובת", + "components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Webhook יצירת אינטגרציה של", + "components.Settings.Notifications.NotificationsWebPush.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": ".HTTPS צריך להיות מוגדר ב Jellyseerr בכדי לקבל התראות פוש", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": ".נכשלה Web שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "…בתהליך שליחה Web הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "!נשלחה בהצלחה Web הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": ".Web אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "!נשמרו בהצלחה Web הגדרות התראות", + "components.Settings.Notifications.NotificationsWebhook.agentenabled": "איפשור יישום", + "components.Settings.Notifications.NotificationsWebhook.authheader": "Authorization Header", + "components.Settings.Notifications.NotificationsWebhook.customJson": "JSON Payload", + "components.Settings.Notifications.NotificationsWebhook.resetPayload": "איפוס לברירת מחדל", + "components.Settings.Notifications.NotificationsWebhook.resetPayloadSuccess": "!בוצע בהצלחה JSON איפוס", + "components.Settings.Notifications.NotificationsWebhook.templatevariablehelp": "עזרה עם משתני התבנית", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": ".נכשלה Webhook שליחת הודעת בדיקה", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSending": "…בתהליך שליחה Webhook הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSuccess": "!נשלחה בהצלחה Webhook הודעת בדיקה אל", + "components.Settings.Notifications.NotificationsWebhook.validationJsonPayloadRequired": "תקין JSON יש לרשום", + "components.Settings.Notifications.NotificationsWebhook.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.NotificationsWebhook.validationWebhookUrl": "יש לרשום כתובת תקינה", + "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook כתובת", + "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": ".Webhook אירעה שגיאה בשמירת הגדרות עבור", + "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "!נשמרו בהצלחה Webhook הגדרות התראות", + "components.Settings.Notifications.agentenabled": "איפשור יישום", + "components.Settings.Notifications.allowselfsigned": "איפשור תעודות חתומות עצמית", + "components.Settings.Notifications.authPass": "SMTP סיסמה", + "components.Settings.Notifications.authUser": "SMTP משתמש", + "components.Settings.Notifications.botAPI": "אימות של בוט Token", + "components.Settings.Notifications.botApiTip": "Jellyseerr לשימוש עם יצירת בוט", + "components.Settings.Notifications.botAvatarUrl": "כתובת של תמונת בוט", + "components.Settings.Notifications.botUsername": "משתמש בוט", + "components.Settings.Notifications.botUsernameTip": "מאפשר למשתמשים להתחיל צ׳אט עם הבוט ולהגדיר את ההתראות לעצמם", + "components.Settings.Notifications.chatId": "Chat ID", + "components.Settings.Notifications.chatIdTip": "/my_id ובדיקה של @get_id_bot התחלת צ׳אט עם הבוט הוספת,", + "components.Settings.Notifications.discordsettingsfailed": ".Discord אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.Settings.Notifications.discordsettingssaved": "!נשמרו בהצלחה Discord הגדרות התראות", + "components.Settings.Notifications.emailsender": "כתובת השולח", + "components.Settings.Notifications.emailsettingsfailed": ".אירעה שגיאה בעת שמירת הגדרות התראות למייל", + "components.Settings.Notifications.emailsettingssaved": "!הגדרות התראות למייל נשמרו בהצלחה", + "components.Settings.Notifications.enableMentions": "אפישור איזכורים", + "components.Settings.Notifications.encryption": "סוג הצפנה", + "components.Settings.Notifications.encryptionDefault": "אם זמין STARTTLS השתמש ב", + "components.Settings.Notifications.encryptionImplicitTls": "TLS שימוש ב", + "components.Settings.Notifications.encryptionNone": "ללא", + "components.Settings.Notifications.encryptionOpportunisticTls": "STARTTLS תמיד יש להשתמש ב", + "components.Settings.Notifications.encryptionTip": "ברוב המקרים TLS משתמש בפורט 465 ו STARTLS בפורט 587", + "components.Settings.Notifications.pgpPassword": "PGP סיסמת", + "components.Settings.Notifications.pgpPasswordTip": "OpenPGP הצפן הודעת מייל באמצעות", + "components.Settings.Notifications.pgpPrivateKey": "PGP Private Key", + "components.Settings.Notifications.pgpPrivateKeyTip": "OpenPGP הצפן הודעת מייל באמצעות", + "components.Settings.Notifications.sendSilently": "שליחה שקטה", + "components.Settings.Notifications.sendSilentlyTip": "שליחת התראות ללא צליל", + "components.Settings.Notifications.senderName": "שם השולח", + "components.Settings.Notifications.smtpHost": "SMTP מארח", + "components.Settings.Notifications.smtpPort": "SMTP Port", + "components.Settings.Notifications.telegramsettingsfailed": ".Telegram אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.Settings.Notifications.telegramsettingssaved": "!נשמרו בהצלחה Telegram הגדרות התראות", + "components.Settings.Notifications.toastDiscordTestFailed": ".נכשלה Discord שליחת הודעת בדיקה", + "components.Settings.Notifications.toastDiscordTestSending": "…בתהליך שליחה Discord הודעת בדיקה אל", + "components.Settings.Notifications.toastDiscordTestSuccess": "!נשלחה בהצלחה Discord הודעת בדיקה אל", + "components.Settings.Notifications.toastEmailTestFailed": ".שליחת הודעת מייל לבדיקה נכשלה", + "components.Settings.Notifications.toastEmailTestSending": "…שליחת הודעת מייל לבדיקה", + "components.Settings.Notifications.toastEmailTestSuccess": "!הודעת בדיקת מייל נשלחה בהצלחה", + "components.Settings.Notifications.toastTelegramTestFailed": ".נכשלה Telegram שליחת הודעת בדיקה", + "components.Settings.Notifications.toastTelegramTestSending": "…בתהליך שליחה Telegram הודעת בדיקה אל", + "components.Settings.Notifications.toastTelegramTestSuccess": "!נשלחה בהצלחה Telegram הודעת בדיקה אל", + "components.Settings.Notifications.userEmailRequired": "דרישת מייל משתמש", + "components.Settings.Notifications.validationBotAPIRequired": "authorization token יש לרשום", + "components.Settings.Notifications.validationChatIdRequired": "chat ID יש לרשום", + "components.Settings.Notifications.validationEmail": "יש לרשום כתובת מייל תקינה", + "components.Settings.Notifications.validationPgpPassword": "PGP יש לרשום סיסמת", + "components.Settings.Notifications.validationPgpPrivateKey": "תקין PGP private key יש לרשום", + "components.Settings.Notifications.validationSmtpHostRequired": "או מארח תקינים IP Address יש לרשום", + "components.Settings.Notifications.validationSmtpPortRequired": "יש לרשום מספר פורט תקין", + "components.Settings.Notifications.validationTypes": "יש לבחור לפחות סוג התראה אחד", + "components.Settings.Notifications.validationUrl": "יש לרשום כתובת", + "components.Settings.Notifications.webhookUrl": "Webhook כתובת", + "components.Settings.Notifications.webhookUrlTip": "בשרת webhook אינטגרציית יצירת", + "components.Settings.RadarrModal.add": "הוספת השרת", + "components.Settings.RadarrModal.announced": "הוכרז", + "components.Settings.RadarrModal.apiKey": "API Key", + "components.Settings.RadarrModal.baseUrl": "תחילית לכתובת השרת", + "components.Settings.RadarrModal.create4kradarr": "חדש Radarr 4K הוספת שרת", + "components.Settings.RadarrModal.createradarr": "Radarr הוספת שרת", + "components.Settings.RadarrModal.default4kserver": "4K קבע כשרת ברירת מחדל", + "components.Settings.RadarrModal.defaultserver": "שרת ברירת מחדל", + "components.Settings.RadarrModal.edit4kradarr": "4K Radarr ערוך שרת", + "components.Settings.RadarrModal.editradarr": "Radarr ערוך שרת", + "components.Settings.RadarrModal.enableSearch": "אפשר חיפוש אוטומטי", + "components.Settings.RadarrModal.externalUrl": "כתובת פומבית", + "components.Settings.RadarrModal.hostname": "IP שם מארח או כתובת", + "components.Settings.RadarrModal.inCinemas": "בקולנוע", + "components.Settings.RadarrModal.loadingTags": "טוען תגיות…", + "components.Settings.RadarrModal.loadingprofiles": "…טוען פרופילי איכות", + "components.Settings.RadarrModal.loadingrootfolders": "…טוען תיקיות מדיה", + "components.Settings.RadarrModal.minimumAvailability": "זמינות מינימאלית", + "components.Settings.RadarrModal.notagoptions": ".אין תגיות", + "components.Settings.RadarrModal.port": "פורט", + "components.Settings.RadarrModal.qualityprofile": "פרופיל איכות", + "components.Settings.RadarrModal.released": "שוחרר", + "components.Settings.RadarrModal.rootfolder": "תיקיית מדיה", + "components.Settings.RadarrModal.selectMinimumAvailability": "בחירת זמינות מינימלית", + "components.Settings.RadarrModal.selectQualityProfile": "בחירת פרופיל איכות", + "components.Settings.RadarrModal.selectRootFolder": "בחירת תקיית מדיה", + "components.Settings.RadarrModal.selecttags": "בחירת תגיות", + "components.Settings.RadarrModal.server4k": "4K שרת", + "components.Settings.RadarrModal.servername": "שם השרת", + "components.Settings.RadarrModal.ssl": "SSL שימוש ב", + "components.Settings.RadarrModal.syncEnabled": "אפשר סריקה", + "components.Settings.RadarrModal.tagRequests": "תיוג בקשות", + "components.Settings.RadarrModal.tagRequestsInfo": "הוספת תגית אוטומטית עם מזהה המשתמש ושם התצוגה", + "components.Settings.RadarrModal.tags": "תגיות", + "components.Settings.RadarrModal.testFirstQualityProfiles": "לטעינת פרופיל איכות, יש ללחוץ על בדיקה", + "components.Settings.RadarrModal.testFirstRootFolders": "לטעינת ספריות מדיה, יש ללחוץ על בדיקה", + "components.Settings.RadarrModal.testFirstTags": "לטעינת תגיות, יש ללחוץ על בדיקה", + "components.Settings.RadarrModal.toastRadarrTestFailure": ".Radarr התחברות נכשלה אל", + "components.Settings.RadarrModal.toastRadarrTestSuccess": "!נוצר בהצלחה Radarr החיבור אל", + "components.Settings.RadarrModal.validationApiKeyRequired": "תקין API Key יש לרשום", + "components.Settings.RadarrModal.validationApplicationUrl": "יש לרשום כתובת שרת תקינה", + "components.Settings.RadarrModal.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", + "components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "יש להתחיל עם סלאש", + "components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", + "components.Settings.RadarrModal.validationHostnameRequired": "תקינה IP יש לרשום שם מאחר או כתובת", + "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "יש לבחור זמינות מינימאלית", + "components.Settings.RadarrModal.validationNameRequired": "יש לרשום שם שרת", + "components.Settings.RadarrModal.validationPortRequired": "יש לרשום פורט תקין", + "components.Settings.RadarrModal.validationProfileRequired": "יש לבחור פרופיל איכות", + "components.Settings.RadarrModal.validationRootFolderRequired": "יש לבחור תיקיית מדיה", + "components.Settings.SettingsAbout.Releases.currentversion": "נוכחית", + "components.Settings.SettingsAbout.Releases.latestversion": "עדכנית", + "components.Settings.SettingsAbout.Releases.releasedataMissing": ".מידע על גרסה אינו זמין", + "components.Settings.SettingsAbout.Releases.releases": "גרסאות", + "components.Settings.SettingsAbout.Releases.versionChangelog": "{version} שינויים לגרסה", + "components.Settings.SettingsAbout.Releases.viewchangelog": "צפייה בשינויים", + "components.Settings.SettingsAbout.Releases.viewongithub": "GitHub צפייה ב", + "components.Settings.SettingsAbout.about": "אודות", + "components.Settings.SettingsAbout.appDataPath": "תיקיית מדיה", + "components.Settings.SettingsAbout.betawarning": "!GitHub זוהי גרסת בטה. יכולות עלולות להיות שבורות או להתנהג באופן בלתי צפוי. אנא דווחו על כל תקלה", + "components.Settings.SettingsAbout.documentation": "דוקומנטציה", + "components.Settings.SettingsAbout.gettingsupport": "קבלת תמיכה", + "components.Settings.SettingsAbout.githubdiscussions": "GitHub דיונים ב", + "components.Settings.SettingsAbout.helppaycoffee": "הזמינו אותי לקפה", + "components.Settings.SettingsAbout.outofdate": "ישן", + "components.Settings.SettingsAbout.overseerrinformation": "Jellyseerr אודות", + "components.Settings.SettingsAbout.preferredmethod": "מועדף", + "components.Settings.SettingsAbout.runningDevelop": ".מומלץ להתקין ממנו רק עבור מפתחים או משתמשים שתורמים בפיתוח ו/או מוכנים לנסות את הפיתוחים האחרונים develop הבראנץ הנוכחי הוא", + "components.Settings.SettingsAbout.supportjellyseerr": "Jellyseerr תמיכה ב", + "components.Settings.SettingsAbout.supportoverseerr": "Overseerr תמיכה ב", + "components.Settings.SettingsAbout.timezone": "איזור זמן", + "components.Settings.SettingsAbout.totalmedia": "סה״כ מדיה", + "components.Settings.SettingsAbout.totalrequests": "סה״כ בקשות", + "components.Settings.SettingsAbout.uptodate": "מעודכן", + "components.Settings.SettingsAbout.version": "גרסה", + "components.Settings.SettingsJobsCache.availability-sync": "סינכרון זמינות מדיה", + "components.Settings.SettingsJobsCache.cache": "מטמון", + "components.Settings.SettingsJobsCache.cacheDescription": ".מיותרות API חיצוניות בכדי לייעל ולהימנע מקריאות API ממחזר קריאות Jellyseerr", + "components.Settings.SettingsJobsCache.cacheflushed": ".נוקה {cachename} המטמון", + "components.Settings.SettingsJobsCache.cachehits": "הפעלות", + "components.Settings.SettingsJobsCache.cachekeys": "סה״כ מפתחות", + "components.Settings.SettingsJobsCache.cacheksize": "גודל מפתח", + "components.Settings.SettingsJobsCache.cachemisses": "החמצות", + "components.Settings.SettingsJobsCache.cachename": "שם מטמון", + "components.Settings.SettingsJobsCache.cachevsize": "גודל ערך", + "components.Settings.SettingsJobsCache.canceljob": "ביטול משימה", + "components.Settings.SettingsJobsCache.command": "פעולה", + "components.Settings.SettingsJobsCache.download-sync": "סינכרון הורדות", + "components.Settings.SettingsJobsCache.download-sync-reset": "איפוס סינכון הורדות", + "components.Settings.SettingsJobsCache.editJobSchedule": "עריכת משימה", + "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "תדירות נוכחית", + "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "תדירות חדשה", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "כל {jobScheduleHours, plural, one {שעה} other {{jobScheduleHours} שעות}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "כל {jobScheduleMinutes, plural, one {דקה} other {{jobScheduleMinutes} דקות}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "כל {jobScheduleSeconds, plural, one {שניה} other {{jobScheduleSeconds} שניות}}", + "components.Settings.SettingsJobsCache.flushcache": "ניקוי מטמון", + "components.Settings.SettingsJobsCache.image-cache-cleanup": "ניקוי מטמון תמונות", + "components.Settings.SettingsJobsCache.imagecache": "מטמון תמונות", + "components.Settings.SettingsJobsCache.imagecacheDescription": "מחדש יתחבר וישמור מטמון של תמונות ממקורות חיצוניים. התמונות נשמרות בתיקיית הקונפיגורציה. ניתן למצוא את הקבצים בנתיב הבא Jellyseerr ,כאשר מופעל בהגדרות {appDataPath}/cache/images", + "components.Settings.SettingsJobsCache.imagecachecount": "תמונות נשמרו במטמון", + "components.Settings.SettingsJobsCache.imagecachesize": "גודל מטמון", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Jellyfin סריקה מלאה לספריות", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "אחר התווסף לאחרונה Jellyfin סריקת", + "components.Settings.SettingsJobsCache.jobScheduleEditFailed": ".משהו השתבש בשמירת המשימה", + "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "!המשימה עודכנה בהצלחה", + "components.Settings.SettingsJobsCache.jobcancelled": ".בוטלה {jobname}", + "components.Settings.SettingsJobsCache.jobname": "שם משימה", + "components.Settings.SettingsJobsCache.jobs": "משימות", + "components.Settings.SettingsJobsCache.jobsDescription": ".מבצע משימות תחזוקה כמשימות מתוזמנות מראש. משימות אלו ניתנות להפעלה באופן יזום ללא השפעה על לו״ז המשימות Jellyseerr", + "components.Settings.SettingsJobsCache.jobsandcache": "משימות & מטמון", + "components.Settings.SettingsJobsCache.jobstarted": ".התחילה {jobname}", + "components.Settings.SettingsJobsCache.jobtype": "סוג", + "components.Settings.SettingsJobsCache.nextexecution": "ההרצה הבאה", + "components.Settings.SettingsJobsCache.plex-full-scan": "מלאה Plex סריקת ספריית", + "components.Settings.SettingsJobsCache.plex-recently-added-scan": "אחר התווסף לאחרונה Plex סריקת", + "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex סיכנרון רשימת צפייה של", + "components.Settings.SettingsJobsCache.process": "תהליך", + "components.Settings.SettingsJobsCache.radarr-scan": "Radarr סריקת", + "components.Settings.SettingsJobsCache.runnow": "הפעלה", + "components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr סריקת", + "components.Settings.SettingsJobsCache.unknownJob": "משימה לא ידועה", + "components.Settings.SettingsLogs.copiedLogMessage": ".לוע הועתק ללוח", + "components.Settings.SettingsLogs.copyToClipboard": "העתקה ללוח", + "components.Settings.SettingsLogs.extraData": "מידע נוסף", + "components.Settings.SettingsLogs.filterDebug": "Debug", + "components.Settings.SettingsLogs.filterError": "Error", + "components.Settings.SettingsLogs.filterInfo": "Info", + "components.Settings.SettingsLogs.filterWarn": "Warning", + "components.Settings.SettingsLogs.label": "תווית", + "components.Settings.SettingsLogs.level": "דחיפות", + "components.Settings.SettingsLogs.logDetails": "מידע לוג", + "components.Settings.SettingsLogs.logs": "לוגים", + "components.Settings.SettingsLogs.logsDescription": "או בנתיב stdout ניתן לצפות בלוגים הללו באמצעות {appDataPath}/logs/overseerr.log", + "components.Settings.SettingsLogs.message": "הודעה", + "components.Settings.SettingsLogs.pauseLogs": "השהיית הלו״ז", + "components.Settings.SettingsLogs.resumeLogs": "המשך", + "components.Settings.SettingsLogs.showall": "הצגת כל הלוגים", + "components.Settings.SettingsLogs.time": "זמן", + "components.Settings.SettingsLogs.viewdetails": "פרטים", + "components.Settings.SettingsMain.apikey": "API Key", + "components.Settings.SettingsMain.applicationTitle": "כותרת היישום", + "components.Settings.SettingsMain.applicationurl": "תחילית עבור הכתובת", + "components.Settings.SettingsMain.cacheImages": "הפעלת מטמון לתמונות", + "components.Settings.SettingsMain.cacheImagesTip": "שמירה תמונות חיצוניות במטמון (דורש שטח דיסק גדול בהרבה)", + "components.Settings.SettingsMain.csrfProtection": "CSRF הפעלת הגנת", + "components.Settings.SettingsMain.csrfProtectionHoverTip": "!יש לאפשר הגדרה זו רק אם מבינים בנושא", + "components.Settings.SettingsMain.csrfProtectionTip": "(HTTPS דורש) לקריאה בלבד Api קבע את ה", + "components.Settings.SettingsMain.general": "כללי", + "components.Settings.SettingsMain.generalsettings": "הגדרות כלליות", + "components.Settings.SettingsMain.generalsettingsDescription": ".Jellyseerr הגדרות גלובליות וברירות מחדל עבור", + "components.Settings.SettingsMain.hideAvailable": "הסתרת מדיה זמינה", + "components.Settings.SettingsMain.locale": "שפת התצוגה", + "components.Settings.SettingsMain.originallanguage": "שפת מה חדש", + "components.Settings.SettingsMain.originallanguageTip": "סינון תכנים בשפה המועדפת", + "components.Settings.SettingsMain.partialRequestsEnabled": "בקשה חלקית של סדרות", + "components.Settings.SettingsMain.region": "איזור עבור מה חדש", + "components.Settings.SettingsMain.regionTip": "סינון תכנים לפי זמינות איזורית", + "components.Settings.SettingsMain.toastApiKeyFailure": ".חדש API key משהו השתבש ביצירת", + "components.Settings.SettingsMain.toastApiKeySuccess": "!חדש נוצר בהצלחה Api Key", + "components.Settings.SettingsMain.toastSettingsFailure": ".משהו השתבש בעת שמירת ההגדרות", + "components.Settings.SettingsMain.toastSettingsSuccess": "!ההגדרות נשמרו בהצלחה", + "components.Settings.SettingsMain.trustProxy": "הפעלת תמיכב בפרוקסי", + "components.Settings.SettingsMain.trustProxyTip": "מאחורי פרוקסי IP לרשום כתובות של Jellyseerr איפשור ל", + "components.Settings.SettingsMain.validationApplicationTitle": "יש לרשום כותרת יישום", + "components.Settings.SettingsMain.validationApplicationUrl": "יש לרשום כתובת תקינה", + "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", + "components.Settings.SettingsUsers.defaultPermissions": "הרשאות ברירת מחדל", + "components.Settings.SettingsUsers.defaultPermissionsTip": "הרשאות התחלתיות למשתמשים חדשים", + "components.Settings.SettingsUsers.localLogin": "איפשור התחברות מקומית", + "components.Settings.SettingsUsers.localLoginTip": "{mediaServerName} OAuth אפשר למשתמשים לבצע לוגאין באמצעות מייל וסיסמה במקום", + "components.Settings.SettingsUsers.movieRequestLimitLabel": "מגבלת גלוגלית לבקשת סרטים", + "components.Settings.SettingsUsers.newPlexLogin": "חדשים לבצע לוגאין {mediaServerName} לאפשר למשתמשי", + "components.Settings.SettingsUsers.newPlexLoginTip": "לבצע לוגאין ללא ייבוא מראש {mediaServerName} אפשר למשתמשי", + "components.Settings.SettingsUsers.toastSettingsFailure": ".אירעה שגיאה בעת שמירת ההגדרות", + "components.Settings.SettingsUsers.toastSettingsSuccess": "!הגדרות המשתמש נשמרו בהצלחה", + "components.Settings.SettingsUsers.tvRequestLimitLabel": "מגבלת גלוגלית לבקשת סדרות", + "components.Settings.SettingsUsers.userSettings": "הגדרות משתמש", + "components.Settings.SettingsUsers.userSettingsDescription": ".קביעת הגדרות משתמש וערכי ברירת מחדל", + "components.Settings.SettingsUsers.users": "משתמשים", + "components.Settings.SonarrModal.add": "הוספת השרת", + "components.Settings.SonarrModal.animeSeriesType": "סוג סדרת אנימה", + "components.Settings.SonarrModal.animeTags": "תגיות אנימה", + "components.Settings.SonarrModal.animelanguageprofile": "פרופיל שפת אנימה", + "components.Settings.SonarrModal.animequalityprofile": "פרופיל איכות אנימה", + "components.Settings.SonarrModal.animerootfolder": "ספריות מדיה של אנימה", + "components.Settings.SonarrModal.apiKey": "API Key", + "components.Settings.SonarrModal.baseUrl": "תחילית לכתובת השרת", + "components.Settings.SonarrModal.create4ksonarr": "חדש Sonarr 4K הוספת שרת", + "components.Settings.SonarrModal.createsonarr": "Sonarr הוספת שרת", + "components.Settings.SonarrModal.default4kserver": "4K קבע כשרת ברירת מחדל", + "components.Settings.SonarrModal.defaultserver": "שרת ברירת מחדל", + "components.Settings.SonarrModal.edit4ksonarr": "4K Sonarr ערוך שרת", + "components.Settings.SonarrModal.editsonarr": "Sonarr ערוך שרת", + "components.Settings.SonarrModal.enableSearch": "אפשר חיפוש אוטומטי", + "components.Settings.SonarrModal.externalUrl": "כתובת פומבית", + "components.Settings.SonarrModal.hostname": "IP שם מארח או כתובת", + "components.Settings.SonarrModal.languageprofile": "פרופיל שפה", + "components.Settings.SonarrModal.loadingTags": "טוען תגיות…", + "components.Settings.SonarrModal.loadinglanguageprofiles": "…טוען פרופילי שפה", + "components.Settings.SonarrModal.loadingprofiles": "…טוען פרופילי איכות", + "components.Settings.SonarrModal.loadingrootfolders": "…טוען תיקיות מדיה", + "components.Settings.SonarrModal.notagoptions": ".אין תגיות", + "components.Settings.SonarrModal.port": "פורט", + "components.Settings.SonarrModal.qualityprofile": "פרופיל איכות", + "components.Settings.SonarrModal.rootfolder": "תיקיית מדיה", + "components.Settings.SonarrModal.seasonfolders": "תיקייה לכל עונה", + "components.Settings.SonarrModal.selectLanguageProfile": "בחירת פרופיל שפה", + "components.Settings.SonarrModal.selectQualityProfile": "בחירת פרופיל איכות", + "components.Settings.SonarrModal.selectRootFolder": "בחירת תקיית מדיה", + "components.Settings.SonarrModal.selecttags": "בחירת תגיות", + "components.Settings.SonarrModal.seriesType": "סוג סדרה", + "components.Settings.SonarrModal.server4k": "4K שרת", + "components.Settings.SonarrModal.servername": "שם השרת", + "components.Settings.SonarrModal.ssl": "SSL שימוש ב", + "components.Settings.SonarrModal.syncEnabled": "אפשר סריקה", + "components.Settings.SonarrModal.tagRequests": "תיוג בקשות", + "components.Settings.SonarrModal.tagRequestsInfo": "הוספת תגית אוטומטית עם מזהה המשתמש ושם התצוגה", + "components.Settings.SonarrModal.tags": "תגיות", + "components.Settings.SonarrModal.testFirstLanguageProfiles": "לטעינת פרופילי שפה, יש ללחוץ על בדיקה", + "components.Settings.SonarrModal.testFirstQualityProfiles": "לטעינת פרופיל איכות, יש ללחוץ על בדיקה", + "components.Settings.SonarrModal.testFirstRootFolders": "לטעינת ספריות מדיה, יש ללחוץ על בדיקה", + "components.Settings.SonarrModal.testFirstTags": "לטעינת תגיות, יש ללחוץ על בדיקה", + "components.Settings.SonarrModal.toastSonarrTestFailure": ".Sonarr התחברות נכשלה אל", + "components.Settings.SonarrModal.toastSonarrTestSuccess": "!נוצר בהצלחה Sonarr החיבור אל", + "components.Settings.SonarrModal.validationApiKeyRequired": "תקין API Key יש לרשום", + "components.Settings.SonarrModal.validationApplicationUrl": "יש לרשום כתובת שרת תקינה", + "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", + "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "יש להתחיל עם סלאש", + "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", + "components.Settings.SonarrModal.validationHostnameRequired": "תקינה IP יש לרשום שם מאחר או כתובת", + "components.Settings.SonarrModal.validationLanguageProfileRequired": "יש לבחור פרופיל שפה", + "components.Settings.SonarrModal.validationNameRequired": "יש לרשום שם שרת", + "components.Settings.SonarrModal.validationPortRequired": "יש לרשום פורט תקין", + "components.Settings.SonarrModal.validationProfileRequired": "יש לבחור פרופיל איכות", + "components.Settings.SonarrModal.validationRootFolderRequired": "יש לבחור תיקיית מדיה", + "components.Settings.activeProfile": "פרופיל נבחר", + "components.Settings.addradarr": "Radarr הוספת שרת", + "components.Settings.address": "כתובת", + "components.Settings.addsonarr": "Sonarr הוספת שרת", + "components.Settings.advancedTooltip": "הגדרה שגויה של אפשרות זו עלולה לשבש את הפונקציונאליות", + "components.Settings.cancelscan": "ביטול סריקה", + "components.Settings.copied": ".ללוח API key העתקת", + "components.Settings.currentlibrary": "{name} :ספריה נוכחית", + "components.Settings.default": "ברירת מחדל", + "components.Settings.default4k": "4K ברירת מחדל", + "components.Settings.deleteServer": "{serverType} מחיקת שרת", + "components.Settings.deleteserverconfirm": "?האם למחוק את השרת", + "components.Settings.email": "מייל", + "components.Settings.enablessl": "SSL שימוש ב", + "components.Settings.experimentalTooltip": "איפשור ההגדרה הזו עלול לגרום להתנהגות בלתי צפוייה של האפליקציה", + "components.Settings.externalUrl": "כתובת", + "components.Settings.hostname": "IP שם מארח או כתובת", + "components.Settings.invalidurlerror": ".{mediaServerName} אין אפשרות להתחבר לשרת", + "components.Settings.is4k": "4K", + "components.Settings.jellyfinForgotPasswordUrl": "כתובת עבור שכחתי סיסמה", + "components.Settings.jellyfinSettings": "{mediaServerName} הגדרות", + "components.Settings.jellyfinSettingsDescription": ".ניתן גם להגדיר כתובת עבור שכחתי סיסמה בכדי לכוון את המשתמשים לכתובת מותאמת אישית .{mediaServerName} ניתן להגדיר את הכתובות הפומביות של והפנימיות של השרת ברוב המקרים, הכתובת הפומבית שונה מהכתובת המקומית.", + "components.Settings.jellyfinSettingsFailure": ".{mediaServerName} אירעה שגיאה בשמירת הגדרות", + "components.Settings.jellyfinSettingsSuccess": "!נשמרו בהצלחה {mediaServerName} ההגדרות", + "components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "אימות מותאם אישית ביחד עם קיבוץ סיפריות אוטומטי אינו נתמך", + "components.Settings.jellyfinSyncFailedGenericError": "משהו השתבש בסינכרון ספריות", + "components.Settings.jellyfinSyncFailedNoLibrariesFound": "אין ספריות זמינות", + "components.Settings.jellyfinlibraries": "{mediaServerName} ספריות", + "components.Settings.jellyfinlibrariesDescription": "ספריות {mediaServerName} סורקות כותרים. אם אין ספריות שנמצאו, יש ללחוץ על הכפתור מטה.", + "components.Settings.jellyfinsettings": "{mediaServerName} הגדרות", + "components.Settings.jellyfinsettingsDescription": ".בכדי לראות איזה תוכן זמין {mediaServerName} סורק את ספריות {mediaServerName}. {mediaServerName} הגדרת ההגדרות של שרת", + "components.Settings.librariesRemaining": "ספריות שנותרו: {count}", + "components.Settings.manualscan": "סריקת ספריה ידנית", + "components.Settings.manualscanDescription": "בד״כ, הסריקה מבוצעת כל 24 שעות. Jellyseerr יבדוק את התסווספו לאחרונה של שרת Plex. אם זוהי הפעם הראשונה בהגדרת שרת Plex, מומלץ לבצע סריקה ידנית מלאה של כלל הספריות לפחות פעם אחת", + "components.Settings.manualscanDescriptionJellyfin": "{mediaServerName} יבדוק את התווספו לאחרונה של שרת בד״כ, הסריקה תבוצע פעם אחת כל 24 שעות. אם זוהי הפעם הראשונה בהגדרת השרת, מומלץ לבצע סריקה ידנית מלאה לפחות פעם אחת Jellyseerr.", + "components.Settings.manualscanJellyfin": "סריקת ספריה ידנית", + "components.Settings.mediaTypeMovie": "סרט", + "components.Settings.mediaTypeSeries": "סדרה", + "components.Settings.menuAbout": "אודות", + "components.Settings.menuGeneralSettings": "כללי", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.menuJobs": "משימות & מטמון", + "components.Settings.menuLogs": "לוגים", + "components.Settings.menuNotifications": "התראות", + "components.Settings.menuPlexSettings": "Plex", + "components.Settings.menuServices": "שרתים", + "components.Settings.menuUsers": "משתמשים", + "components.Settings.noDefault4kServer": "כברירת מחדל 4K {serverType} יש להגדיר 4K {serverType} בכדי לאפשר למשתמשים ליצור בקשות חדשות אל", + "components.Settings.noDefaultNon4kServer": "לייעד אותו עבור איכות גבוהה מאוד אין {serverType}, אחד עבור כל האיכויות או שאם הוא משתמש רק להורדת תוכן באיכות גבוהה מאוד {serverType} אם יש ברשותך רק שרת", + "components.Settings.noDefaultServer": ". אחד בכדי שבקשות {mediaType} יטופלו {serverType} יש לסמן לפחות שרת", + "components.Settings.notificationAgentSettingsDescription": ".איפשור והגדרת של יישומי התראות", + "components.Settings.notifications": "התראות", + "components.Settings.notificationsettings": "הגדרות התראות", + "components.Settings.notrunning": "בהשהייה", + "components.Settings.plex": "Plex", + "components.Settings.plexlibraries": "Plex ספריות", + "components.Settings.plexlibrariesDescription": "ספריות Jellyseerr סורקות כותרים. אם אין ספריות שנמצאו, יש ללחוץ על הכפתור מטה.", + "components.Settings.plexsettings": "Plex הגדרות", + "components.Settings.plexsettingsDescription": ".בכדי לראות איזה תוכן זמין Jellyseerr סורק את ספריות Plex. Plex הגדרת ההגדרות של שרת", + "components.Settings.port": "פורט", + "components.Settings.radarrsettings": "Radarr הגדרות", + "components.Settings.restartrequiredTooltip": "בכדי שההגדרות ייכנסו לתוקף Jellyseerr יש לאתחל את", + "components.Settings.save": "שמירת שינויים", + "components.Settings.saving": "…שמירה", + "components.Settings.scan": "סריקת ספריות", + "components.Settings.scanning": "…סינכרון", + "components.Settings.serverLocal": "מקומי", + "components.Settings.serverRemote": "מרוחק", + "components.Settings.serverSecure": "מאובטח", + "components.Settings.serverpreset": "שרת", + "components.Settings.serverpresetLoad": "יש ללחוץ על הכפתור בכדי לטעון שרתים זמינים", + "components.Settings.serverpresetManualMessage": "הגדרה ידנית", + "components.Settings.serverpresetRefreshing": "…איחזור שרתים", + "components.Settings.serviceSettingsDescription": "רק שניים יוכלו להיות מוגדרים כברירת מחדל (אחד רגיל ואחד איכות גבוהה). מנהלים יוכלו לבחור את השרת שבו תעובד הבקשה לפני שהיא תאושר.{serverType} ניתן להגדיר מספר שרתי", + "components.Settings.services": "שירותים", + "components.Settings.settingUpPlexDescription": ".יש ללחוץ על הכפתור לצד הרשימה כדי לטעון שרתים זמינים plex.tv ניתן לרשום את הפרטים באופן ידני או לבחור שרת מתוך ,Plex בכדי להגדיר את", + "components.Settings.sonarrsettings": "Sonarr הגדרות", + "components.Settings.ssl": "SSL", + "components.Settings.startscan": "התחל סריקה", + "components.Settings.syncJellyfin": "סריקת ספריות", + "components.Settings.syncing": "מסנכרן", + "components.Settings.tautulliApiKey": "API Key", + "components.Settings.tautulliSettings": "Tautulli הגדרות", + "components.Settings.tautulliSettingsDescription": ".Tautulli הגדרת ההגדרות עבור שרת. Jellyseerr מאחזר את היסטוריית הצפייה של Plex מ Tautulli", + "components.Settings.timeout": "שגיאה", + "components.Settings.toastPlexConnecting": "…Plex מנסה להתחבר אל", + "components.Settings.toastPlexConnectingFailure": ".Plex כשל בחיבור אל", + "components.Settings.toastPlexConnectingSuccess": "!בוצע בהצלחה Plex החיבור אל", + "components.Settings.toastPlexRefresh": "…Plex איחזור רשימת שרתי", + "components.Settings.toastPlexRefreshFailure": ".נכשל Plex איחזור שרתי", + "components.Settings.toastPlexRefreshSuccess": "!בוצע בהצלחה Plex איחזור שרתי", + "components.Settings.toastTautulliSettingsFailure": ".Tautulli אירעה שגיאה בעת שמירת הגדרות", + "components.Settings.toastTautulliSettingsSuccess": "!נשמרו בהצלחה Tautulli הגדרות", + "components.Settings.urlBase": "תחילית לכתובת", + "components.Settings.validationApiKey": "API key יש לרשום", + "components.Settings.validationHostnameRequired": "יש לרשום שם מארח או כתובת חוקיים", + "components.Settings.validationPortRequired": "יש לרשום מספר פורט תקין", + "components.Settings.validationUrl": "יש לרשום כתובת", + "components.Settings.validationUrlBaseLeadingSlash": "יש להתחיל עם סלאש בתחילית לכתובת", + "components.Settings.validationUrlBaseTrailingSlash": "אין לסיים את תחילית הכתובת עם סלאש", + "components.Settings.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", + "components.Settings.webAppUrl": "Web App כתובת", + "components.Settings.webAppUrlTip": "\"hosted\" web app במקום אל web app ניתן להפנות משתמשים אל", + "components.Settings.webhook": "Webhook", + "components.Settings.webpush": "Web Push", + "components.Setup.configuremediaserver": "הגדרות שרת המדיה", + "components.Setup.configureservices": "הגדרת שרתים", + "components.Setup.continue": "הבא", + "components.Setup.finish": "סיום הגדרה ראשונית", + "components.Setup.finishing": "…מסיים הגדרה", + "components.Setup.scanbackground": ".הסריקה תרוץ ברקע בכדי לאפשר את המשך ההגדרה הראשונית", + "components.Setup.setup": "Setup", + "components.Setup.signin": "התחברות", + "components.Setup.signinMessage": "יש לבצע לוגאין בכדי להתחיל", + "components.Setup.signinWithJellyfin": "{mediaServerName} שימוש בחשבון", + "components.Setup.signinWithPlex": "Plex שימוש בחשבון", + "components.Setup.tip": "טיפ", + "components.Setup.welcome": "Jellyseerr ברוך הבא אל", + "components.StatusBadge.managemedia": "ניהול ה{mediaType}", + "components.StatusBadge.openinarr": "{arr} פתיחה ב", + "components.StatusBadge.playonplex": "{mediaServerName} ניגון ב", + "components.StatusBadge.seasonepisodenumber": "{episodeNumber}פ{seasonNumber}ע", + "components.StatusBadge.status": "{status}", + "components.StatusBadge.status4k": "4K {status}", + "components.StatusChecker.appUpdated": "{applicationTitle} עודכן", + "components.StatusChecker.appUpdatedDescription": ".יש ללחוץ על השרת מטה בכדי לטעון מחדש את האפליקציה", + "components.StatusChecker.reloadApp": "{applicationTitle} טעינה חוזרת של", + "components.StatusChecker.restartRequired": "יש לאתחל את השרת", + "components.StatusChecker.restartRequiredDescription": ".יש לאתחל את השרת הכדי להחיל את ההגדרות שעודכנו", + "components.TitleCard.addToWatchList": "הוספה לרשימת צפייה", + "components.TitleCard.cleardata": "ניקוי מידע", + "components.TitleCard.mediaerror": "חסר {mediaType}", + "components.TitleCard.tmdbid": "TMDB ID", + "components.TitleCard.tvdbid": "TheTVDB ID", + "components.TitleCard.watchlistCancel": ".{title} בוטלה רשימת הצפייה עבור", + "components.TitleCard.watchlistDeleted": "!{title} הוסר מרשימת הצפייה בהצלחה", + "components.TitleCard.watchlistError": ".משהו השתבש, יש לנסות שוב", + "components.TitleCard.watchlistSuccess": "!{title} התווסף לרשימת הצפייה בהצלחה", + "components.TvDetails.Season.noepisodes": ".אין רשימת פרקים זמינה", + "components.TvDetails.Season.somethingwentwrong": ".אירעה שגיאה האיחזור נתוני עונה", + "components.TvDetails.TvCast.fullseriescast": "הצגת הליהוק המלא", + "components.TvDetails.TvCrew.fullseriescrew": "הצגת הצוות המלא", + "components.TvDetails.anime": "אנימה", + "components.TvDetails.cast": "ליהוק", + "components.TvDetails.episodeCount": "{episodeCount, plural, one {# פרק} other {פרקים #}, }", + "components.TvDetails.episodeRuntime": "זמן פרק", + "components.TvDetails.episodeRuntimeMinutes": "{runtime} דקות", + "components.TvDetails.firstAirDate": "שודר לראשונה", + "components.TvDetails.manageseries": "ניהול סדרה", + "components.TvDetails.network": "{networkCount, plural, one {רשת} other {רשתות}}", + "components.TvDetails.nextAirDate": "תאריך השידור הבא", + "components.TvDetails.originallanguage": "שפת מקור", + "components.TvDetails.originaltitle": "שם מקורי", + "components.TvDetails.overview": "תקציר", + "components.TvDetails.overviewunavailable": "תקציר אינו זמין", + "components.TvDetails.play": "{mediaServerName} ניגון ב", + "components.TvDetails.play4k": "{mediaServerName} 4K ניגון", + "components.TvDetails.productioncountries": "{countryCount, plural, one {מדינה} other {מדינות}} מפיקה", + "components.TvDetails.recommendations": "דומים לסדרה", + "components.TvDetails.reportissue": "דיווח על תקלה", + "components.TvDetails.rtaudiencescore": "Rotten Tomatoes דירוג קהל", + "components.TvDetails.rtcriticsscore": "Rotten Tomatoes טומטומטר של", + "components.TvDetails.seasonnumber": "עונה {seasonNumber}", + "components.TvDetails.seasons": "{seasonCount, plural, one {# עונה} other {# עונות}}", + "components.TvDetails.seasonstitle": "עונות", + "components.TvDetails.showtype": "סוג סדרה", + "components.TvDetails.similar": "סדרות דומות", + "components.TvDetails.status4k": "4K {status}", + "components.TvDetails.streamingproviders": "משודר ברשת", + "components.TvDetails.tmdbuserscore": "TMDB דירוג משתמשי", + "components.TvDetails.viewfullcrew": "הצגת הצוות המלא", + "components.TvDetails.watchtrailer": "צפייה בטריילר", + "components.UserList.accounttype": "סוג", + "components.UserList.admin": "מנהל", + "components.UserList.autogeneratepassword": "יצירת סיסמה באופן אוטומטי", + "components.UserList.autogeneratepasswordTip": "שליחת הסיסמה שנוצרה במייל למשתמש", + "components.UserList.bulkedit": "עריכת הכל", + "components.UserList.create": "יצירה", + "components.UserList.created": "תאריך הצטרפות", + "components.UserList.createlocaluser": "יצירת משתמש מקומי", + "components.UserList.creating": "…יצירה", + "components.UserList.deleteconfirm": ".למחוק את המשתמש? כל הבקשות והמידע גם יימחק", + "components.UserList.deleteuser": "מחיקת משתמש", + "components.UserList.displayName": "שם תצוגה", + "components.UserList.edituser": "עריכת הרשאות משתמש", + "components.UserList.email": "כתובת מייל", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} יובא בהצלחה!", + "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {משתמש} other {משתמשים}} יובא בהצלחה!", + "components.UserList.importfromJellyfin": "{mediaServerName} ייבוא משתמשי", + "components.UserList.importfromJellyfinerror": ".{mediaServerName} משהו השתבש בעת ייבוא משתמשי", + "components.UserList.importfrommediaserver": "{mediaServerName} ייבוא משתמשי", + "components.UserList.importfromplex": "Plex ייבוא משתמשי", + "components.UserList.importfromplexerror": ".Plex משהו השתבש בעת ייבוא משתמשי", + "components.UserList.localLoginDisabled": ".כרגע מבוטלת איפשור לוגאין מקומי הגדרת", + "components.UserList.localuser": "משתמש מקומי", + "components.UserList.mediaServerUser": "{mediaServerName} משתמש", + "components.UserList.newJellyfinsigninenabled": "כרגע מאופשרת חדש {mediaServerName} איפשור לוגאין ההגדרה של. {mediaServerName} אין צורך לייבא משתמשים בעלי גישה לספרייה של", + "components.UserList.newplexsigninenabled": "כרגע מאופשרת חדש {mediaServerName} איפשור לוגאין ההגדרה של. Plex אין צורך לייבא משתמשים בעלי גישה לספרייה של", + "components.UserList.noJellyfinuserstoimport": ".לייבוא {mediaServerName} אין משתמשי", + "components.UserList.nouserstoimport": ".שניתן לייבא Plex אין משתמשי", + "components.UserList.owner": "בעלים", + "components.UserList.password": "סיסמה", + "components.UserList.passwordinfodescription": ".יש להגדיר כתובת יישום ולאפשר הודעות מייל בכדי שיהיה ניתן ליצור סיסמה באופן אוטומטי", + "components.UserList.plexuser": "Plex משתמש", + "components.UserList.role": "תפקיד", + "components.UserList.sortCreated": "תאריך הצטרפות", + "components.UserList.sortDisplayName": "שם תצוגה", + "components.UserList.sortRequests": "מספר בקשות", + "components.UserList.totalrequests": "בקשות", + "components.UserList.user": "משתמש", + "components.UserList.usercreatedfailed": ".אירעה שגיאה ביצירת המשתמש", + "components.UserList.usercreatedfailedexisting": ".כתובת המייל שנרשמה כבר שייכת למשתמש אחר", + "components.UserList.usercreatedsuccess": "!המשתמש נוצר בהצלחה", + "components.UserList.userdeleted": "!המשתמש נמחק בהצלחה", + "components.UserList.userdeleteerror": ".אירעה שגיאה בעת מחיקת המשתמש", + "components.UserList.userfail": ".אירעה שגיאה בעת שמירת הרשאות המשתמש", + "components.UserList.userlist": "רשימת משתמשים", + "components.UserList.users": "משתמשים", + "components.UserList.userssaved": "!הרשאות המשתמש נשמרו בהצלחה", + "components.UserList.validationEmail": "יש לרשום כתובת מייל תקינה", + "components.UserList.validationpasswordminchars": "הסיסמה צריכה להיות באורך 8 תווים", + "components.UserProfile.ProfileHeader.joindate": "חבר מאז {joindate}", + "components.UserProfile.ProfileHeader.profile": "צפייה בפרופיל", + "components.UserProfile.ProfileHeader.settings": "עריכת הגדרות", + "components.UserProfile.ProfileHeader.userid": "{userid} :מזהה משתמש", + "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "סוג החשבון", + "components.UserProfile.UserSettings.UserGeneralSettings.admin": "מנהל", + "components.UserProfile.UserSettings.UserGeneralSettings.applanguage": "שפת התצוגה", + "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "מזהה משתמש מה חדש", + "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "מזהה ספרות הינו מזהה הספרות המשוייך לחשבון Discord", + "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "שם תצוגה", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "מייל", + "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "עקיפת מגבלה", + "components.UserProfile.UserSettings.UserGeneralSettings.general": "כללי", + "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "הגדרות כללות", + "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "({language}) ברירת מחדל", + "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "משתמש מקומי", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} משתמש", + "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "הגבלת בקשות סרטים", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Discover שפת", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "סינון תוכן לפי שפת המקור", + "components.UserProfile.UserSettings.UserGeneralSettings.owner": "בעלים", + "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex משתמש", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "בקשת סרטים אוטומטית", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Plex רשימת צפייה של בקשת סרטים אוטומטית עבור", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "בקשת סדרות אוטומטית", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Plex רשימת צפייה של בקשת סדרות אוטומטית עבור", + "components.UserProfile.UserSettings.UserGeneralSettings.region": "איזור מה חדש", + "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "סינון תוכן לפי זמינות איזורית", + "components.UserProfile.UserSettings.UserGeneralSettings.role": "תפקיד", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "שמירת שיניים", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "…בשמירה", + "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "הגבלת בקשת סדרות", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": ".אירעה שגיאה בעת שמירת ההגדרות", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "!ההגדרות נשמרו בהצלחה", + "components.UserProfile.UserSettings.UserGeneralSettings.user": "משתמש", + "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "תקין Discord user ID יש לרשום", + "components.UserProfile.UserSettings.UserGeneralSettings.validationemailformat": "יש לרשום מייל תקין", + "components.UserProfile.UserSettings.UserGeneralSettings.validationemailrequired": "יש לרשום מייל", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "ברירת המחדל של המכשיר", + "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID", + "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "שמקושר לחשבון שלך multi-digit ID number ה", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": ".Discord אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "!נשמרו בהצלחה Discord הגדרות התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.email": "מייל", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": ".אירעה שגיאה בעת שמירת הגדרות התראות למייל", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "!הגדרות התראות למייל נשמרו בהצלחה", + "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "הגדרות התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "PGP Public Key", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "OpenPGP הצפנת הודעות מייל באמצעות", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessToken": "Access Token", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "בהגדרות חשבון Token ניתן ליצור", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": ".Pushbullet אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "!נשמרו בהצלחה Pushbullet הגדרות התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Application API Token", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "{applicationTitle} לשימוש עם רישום אפליקציה", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "User או Group Key", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "מזהה משתמש או קבוצה יש לרשום מפתח 30 תווים של", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": ".Pushover אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "!נשמרו בהצלחה Pushover הגדרות התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "שליחה שקטה", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "שליחת התראות ללא צליל", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "צליל התראה", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat ID", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat, add @get_id_bot, and issue the /my_id command", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": ".אירעה שגיאה בשמירת הגדרות התראות לטלגרם", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "!נשמרו בהצלחה Telegram הגדרות התראות", + "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "תקין user ID יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "תקין PGP public key יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "תקין access token יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "תקין application token יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "תקין group key יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "תקין chat ID יש לרשום", + "components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": ".Web אירעה שגיאה בעת שמירת הגדרות התראות עבור", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "!נשמרו בהצלחה Web הגדרות התראות", + "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "אימות סיסמה", + "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "סיסמה נוכחית", + "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "סיסמה חדשה", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": ".אין סיסמה מוגדרת למשתמש. בכדי שיתאפשר לוגאין עם \"משתמש מקומי\" באמצעות מייל, יש להגדיר סיסמה", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": ".אין סיסמה מוגדרת בחשבון. בכדי שיתאפשר לוגאין עם \"משתמש מקומי\" באמצעות מייל, יש להגדיר סיסמה", + "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": ".אין הרשאות מספקות בכדי לשנות את הסיסמה עבור המשתמש הזה", + "components.UserProfile.UserSettings.UserPasswordChange.password": "סיסמה", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": ".משהו השתבש בשמירת הסיסמה", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "?משהו השתבש בשמירת הסיסמה. האם הסיסמה הנוכחית נכונה", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "!הסיסמה נשמרה בהצלחה", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "יש לרשום סיסמה זהה", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "הסיסמאות צריכות להיות זהות", + "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "יש לרשום את הסיסמה הנוכחית", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "יש לרשום סיסמה חדשה", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "הסיסמה צריכה להיות באורך 8 תווים לפחות", + "components.UserProfile.UserSettings.UserPermissions.permissions": "הרשאות", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": ".משהו השתבש בשמירת ההגדרות", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "!הרשאות נשמרו בהצלחה", + "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": ".אין אפשרות לשנות את ההגדרות לעצמך", + "components.UserProfile.UserSettings.menuChangePass": "סיסמה", + "components.UserProfile.UserSettings.menuGeneralSettings": "כללי", + "components.UserProfile.UserSettings.menuNotifications": "התראות", + "components.UserProfile.UserSettings.menuPermissions": "הרשאות", + "components.UserProfile.UserSettings.unauthorizedDescription": ".אין הרשאות מספקות הכדי לשנות את הגדרות המשתמש הזה", + "components.UserProfile.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", + "components.UserProfile.limit": "{limit} מתוך {remaining}", + "components.UserProfile.localWatchlist": "{username} רשימת הצפייה של", + "components.UserProfile.movierequests": "בקשות סרטים", + "components.UserProfile.pastdays": "{type} (ימים האחרונים {days} ב)", + "components.UserProfile.plexwatchlist": "Plex רשימת צפייה של", + "components.UserProfile.recentlywatched": "נצפו לאחרונה", + "components.UserProfile.recentrequests": "התבקשו לאחרונה", + "components.UserProfile.requestsperdays": "{limit} נותרו", + "components.UserProfile.seriesrequest": "בקשת סדרות", + "components.UserProfile.totalrequests": "סה״כ בקשות", + "components.UserProfile.unlimited": "חופשי", + "i18n.advanced": "מתקדם", + "i18n.all": "הכל", + "i18n.approve": "אישור", + "i18n.approved": "אושר", + "i18n.areyousure": "?בטוח", + "i18n.available": "זמינה", + "i18n.back": "חזרה", + "i18n.cancel": "ביטול", + "i18n.canceling": "…מבטל", + "i18n.close": "סגירה", + "i18n.collection": "אוסף", + "i18n.decline": "סרב", + "i18n.declined": "סורב", + "i18n.delete": "מחיקה", + "i18n.deleting": "…מוחק", + "i18n.delimitedlist": "{a}, {b}", + "i18n.edit": "עריכה", + "i18n.experimental": "נסיוני", + "i18n.failed": "נכשל", + "i18n.import": "ייבוא", + "i18n.importing": "…מייבא", + "i18n.loading": "…טוען", + "i18n.movie": "סרט", + "i18n.movies": "סרטים", + "i18n.next": "הבא", + "i18n.noresults": "אין תוצאות", + "i18n.notrequested": "אין בקשה", + "i18n.open": "פתוח", + "i18n.partiallyavailable": "זמין חלקית", + "i18n.pending": "ממתין", + "i18n.previous": "הקודם", + "i18n.processing": "מעבד", + "i18n.request": "בקשה", + "i18n.request4k": "4K בקשה של", + "i18n.requested": "בקשה ממתינה", + "i18n.requesting": "…מבקש", + "i18n.resolved": "נפתר", + "i18n.restartRequired": "נדרש איתחול", + "i18n.resultsperpage": "מציג {pageSize} תוצאות בעמוד", + "i18n.retry": "ניסיון חוזר", + "i18n.retrying": "…ניסיון חוזר", + "i18n.save": "שמירת שינויים", + "i18n.saving": "…שמירה", + "i18n.settings": "הגדרות", + "i18n.showingresults": "תוצאות {total} עד {to} מתוך {from} מציג", + "i18n.status": "סטאטוס", + "i18n.test": "בדיקה", + "i18n.testing": "…בדיקה", + "i18n.tvshow": "סדרה", + "i18n.tvshows": "סדרות", + "i18n.unavailable": "אין זמינות", + "i18n.usersettings": "הגדרות משתמש", + "i18n.view": "צפייה", + "pages.errormessagewithcode": "{statusCode} - {error}", + "pages.internalservererror": "שגיאת שרת", + "pages.oops": "אופס", + "pages.pagenotfound": "הדף חסר", + "pages.returnHome": "חזרה למסך הבית", + "pages.serviceunavailable": "השירות אינו זמין", + "pages.somethingwentwrong": "משהו השתבש" } From 0116c13e0632d1ccec43299fbb10cd71db45bc29 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 24 Jul 2024 21:31:18 +0200 Subject: [PATCH 06/53] fix(api): fix nextjs error handler (#882) This PR removes a custom error handler that sometimes caused issues by sending headers after some content had already been sent. --- server/index.ts | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/server/index.ts b/server/index.ts index 2d90f05c..00ddf121 100644 --- a/server/index.ts +++ b/server/index.ts @@ -26,7 +26,6 @@ import { getClientIp } from '@supercharge/request-ip'; import { TypeormStore } from 'connect-typeorm/out'; import cookieParser from 'cookie-parser'; import csurf from 'csurf'; -import type { NextFunction, Request, Response } from 'express'; import express from 'express'; import * as OpenApiValidator from 'express-openapi-validator'; import type { Store } from 'express-session'; @@ -180,6 +179,16 @@ app ); const apiDocs = YAML.load(API_SPEC_PATH); server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs)); + /** + * Workaround to avoid the error from the OpenAPI validator when an user send a + * request without any session cookie + */ + server.use((req, res, next) => { + if (!req.cookies['connect.sid']) { + req.cookies['connect.sid'] = 'none'; + } + next(); + }); server.use( OpenApiValidator.middleware({ apiSpec: API_SPEC_PATH, @@ -203,23 +212,7 @@ app // Do not set cookies so CDNs can cache them server.use('/imageproxy', clearCookies, imageproxy); - server.get('*', (req, res) => handle(req, res)); - server.use( - ( - err: { status: number; message: string; errors: string[] }, - _req: Request, - res: Response, - // We must provide a next function for the function signature here even though its not used - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _next: NextFunction - ) => { - // format error - res.status(err.status || 500).json({ - message: err.message, - errors: err.errors, - }); - } - ); + server.all('*', (req, res) => handle(req, res)); const port = Number(process.env.PORT) || 5055; const host = process.env.HOST; From 62dbde448c7f7d530de8534bb8538452d0f91276 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:48:29 +0500 Subject: [PATCH 07/53] revert: fix(api): fix nextjs error handler (#882) (#892) This commit reverts the nextjs error handler fix that was introduced in #882 as that change requires further refactor which should be held off for another version owing to the fact that there are currently a lot of changes ready for the next version of jellyseerr. --- server/index.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/server/index.ts b/server/index.ts index 00ddf121..2d90f05c 100644 --- a/server/index.ts +++ b/server/index.ts @@ -26,6 +26,7 @@ import { getClientIp } from '@supercharge/request-ip'; import { TypeormStore } from 'connect-typeorm/out'; import cookieParser from 'cookie-parser'; import csurf from 'csurf'; +import type { NextFunction, Request, Response } from 'express'; import express from 'express'; import * as OpenApiValidator from 'express-openapi-validator'; import type { Store } from 'express-session'; @@ -179,16 +180,6 @@ app ); const apiDocs = YAML.load(API_SPEC_PATH); server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs)); - /** - * Workaround to avoid the error from the OpenAPI validator when an user send a - * request without any session cookie - */ - server.use((req, res, next) => { - if (!req.cookies['connect.sid']) { - req.cookies['connect.sid'] = 'none'; - } - next(); - }); server.use( OpenApiValidator.middleware({ apiSpec: API_SPEC_PATH, @@ -212,7 +203,23 @@ app // Do not set cookies so CDNs can cache them server.use('/imageproxy', clearCookies, imageproxy); - server.all('*', (req, res) => handle(req, res)); + server.get('*', (req, res) => handle(req, res)); + server.use( + ( + err: { status: number; message: string; errors: string[] }, + _req: Request, + res: Response, + // We must provide a next function for the function signature here even though its not used + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _next: NextFunction + ) => { + // format error + res.status(err.status || 500).json({ + message: err.message, + errors: err.errors, + }); + } + ); const port = Number(process.env.PORT) || 5055; const host = process.env.HOST; From 3fc14c9e2262463afec666e7f54e38d0d36cff68 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Fri, 26 Jul 2024 18:12:41 +0200 Subject: [PATCH 08/53] fix: rewrite the rate limit utility (#896) --- server/utils/rateLimit.ts | 53 ++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/server/utils/rateLimit.ts b/server/utils/rateLimit.ts index 3d69d6c5..0ecdec5c 100644 --- a/server/utils/rateLimit.ts +++ b/server/utils/rateLimit.ts @@ -7,9 +7,10 @@ type RateLimiteState) => Promise, U> = { queue: { args: Parameters; resolve: (value: U) => void; + reject: (reason?: unknown) => void; }[]; - activeRequests: number; - timer: NodeJS.Timeout | null; + lastTimestamps: number[]; + timeout: ReturnType; }; const rateLimitById: Record = {}; @@ -27,46 +28,40 @@ export default function rateLimit< >(fn: T, options: RateLimitOptions): (...args: Parameters) => Promise { const state: RateLimiteState = (rateLimitById[ options.id || '' - ] as RateLimiteState) || { queue: [], activeRequests: 0, timer: null }; + ] as RateLimiteState) || { queue: [], lastTimestamps: [] }; if (options.id) { rateLimitById[options.id] = state; } const processQueue = () => { - if (state.queue.length === 0) { - if (state.timer) { - clearInterval(state.timer); - state.timer = null; - } - return; - } + // remove old timestamps + state.lastTimestamps = state.lastTimestamps.filter( + (timestamp) => Date.now() - timestamp < 1000 + ); - while (state.activeRequests < options.maxRPS) { - state.activeRequests++; + if (state.lastTimestamps.length < options.maxRPS) { + // process requests if RPS not exceeded const item = state.queue.shift(); - if (!item) break; - const { args, resolve } = item; + if (!item) return; + state.lastTimestamps.push(Date.now()); + const { args, resolve, reject } = item; fn(...args) .then(resolve) - .finally(() => { - state.activeRequests--; - if (state.queue.length > 0) { - if (!state.timer) { - state.timer = setInterval(processQueue, 1000); - } - } else { - if (state.timer) { - clearInterval(state.timer); - state.timer = null; - } - } - }); + .catch(reject); + processQueue(); + } else { + // rerun once the oldest item in queue is older than 1s + if (state.timeout) clearTimeout(state.timeout); + state.timeout = setTimeout( + processQueue, + 1000 - (Date.now() - state.lastTimestamps[0]) + ); } }; return (...args: Parameters): Promise => { - return new Promise((resolve) => { - state.queue.push({ args, resolve }); + return new Promise((resolve, reject) => { + state.queue.push({ args, resolve, reject }); processQueue(); }); }; From fccfca6ed06c8dc599e1ea4b1b3dbac48eb3a7f6 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 27 Jul 2024 01:43:16 +0200 Subject: [PATCH 09/53] fix: enhance error messages when Fetch API fails (#893) --- server/api/externalapi.ts | 62 +++++++++++++++++-- server/api/jellyfin.ts | 22 +++---- server/api/servarr/radarr.ts | 9 ++- server/api/servarr/sonarr.ts | 9 ++- server/lib/notifications/agents/discord.ts | 14 ++++- server/lib/notifications/agents/gotify.ts | 14 ++++- server/lib/notifications/agents/lunasea.ts | 14 ++++- server/lib/notifications/agents/pushbullet.ts | 42 +++++++++++-- server/lib/notifications/agents/pushover.ts | 54 +++++++++++++--- server/lib/notifications/agents/slack.ts | 14 ++++- server/lib/notifications/agents/telegram.ts | 42 +++++++++++-- server/lib/notifications/agents/webhook.ts | 14 ++++- src/components/Login/JellyfinLogin.tsx | 11 +++- src/components/Login/index.tsx | 11 +++- src/components/Settings/SettingsJellyfin.tsx | 24 +++++-- src/components/UserList/index.tsx | 11 +++- 16 files changed, 308 insertions(+), 59 deletions(-) diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 752601bf..10355800 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -68,7 +68,10 @@ class ExternalAPI { if (!response.ok) { const text = await response.text(); throw new Error( - `${response.status} ${response.statusText}${text ? ': ' + text : ''}` + `${response.status} ${response.statusText}${text ? ': ' + text : ''}`, + { + cause: response, + } ); } const data = await this.getDataFromResponse(response); @@ -106,6 +109,15 @@ class ExternalAPI { }, body: JSON.stringify(data), }); + if (!response.ok) { + const text = await response.text(); + throw new Error( + `${response.status} ${response.statusText}${text ? ': ' + text : ''}`, + { + cause: response, + } + ); + } const resData = await this.getDataFromResponse(response); if (this.cache) { @@ -141,6 +153,15 @@ class ExternalAPI { }, body: JSON.stringify(data), }); + if (!response.ok) { + const text = await response.text(); + throw new Error( + `${response.status} ${response.statusText}${text ? ': ' + text : ''}`, + { + cause: response, + } + ); + } const resData = await this.getDataFromResponse(response); if (this.cache) { @@ -163,6 +184,15 @@ class ExternalAPI { ...config?.headers, }, }); + if (!response.ok) { + const text = await response.text(); + throw new Error( + `${response.status} ${response.statusText}${text ? ': ' + text : ''}`, + { + cause: response, + } + ); + } const data = await this.getDataFromResponse(response); return data; @@ -197,6 +227,17 @@ class ExternalAPI { ...config?.headers, }, }).then(async (response) => { + if (!response.ok) { + const text = await response.text(); + throw new Error( + `${response.status} ${response.statusText}${ + text ? ': ' + text : '' + }`, + { + cause: response, + } + ); + } const data = await this.getDataFromResponse(response); this.cache?.set(cacheKey, data, ttl ?? DEFAULT_TTL); }); @@ -212,6 +253,15 @@ class ExternalAPI { ...config?.headers, }, }); + if (!response.ok) { + const text = await response.text(); + throw new Error( + `${response.status} ${response.statusText}${text ? ': ' + text : ''}`, + { + cause: response, + } + ); + } const data = await this.getDataFromResponse(response); if (this.cache) { @@ -250,13 +300,13 @@ class ExternalAPI { } private async getDataFromResponse(response: Response) { - const contentType = response.headers.get('Content-Type')?.split(';')[0]; - if (contentType === 'application/json') { + const contentType = response.headers.get('Content-Type'); + if (contentType?.includes('application/json')) { return await response.json(); } else if ( - contentType === 'application/xml' || - contentType === 'text/html' || - contentType === 'text/plain' + contentType?.includes('application/xml') || + contentType?.includes('text/html') || + contentType?.includes('text/plain') ) { return await response.text(); } else { diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts index 42864045..ff5144ce 100644 --- a/server/api/jellyfin.ts +++ b/server/api/jellyfin.ts @@ -152,7 +152,7 @@ class JellyfinAPI extends ExternalAPI { try { return await authenticate(false); } catch (e) { - const status = e.response?.status; + const status = e.cause?.status; const networkErrorCodes = new Set([ 'ECONNREFUSED', @@ -190,7 +190,7 @@ class JellyfinAPI extends ExternalAPI { return systemInfoResponse; } catch (e) { - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -207,7 +207,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.Unknown); + throw new ApiError(e.cause?.status, ApiErrorCode.Unknown); } } @@ -222,7 +222,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -238,7 +238,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -317,7 +317,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -338,7 +338,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -353,7 +353,7 @@ class JellyfinAPI extends ExternalAPI { return itemResponse; } catch (e) { if (availabilitySync.running) { - if (e.response && e.response.status === 500) { + if (e.cause?.status === 500) { return undefined; } } @@ -362,7 +362,7 @@ class JellyfinAPI extends ExternalAPI { `Something went wrong while getting library content from the Jellyfin server: ${e.message}`, { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -377,7 +377,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } @@ -402,7 +402,7 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API' } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } } diff --git a/server/api/servarr/radarr.ts b/server/api/servarr/radarr.ts index e05d4071..51d30037 100644 --- a/server/api/servarr/radarr.ts +++ b/server/api/servarr/radarr.ts @@ -179,13 +179,20 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> { } return data; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error( 'Failed to add movie to Radarr. This might happen if the movie already exists, in which case you can safely ignore this error.', { label: 'Radarr', errorMessage: e.message, options, - response: e?.response?.data, + response: errorData, } ); throw new Error('Failed to add movie to Radarr'); diff --git a/server/api/servarr/sonarr.ts b/server/api/servarr/sonarr.ts index 1650b6d0..67c9dd2a 100644 --- a/server/api/servarr/sonarr.ts +++ b/server/api/servarr/sonarr.ts @@ -257,11 +257,18 @@ class SonarrAPI extends ServarrBase<{ return createdSeriesData; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Something went wrong while adding a series to Sonarr.', { label: 'Sonarr API', errorMessage: e.message, options, - response: e?.response?.data, + response: errorData, }); throw new Error('Failed to add series'); } diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts index 1585af5a..e949e3e1 100644 --- a/server/lib/notifications/agents/discord.ts +++ b/server/lib/notifications/agents/discord.ts @@ -291,7 +291,7 @@ class DiscordAgent } } - await fetch(settings.options.webhookUrl, { + const response = await fetch(settings.options.webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -305,15 +305,25 @@ class DiscordAgent content: userMentions.join(' '), } as DiscordWebhookPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } return true; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Discord notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/gotify.ts b/server/lib/notifications/agents/gotify.ts index c390fbf8..299effe4 100644 --- a/server/lib/notifications/agents/gotify.ts +++ b/server/lib/notifications/agents/gotify.ts @@ -132,22 +132,32 @@ class GotifyAgent const endpoint = `${settings.options.url}/message?token=${settings.options.token}`; const notificationPayload = this.getNotificationPayload(type, payload); - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(notificationPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } return true; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Gotify notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/lunasea.ts b/server/lib/notifications/agents/lunasea.ts index 1e8e3efa..b8e47384 100644 --- a/server/lib/notifications/agents/lunasea.ts +++ b/server/lib/notifications/agents/lunasea.ts @@ -100,7 +100,7 @@ class LunaSeaAgent }); try { - await fetch(settings.options.webhookUrl, { + const response = await fetch(settings.options.webhookUrl, { method: 'POST', headers: settings.options.profileName ? { @@ -114,15 +114,25 @@ class LunaSeaAgent }, body: JSON.stringify(this.buildPayload(type, payload)), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } return true; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending LunaSea notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/pushbullet.ts b/server/lib/notifications/agents/pushbullet.ts index 68aa911f..882e8276 100644 --- a/server/lib/notifications/agents/pushbullet.ts +++ b/server/lib/notifications/agents/pushbullet.ts @@ -122,7 +122,7 @@ class PushbulletAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -133,13 +133,23 @@ class PushbulletAgent channel_tag: settings.options.channelTag, }), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushbullet notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -164,7 +174,7 @@ class PushbulletAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -172,14 +182,24 @@ class PushbulletAgent }, body: JSON.stringify(notificationPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushbullet notification', { label: 'Notifications', recipient: payload.notifyUser.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -215,7 +235,7 @@ class PushbulletAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -223,14 +243,24 @@ class PushbulletAgent }, body: JSON.stringify(notificationPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushbullet notification', { label: 'Notifications', recipient: user.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts index e2a0650e..abdb78f2 100644 --- a/server/lib/notifications/agents/pushover.ts +++ b/server/lib/notifications/agents/pushover.ts @@ -52,6 +52,9 @@ class PushoverAgent ): Promise> { try { const response = await fetch(imageUrl); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } const arrayBuffer = await response.arrayBuffer(); const base64 = Buffer.from(arrayBuffer).toString('base64'); const contentType = ( @@ -64,10 +67,17 @@ class PushoverAgent attachment_type: contentType, }; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error getting image payload', { label: 'Notifications', errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return {}; } @@ -200,7 +210,7 @@ class PushoverAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -212,13 +222,23 @@ class PushoverAgent sound: settings.options.sound, } as PushoverPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushover notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -246,7 +266,7 @@ class PushoverAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -258,14 +278,24 @@ class PushoverAgent sound: payload.notifyUser.settings.pushoverSound, } as PushoverPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushover notification', { label: 'Notifications', recipient: payload.notifyUser.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -302,7 +332,7 @@ class PushoverAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -313,14 +343,24 @@ class PushoverAgent user: user.settings.pushoverUserKey, } as PushoverPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Pushover notification', { label: 'Notifications', recipient: user.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts index 175f38a5..7a3b9790 100644 --- a/server/lib/notifications/agents/slack.ts +++ b/server/lib/notifications/agents/slack.ts @@ -237,22 +237,32 @@ class SlackAgent subject: payload.subject, }); try { - await fetch(settings.options.webhookUrl, { + const response = await fetch(settings.options.webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(this.buildEmbed(type, payload)), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } return true; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Slack notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts index 50cea9c0..a66f9710 100644 --- a/server/lib/notifications/agents/telegram.ts +++ b/server/lib/notifications/agents/telegram.ts @@ -174,7 +174,7 @@ class TelegramAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -185,13 +185,23 @@ class TelegramAgent disable_notification: !!settings.options.sendSilently, } as TelegramMessagePayload | TelegramPhotoPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Telegram notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -215,7 +225,7 @@ class TelegramAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -227,14 +237,24 @@ class TelegramAgent !!payload.notifyUser.settings.telegramSendSilently, } as TelegramMessagePayload | TelegramPhotoPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Telegram notification', { label: 'Notifications', recipient: payload.notifyUser.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; @@ -268,7 +288,7 @@ class TelegramAgent }); try { - await fetch(endpoint, { + const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -279,14 +299,24 @@ class TelegramAgent disable_notification: !!user.settings?.telegramSendSilently, } as TelegramMessagePayload | TelegramPhotoPayload), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending Telegram notification', { label: 'Notifications', recipient: user.displayName, type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts index 7382f1d0..d91683be 100644 --- a/server/lib/notifications/agents/webhook.ts +++ b/server/lib/notifications/agents/webhook.ts @@ -177,7 +177,7 @@ class WebhookAgent }); try { - await fetch(settings.options.webhookUrl, { + const response = await fetch(settings.options.webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -187,15 +187,25 @@ class WebhookAgent }, body: JSON.stringify(this.buildPayload(type, payload)), }); + if (!response.ok) { + throw new Error(response.statusText, { cause: response }); + } return true; } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } logger.error('Error sending webhook notification', { label: 'Notifications', type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response?.data, + response: errorData, }); return false; diff --git a/src/components/Login/JellyfinLogin.tsx b/src/components/Login/JellyfinLogin.tsx index 49e2d62f..dd08e53d 100644 --- a/src/components/Login/JellyfinLogin.tsx +++ b/src/components/Login/JellyfinLogin.tsx @@ -119,10 +119,17 @@ const JellyfinLogin: React.FC = ({ email: values.email, }), }); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } let errorMessage = null; - switch (e.response?.data?.message) { + switch (errorData?.message) { case ApiErrorCode.InvalidUrl: errorMessage = messages.invalidurlerror; break; diff --git a/src/components/Login/index.tsx b/src/components/Login/index.tsx index 27695d19..eca7b6ac 100644 --- a/src/components/Login/index.tsx +++ b/src/components/Login/index.tsx @@ -50,14 +50,21 @@ const Login = () => { }, body: JSON.stringify({ authToken }), }); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); const data = await res.json(); if (data?.id) { revalidate(); } } catch (e) { - setError(e.response.data.message); + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } + setError(errorData?.message); setAuthToken(undefined); setProcessing(false); } diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index a5fb3498..91c44e12 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -173,11 +173,18 @@ const SettingsJellyfin: React.FC = ({ const res = await fetch( `/api/v1/settings/jellyfin/library?${searchParams.toString()}` ); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); setIsSyncing(false); revalidate(); } catch (e) { - if (e.response.data.message === 'SYNC_ERROR_GROUPED_FOLDERS') { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } + if (errorData?.message === 'SYNC_ERROR_GROUPED_FOLDERS') { toasts.addToast( intl.formatMessage( messages.jellyfinSyncFailedAutomaticGroupedFolders @@ -187,7 +194,7 @@ const SettingsJellyfin: React.FC = ({ appearance: 'warning', } ); - } else if (e.response.data.message === 'SYNC_ERROR_NO_LIBRARIES') { + } else if (errorData?.message === 'SYNC_ERROR_NO_LIBRARIES') { toasts.addToast( intl.formatMessage(messages.jellyfinSyncFailedNoLibrariesFound), { @@ -485,7 +492,7 @@ const SettingsJellyfin: React.FC = ({ jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl, } as JellyfinSettings), }); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); addToast( intl.formatMessage(messages.jellyfinSettingsSuccess, { @@ -500,7 +507,14 @@ const SettingsJellyfin: React.FC = ({ } ); } catch (e) { - if (e.response?.data?.message === ApiErrorCode.InvalidUrl) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } + if (errorData?.message === ApiErrorCode.InvalidUrl) { addToast( intl.formatMessage(messages.invalidurlerror, { mediaServerName: diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index bc840dd4..df1ce3e4 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -295,16 +295,23 @@ const UserList = () => { password: values.genpassword ? null : values.password, }), }); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); addToast(intl.formatMessage(messages.usercreatedsuccess), { appearance: 'success', autoDismiss: true, }); setCreateModal({ isOpen: false }); } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } addToast( intl.formatMessage( - e.response.data.errors?.includes('USER_EXISTS') + errorData.errors?.includes('USER_EXISTS') ? messages.usercreatedfailedexisting : messages.usercreatedfailed ), From 422085523e5dfc132f3c3ca19eaa87117828b7be Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 29 Jul 2024 16:49:51 +0200 Subject: [PATCH 10/53] fix: resize header image in network and studio pages (#902) --- src/components/Discover/DiscoverNetwork/index.tsx | 4 ++-- src/components/Discover/DiscoverStudio/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Discover/DiscoverNetwork/index.tsx b/src/components/Discover/DiscoverNetwork/index.tsx index af3f9ed5..00e28930 100644 --- a/src/components/Discover/DiscoverNetwork/index.tsx +++ b/src/components/Discover/DiscoverNetwork/index.tsx @@ -48,11 +48,11 @@ const DiscoverTvNetwork = () => {
{firstResultData?.network.logoPath ? ( -
+
{firstResultData.network.name}
diff --git a/src/components/Discover/DiscoverStudio/index.tsx b/src/components/Discover/DiscoverStudio/index.tsx index 23cb2130..b4a6dfae 100644 --- a/src/components/Discover/DiscoverStudio/index.tsx +++ b/src/components/Discover/DiscoverStudio/index.tsx @@ -48,11 +48,11 @@ const DiscoverMovieStudio = () => {
{firstResultData?.studio.logoPath ? ( -
+
{firstResultData.studio.name}
From d5f817e734131cdacc229361d9498a095af57950 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 29 Jul 2024 21:27:31 +0200 Subject: [PATCH 11/53] fix: remove email requirement for the user, and use the username if no email provided (#900) * fix: remove email requirement for the user, and use the username if no email provided * fix: update translations * fix: remove useless console.log * test: fix user list test * fix: disallow Plex users from changing their email --- cypress/e2e/user/user-list.cy.ts | 4 +- server/routes/auth.ts | 18 ++++---- server/routes/user/index.ts | 21 +++++++-- server/routes/user/usersettings.ts | 4 +- src/components/Layout/UserDropdown/index.tsx | 8 ++-- .../UserList/JellyfinImportModal.tsx | 2 +- src/components/UserList/index.tsx | 46 +++++++++++-------- .../UserGeneralSettings/index.tsx | 21 ++++++--- src/hooks/useUser.ts | 1 + src/i18n/locale/en.json | 5 +- 10 files changed, 85 insertions(+), 45 deletions(-) diff --git a/cypress/e2e/user/user-list.cy.ts b/cypress/e2e/user/user-list.cy.ts index 503bd23f..82117023 100644 --- a/cypress/e2e/user/user-list.cy.ts +++ b/cypress/e2e/user/user-list.cy.ts @@ -1,5 +1,5 @@ const testUser = { - displayName: 'Test User', + username: 'Test User', emailAddress: 'test@seeerr.dev', password: 'test1234', }; @@ -32,7 +32,7 @@ describe('User List', () => { cy.get('[data-testid=modal-title]').should('contain', 'Create Local User'); - cy.get('#displayName').type(testUser.displayName); + cy.get('#username').type(testUser.username); cy.get('#email').type(testUser.emailAddress); cy.get('#password').type(testUser.password); diff --git a/server/routes/auth.ts b/server/routes/auth.ts index a473522f..966dc269 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -320,7 +320,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { // with admin permission settings.main.mediaServerType = MediaServerType.JELLYFIN; user = new User({ - email: body.email, + email: body.email || account.User.Name, jellyfinUsername: account.User.Name, jellyfinUserId: account.User.Id, jellyfinDeviceId: deviceId, @@ -328,7 +328,10 @@ authRoutes.post('/jellyfin', async (req, res, next) => { permissions: Permission.ADMIN, avatar: account.User.PrimaryImageTag ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` - : gravatarUrl(body.email ?? '', { default: 'mm', size: 200 }), + : gravatarUrl(body.email || account.User.Name, { + default: 'mm', + size: 200, + }), userType: UserType.JELLYFIN, }); @@ -371,7 +374,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { if (account.User.PrimaryImageTag) { user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; } else { - user.avatar = gravatarUrl(user.email, { + user.avatar = gravatarUrl(user.email || account.User.Name, { default: 'mm', size: 200, }); @@ -413,10 +416,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { } ); - if (!body.email) { - throw new Error('add_email'); - } - user = new User({ email: body.email, jellyfinUsername: account.User.Name, @@ -426,7 +425,10 @@ authRoutes.post('/jellyfin', async (req, res, next) => { permissions: settings.main.defaultPermissions, avatar: account.User.PrimaryImageTag ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` - : gravatarUrl(body.email, { default: 'mm', size: 200 }), + : gravatarUrl(body.email || account.User.Name, { + default: 'mm', + size: 200, + }), userType: UserType.JELLYFIN, }); //initialize Jellyfin/Emby users with local login diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index 22ebcaa8..016709c6 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -41,7 +41,19 @@ router.get('/', async (req, res, next) => { break; case 'displayname': query = query.orderBy( - "(CASE WHEN (user.username IS NULL OR user.username = '') THEN (CASE WHEN (user.plexUsername IS NULL OR user.plexUsername = '') THEN user.email ELSE LOWER(user.plexUsername) END) ELSE LOWER(user.username) END)", + `CASE WHEN (user.username IS NULL OR user.username = '') THEN ( + CASE WHEN (user.plexUsername IS NULL OR user.plexUsername = '') THEN ( + CASE WHEN (user.jellyfinUsername IS NULL OR user.jellyfinUsername = '') THEN + user.email + ELSE + LOWER(user.jellyfinUsername) + END) + ELSE + LOWER(user.jellyfinUsername) + END) + ELSE + LOWER(user.username) + END`, 'ASC' ); break; @@ -90,12 +102,13 @@ router.post( const settings = getSettings(); const body = req.body; + const email = body.email || body.username; const userRepository = getRepository(User); const existingUser = await userRepository .createQueryBuilder('user') .where('user.email = :email', { - email: body.email.toLowerCase(), + email: email.toLowerCase(), }) .getOne(); @@ -108,7 +121,7 @@ router.post( } const passedExplicitPassword = body.password && body.password.length > 0; - const avatar = gravatarUrl(body.email, { default: 'mm', size: 200 }); + const avatar = gravatarUrl(email, { default: 'mm', size: 200 }); if ( !passedExplicitPassword && @@ -118,9 +131,9 @@ router.post( } const user = new User({ + email, avatar: body.avatar ?? avatar, username: body.username, - email: body.email, password: body.password, permissions: settings.main.defaultPermissions, plexToken: '', diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts index 53eed9ef..9669cb18 100644 --- a/server/routes/user/usersettings.ts +++ b/server/routes/user/usersettings.ts @@ -98,7 +98,9 @@ userSettingsRoutes.post< } user.username = req.body.username; - user.email = req.body.email ?? user.email; + if (user.jellyfinUsername) { + user.email = req.body.email || user.jellyfinUsername || user.email; + } // Update quota values only if the user has the correct permissions if ( diff --git a/src/components/Layout/UserDropdown/index.tsx b/src/components/Layout/UserDropdown/index.tsx index 4abe04e3..8620f606 100644 --- a/src/components/Layout/UserDropdown/index.tsx +++ b/src/components/Layout/UserDropdown/index.tsx @@ -90,9 +90,11 @@ const UserDropdown = () => { {user?.displayName} - - {user?.email} - + {user?.displayName?.toLowerCase() !== user?.email && ( + + {user?.email} + + )}
{user && } diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx index 3d1dc9f6..ed549ac5 100644 --- a/src/components/UserList/JellyfinImportModal.tsx +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -78,7 +78,7 @@ const JellyfinImportModal: React.FC = ({ }), }); if (!res.ok) throw new Error(); - const { data: createdUsers } = await res.json(); + const createdUsers = await res.json(); if (!createdUsers.length) { throw new Error('No users were imported from Jellyfin.'); diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index df1ce3e4..66df469b 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -68,14 +68,15 @@ const messages = defineMessages('components.UserList', { usercreatedfailedexisting: 'The provided email address is already in use by another user.', usercreatedsuccess: 'User created successfully!', - displayName: 'Display Name', + username: 'Username', email: 'Email Address', password: 'Password', passwordinfodescription: 'Configure an application URL and enable email notifications to allow automatic password generation.', autogeneratepassword: 'Automatically Generate Password', autogeneratepasswordTip: 'Email a server-generated password to the user', - validationEmail: 'You must provide a valid email address', + validationUsername: 'You must provide an username', + validationEmail: 'Email required', sortCreated: 'Join Date', sortDisplayName: 'Display Name', sortRequests: 'Request Count', @@ -208,9 +209,10 @@ const UserList = () => { } const CreateUserSchema = Yup.object().shape({ - email: Yup.string() - .required(intl.formatMessage(messages.validationEmail)) - .email(intl.formatMessage(messages.validationEmail)), + username: Yup.string().required( + intl.formatMessage(messages.validationUsername) + ), + email: Yup.string().email(intl.formatMessage(messages.validationEmail)), password: Yup.lazy((value) => !value ? Yup.string() @@ -258,7 +260,7 @@ const UserList = () => { setDeleteModal({ isOpen: false, user: deleteModal.user }) } title={intl.formatMessage(messages.deleteuser)} - subTitle={deleteModal.user?.displayName} + subTitle={deleteModal.user?.username} > {intl.formatMessage(messages.deleteconfirm)} @@ -276,7 +278,7 @@ const UserList = () => { > { 'Content-Type': 'application/json', }, body: JSON.stringify({ - username: values.displayName, + username: values.username, email: values.email, password: values.genpassword ? null : values.password, }), @@ -370,23 +372,24 @@ const UserList = () => { )}
-
@@ -645,9 +648,16 @@ const UserList = () => { className="text-base font-bold leading-5 transition duration-300 hover:underline" data-testid="user-list-username-link" > - {user.displayName} + {user.username || + user.jellyfinUsername || + user.plexUsername || + user.email} - {user.displayName.toLowerCase() !== user.email && ( + {( + user.username || + user.jellyfinUsername || + user.plexUsername + )?.toLowerCase() !== user.email && (
{user.email}
diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 7b100a0d..3bcf1a04 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -93,9 +93,14 @@ const UserGeneralSettings = () => { ); const UserGeneralSettingsSchema = Yup.object().shape({ - email: Yup.string() - .email(intl.formatMessage(messages.validationemailformat)) - .required(intl.formatMessage(messages.validationemailrequired)), + email: + user?.id === 1 + ? Yup.string() + .email(intl.formatMessage(messages.validationemailformat)) + .required(intl.formatMessage(messages.validationemailrequired)) + : Yup.string().email( + intl.formatMessage(messages.validationemailformat) + ), discordId: Yup.string() .nullable() .matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)), @@ -134,7 +139,7 @@ const UserGeneralSettings = () => { { }, body: JSON.stringify({ username: values.displayName, - email: values.email, + email: + values.email || user?.jellyfinUsername || user?.plexUsername, discordId: values.discordId, locale: values.locale, region: values.region, @@ -264,7 +270,9 @@ const UserGeneralSettings = () => { name="displayName" type="text" placeholder={ - user?.plexUsername ? user.plexUsername : user?.email + user?.username || + user?.jellyfinUsername || + user?.plexUsername } />
@@ -289,6 +297,7 @@ const UserGeneralSettings = () => { name="email" type="text" placeholder="example@domain.com" + disabled={user?.plexUsername} className={ user?.warnings.find((w) => w === 'userEmailRequired') ? 'border-2 border-red-400 focus:border-blue-600' diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index fa034437..f1e83006 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -12,6 +12,7 @@ export interface User { id: number; warnings: string[]; plexUsername?: string; + jellyfinUsername?: string; username?: string; displayName: string; email: string; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 390a7e1b..3dc25214 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1111,7 +1111,6 @@ "components.UserList.creating": "Creating…", "components.UserList.deleteconfirm": "Are you sure you want to delete this user? All of their request data will be permanently removed.", "components.UserList.deleteuser": "Delete User", - "components.UserList.displayName": "Display Name", "components.UserList.edituser": "Edit User Permissions", "components.UserList.email": "Email Address", "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} imported successfully!", @@ -1145,9 +1144,11 @@ "components.UserList.userdeleteerror": "Something went wrong while deleting the user.", "components.UserList.userfail": "Something went wrong while saving user permissions.", "components.UserList.userlist": "User List", + "components.UserList.username": "Username", "components.UserList.users": "Users", "components.UserList.userssaved": "User permissions saved successfully!", - "components.UserList.validationEmail": "You must provide a valid email address", + "components.UserList.validationEmail": "Email required", + "components.UserList.validationUsername": "You must provide an username", "components.UserList.validationpasswordminchars": "Password is too short; should be a minimum of 8 characters", "components.UserProfile.ProfileHeader.joindate": "Joined {joindate}", "components.UserProfile.ProfileHeader.profile": "View Profile", From 36d98a2681921a8770027b78878688f2782e8b77 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 30 Jul 2024 00:43:40 +0200 Subject: [PATCH 12/53] fix: add missing parameter to delete requests from ExternalAPI (#904) fix #903 --- server/api/externalapi.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 10355800..2788db1e 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -178,6 +178,7 @@ class ExternalAPI { ): Promise { const url = this.formatUrl(endpoint, params); const response = await this.fetch(url, { + method: 'DELETE', ...config, headers: { ...this.defaultHeaders, @@ -313,7 +314,11 @@ class ExternalAPI { try { return await response.json(); } catch { - return await response.blob(); + try { + return await response.blob(); + } catch { + return null; + } } } } From 64453320d36595e75dcb710dfd43997bf2d2acd5 Mon Sep 17 00:00:00 2001 From: Oliver Laing <37535998+myselfolli@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:59:45 +0200 Subject: [PATCH 13/53] feat: show quality profile on request (#847) * feat: backend fetch and return quality profile * feat: show request profile name * fix: wrong backend types * feat: i18n keys * fix: don't display quality profile if not set * fix: remove development artifact * fix: reduce parent div padding --- server/interfaces/api/common.ts | 13 ++++ server/interfaces/api/requestInterfaces.ts | 5 +- server/routes/request.ts | 61 ++++++++++++++++++- src/components/RequestCard/index.tsx | 28 +++++---- .../RequestList/RequestItem/index.tsx | 45 ++++++++------ .../RequestModal/MovieRequestModal.tsx | 3 +- .../RequestModal/TvRequestModal.tsx | 3 +- src/components/RequestModal/index.tsx | 3 +- src/i18n/locale/en.json | 1 + 9 files changed, 127 insertions(+), 35 deletions(-) diff --git a/server/interfaces/api/common.ts b/server/interfaces/api/common.ts index 9ae25939..d9e9490b 100644 --- a/server/interfaces/api/common.ts +++ b/server/interfaces/api/common.ts @@ -8,3 +8,16 @@ interface PageInfo { export interface PaginatedResponse { pageInfo: PageInfo; } + +/** + * Get the keys of an object that are not functions + */ +type NonFunctionPropertyNames = { + // eslint-disable-next-line @typescript-eslint/ban-types + [K in keyof T]: T[K] extends Function ? never : K; +}[keyof T]; + +/** + * Get the properties of an object that are not functions + */ +export type NonFunctionProperties = Pick>; diff --git a/server/interfaces/api/requestInterfaces.ts b/server/interfaces/api/requestInterfaces.ts index 89863cb0..88b1201d 100644 --- a/server/interfaces/api/requestInterfaces.ts +++ b/server/interfaces/api/requestInterfaces.ts @@ -1,9 +1,9 @@ import type { MediaType } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; -import type { PaginatedResponse } from './common'; +import type { NonFunctionProperties, PaginatedResponse } from './common'; export interface RequestResultsResponse extends PaginatedResponse { - results: MediaRequest[]; + results: NonFunctionProperties[]; } export type MediaRequestBody = { @@ -14,6 +14,7 @@ export type MediaRequestBody = { is4k?: boolean; serverId?: number; profileId?: number; + profileName?: string; rootFolder?: string; languageProfileId?: number; userId?: number; diff --git a/server/routes/request.ts b/server/routes/request.ts index 83c05b48..94ae8384 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -1,3 +1,5 @@ +import RadarrAPI from '@server/api/servarr/radarr'; +import SonarrAPI from '@server/api/servarr/sonarr'; import { MediaRequestStatus, MediaStatus, @@ -19,6 +21,7 @@ import type { RequestResultsResponse, } from '@server/interfaces/api/requestInterfaces'; import { Permission } from '@server/lib/permissions'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { isAuthenticated } from '@server/middleware/auth'; import { Router } from 'express'; @@ -143,6 +146,62 @@ requestRoutes.get, RequestResultsResponse>( .skip(skip) .getManyAndCount(); + const settings = getSettings(); + + // get all quality profiles for every configured sonarr server + const sonarrServers = await Promise.all( + settings.sonarr.map(async (sonarrSetting) => { + const sonarr = new SonarrAPI({ + apiKey: sonarrSetting.apiKey, + url: SonarrAPI.buildUrl(sonarrSetting, '/api/v3'), + }); + + return { + id: sonarrSetting.id, + profiles: await sonarr.getProfiles(), + }; + }) + ); + + // get all quality profiles for every configured radarr server + const radarrServers = await Promise.all( + settings.radarr.map(async (radarrSetting) => { + const radarr = new RadarrAPI({ + apiKey: radarrSetting.apiKey, + url: RadarrAPI.buildUrl(radarrSetting, '/api/v3'), + }); + + return { + id: radarrSetting.id, + profiles: await radarr.getProfiles(), + }; + }) + ); + + // add profile names to the media requests, with undefined if not found + const requestsWithProfileNames = requests.map((r) => { + switch (r.type) { + case MediaType.MOVIE: { + const profileName = radarrServers + .find((serverr) => serverr.id === r.serverId) + ?.profiles.find((profile) => profile.id === r.profileId)?.name; + + return { + ...r, + profileName, + }; + } + case MediaType.TV: { + return { + ...r, + profileName: sonarrServers + .find((serverr) => serverr.id === r.serverId) + ?.profiles.find((profile) => profile.id === r.profileId)?.name, + }; + } + } + }); + return res.status(200).json({ pageInfo: { pages: Math.ceil(requestCount / pageSize), @@ -150,7 +209,7 @@ requestRoutes.get, RequestResultsResponse>( results: requestCount, page: Math.ceil(skip / pageSize) + 1, }, - results: requests, + results: requestsWithProfileNames, }); } catch (e) { next({ status: 500, message: e.message }); diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index 8b75f743..eb78806f 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -19,6 +19,7 @@ import { } from '@heroicons/react/24/solid'; import { MediaRequestStatus } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; +import type { NonFunctionProperties } from '@server/interfaces/api/common'; import type { MovieDetails } from '@server/models/Movie'; import type { TvDetails } from '@server/models/Tv'; import Image from 'next/image'; @@ -58,7 +59,7 @@ const RequestCardPlaceholder = () => { }; interface RequestCardErrorProps { - requestData?: MediaRequest; + requestData?: NonFunctionProperties; } const RequestCardError = ({ requestData }: RequestCardErrorProps) => { @@ -213,7 +214,7 @@ const RequestCardError = ({ requestData }: RequestCardErrorProps) => { }; interface RequestCardProps { - request: MediaRequest; + request: NonFunctionProperties; onTitleData?: (requestId: number, title: MovieDetails | TvDetails) => void; } @@ -238,16 +239,19 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { data: requestData, error: requestError, mutate: revalidate, - } = useSWR(`/api/v1/request/${request.id}`, { - fallbackData: request, - refreshInterval: refreshIntervalHelper( - { - downloadStatus: request.media.downloadStatus, - downloadStatus4k: request.media.downloadStatus4k, - }, - 15000 - ), - }); + } = useSWR>( + `/api/v1/request/${request.id}`, + { + fallbackData: request, + refreshInterval: refreshIntervalHelper( + { + downloadStatus: request.media.downloadStatus, + downloadStatus4k: request.media.downloadStatus4k, + }, + 15000 + ), + } + ); const { mediaUrl: plexUrl, mediaUrl4k: plexUrl4k } = useDeepLinks({ mediaUrl: requestData?.media?.mediaUrl, diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 4694bec0..5d687b51 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -18,6 +18,7 @@ import { } from '@heroicons/react/24/solid'; import { MediaRequestStatus } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; +import type { NonFunctionProperties } from '@server/interfaces/api/common'; import type { MovieDetails } from '@server/models/Movie'; import type { TvDetails } from '@server/models/Tv'; import Image from 'next/image'; @@ -42,6 +43,7 @@ const messages = defineMessages('components.RequestList.RequestItem', { tmdbid: 'TMDB ID', tvdbid: 'TheTVDB ID', unknowntitle: 'Unknown Title', + profileName: 'Profile', }); const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { @@ -49,7 +51,7 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { }; interface RequestItemErrorProps { - requestData?: MediaRequest; + requestData?: NonFunctionProperties; revalidateList: () => void; } @@ -285,7 +287,7 @@ const RequestItemError = ({ }; interface RequestItemProps { - request: MediaRequest; + request: NonFunctionProperties & { profileName?: string }; revalidateList: () => void; } @@ -304,19 +306,18 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { const { data: title, error } = useSWR( inView ? url : null ); - const { data: requestData, mutate: revalidate } = useSWR( - `/api/v1/request/${request.id}`, - { - fallbackData: request, - refreshInterval: refreshIntervalHelper( - { - downloadStatus: request.media.downloadStatus, - downloadStatus4k: request.media.downloadStatus4k, - }, - 15000 - ), - } - ); + const { data: requestData, mutate: revalidate } = useSWR< + NonFunctionProperties + >(`/api/v1/request/${request.id}`, { + fallbackData: request, + refreshInterval: refreshIntervalHelper( + { + downloadStatus: request.media.downloadStatus, + downloadStatus4k: request.media.downloadStatus4k, + }, + 15000 + ), + }); const [isRetrying, setRetrying] = useState(false); @@ -401,7 +402,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { setShowEditModal(false); }} /> -
+
{title.backdropPath && (
{ )}
-
+
{intl.formatMessage(globalMessages.status)} @@ -632,6 +633,16 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
)} + {request.profileName && ( +
+ + {intl.formatMessage(messages.profileName)} + + + {request.profileName} + +
+ )}
diff --git a/src/components/RequestModal/MovieRequestModal.tsx b/src/components/RequestModal/MovieRequestModal.tsx index 3a00e30a..85af7aef 100644 --- a/src/components/RequestModal/MovieRequestModal.tsx +++ b/src/components/RequestModal/MovieRequestModal.tsx @@ -8,6 +8,7 @@ import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { MediaStatus } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; +import type { NonFunctionProperties } from '@server/interfaces/api/common'; import type { QuotaResponse } from '@server/interfaces/api/userInterfaces'; import { Permission } from '@server/lib/permissions'; import type { MovieDetails } from '@server/models/Movie'; @@ -38,7 +39,7 @@ const messages = defineMessages('components.RequestModal', { interface RequestModalProps extends React.HTMLAttributes { tmdbId: number; is4k?: boolean; - editRequest?: MediaRequest; + editRequest?: NonFunctionProperties; onCancel?: () => void; onComplete?: (newStatus: MediaStatus) => void; onUpdating?: (isUpdating: boolean) => void; diff --git a/src/components/RequestModal/TvRequestModal.tsx b/src/components/RequestModal/TvRequestModal.tsx index 4e847294..71750678 100644 --- a/src/components/RequestModal/TvRequestModal.tsx +++ b/src/components/RequestModal/TvRequestModal.tsx @@ -13,6 +13,7 @@ import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants'; import { MediaRequestStatus, MediaStatus } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; import type SeasonRequest from '@server/entity/SeasonRequest'; +import type { NonFunctionProperties } from '@server/interfaces/api/common'; import type { QuotaResponse } from '@server/interfaces/api/userInterfaces'; import { Permission } from '@server/lib/permissions'; import type { TvDetails } from '@server/models/Tv'; @@ -57,7 +58,7 @@ interface RequestModalProps extends React.HTMLAttributes { onComplete?: (newStatus: MediaStatus) => void; onUpdating?: (isUpdating: boolean) => void; is4k?: boolean; - editRequest?: MediaRequest; + editRequest?: NonFunctionProperties; } const TvRequestModal = ({ diff --git a/src/components/RequestModal/index.tsx b/src/components/RequestModal/index.tsx index 9ef6b405..19874179 100644 --- a/src/components/RequestModal/index.tsx +++ b/src/components/RequestModal/index.tsx @@ -4,13 +4,14 @@ import TvRequestModal from '@app/components/RequestModal/TvRequestModal'; import { Transition } from '@headlessui/react'; import type { MediaStatus } from '@server/constants/media'; import type { MediaRequest } from '@server/entity/MediaRequest'; +import type { NonFunctionProperties } from '@server/interfaces/api/common'; interface RequestModalProps { show: boolean; type: 'movie' | 'tv' | 'collection'; tmdbId: number; is4k?: boolean; - editRequest?: MediaRequest; + editRequest?: NonFunctionProperties; onComplete?: (newStatus: MediaStatus) => void; onCancel?: () => void; onUpdating?: (isUpdating: boolean) => void; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 3dc25214..a7bf780c 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -465,6 +465,7 @@ "components.RequestList.RequestItem.mediaerror": "{mediaType} Not Found", "components.RequestList.RequestItem.modified": "Modified", "components.RequestList.RequestItem.modifieduserdate": "{date} by {user}", + "components.RequestList.RequestItem.profileName": "Profile", "components.RequestList.RequestItem.requested": "Requested", "components.RequestList.RequestItem.requesteddate": "Requested", "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", From b43c1e350e5cfc4ac0f9b957d409589d4ee0df57 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:46:08 +0500 Subject: [PATCH 14/53] chore(codeowners): add gauthier-th as a codeowner --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b2f0d8b7..280424a4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Global code ownership -* @Fallenbagel +* @Fallenbagel @gauthier-th From 32c77f9e9408c6c1641ce375648c762eac7ebafa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:05:26 +0500 Subject: [PATCH 15/53] docs: add mobihen as a contributor for translation (#913) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8b0debad..3912b934 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -403,6 +403,15 @@ "contributions": [ "doc" ] + }, + { + "login": "mobihen", + "name": "Nir Israel Hen", + "avatar_url": "https://avatars.githubusercontent.com/u/35529491?v=4", + "profile": "https://mobihen.com", + "contributions": [ + "translation" + ] } ] } diff --git a/README.md b/README.md index ed35c97b..69569573 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Translation status GitHub -All Contributors +All Contributors **Jellyseerr** is a free and open source software application for managing requests for your media library. @@ -137,6 +137,12 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon Joaquin Olivero
Joaquin Olivero

💻 Julian Behr
Julian Behr

🌍 ThowZzy
ThowZzy

💻 + Joseph Risk
Joseph Risk

💻 + Loetwiek
Loetwiek

💻 + + + Fuochi
Fuochi

📖 + Nir Israel Hen
Nir Israel Hen

🌍 From 181cb190487d991e418e6ff6136f576652146c64 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:06:58 +0500 Subject: [PATCH 16/53] docs: add XDark187 as a contributor for code (#915) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 3912b934..ded72bbc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -412,6 +412,15 @@ "contributions": [ "translation" ] + }, + { + "login": "XDark187", + "name": "Baraa", + "avatar_url": "https://avatars.githubusercontent.com/u/39034192?v=4", + "profile": "https://github.com/XDark187", + "contributions": [ + "code" + ] } ] } diff --git a/README.md b/README.md index 69569573..5200d9e0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Translation status GitHub -All Contributors +All Contributors **Jellyseerr** is a free and open source software application for managing requests for your media library. @@ -143,6 +143,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon Fuochi
Fuochi

📖 Nir Israel Hen
Nir Israel Hen

🌍 + Baraa
Baraa

💻 From f7be4789a23116bc1e23468473861c5261c57bf3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:07:29 +0500 Subject: [PATCH 17/53] docs: add franciscofsales as a contributor for code (#916) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index ded72bbc..56ed44d5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -421,6 +421,15 @@ "contributions": [ "code" ] + }, + { + "login": "franciscofsales", + "name": "Francisco Sales", + "avatar_url": "https://avatars.githubusercontent.com/u/7977645?v=4", + "profile": "https://github.com/franciscofsales", + "contributions": [ + "code" + ] } ] } diff --git a/README.md b/README.md index 5200d9e0..0c6713fb 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Translation status GitHub -All Contributors +All Contributors **Jellyseerr** is a free and open source software application for managing requests for your media library. @@ -144,6 +144,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon Fuochi
Fuochi

📖 Nir Israel Hen
Nir Israel Hen

🌍 Baraa
Baraa

💻 + Francisco Sales
Francisco Sales

💻 From 29a32d039189a7f0932f8fb70fc97269d69624e4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:08:21 +0500 Subject: [PATCH 18/53] docs: add myselfolli as a contributor for code (#917) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 56ed44d5..91017932 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -430,6 +430,15 @@ "contributions": [ "code" ] + }, + { + "login": "myselfolli", + "name": "Oliver Laing", + "avatar_url": "https://avatars.githubusercontent.com/u/37535998?v=4", + "profile": "https://github.com/myselfolli", + "contributions": [ + "code" + ] } ] } diff --git a/README.md b/README.md index 0c6713fb..4f0fa356 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Translation status GitHub -All Contributors +All Contributors **Jellyseerr** is a free and open source software application for managing requests for your media library. @@ -145,6 +145,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon Nir Israel Hen
Nir Israel Hen

🌍 Baraa
Baraa

💻 Francisco Sales
Francisco Sales

💻 + Oliver Laing
Oliver Laing

💻 From 71acfb1b1ff2e6a933958daec29f411d2c4f6e2d Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:13:22 +0500 Subject: [PATCH 19/53] docs(windows): add missing win-node-env dependency in the installation steps (#912) --- docs/getting-started/buildfromsource.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting-started/buildfromsource.mdx b/docs/getting-started/buildfromsource.mdx index e8148010..44e1f5ac 100644 --- a/docs/getting-started/buildfromsource.mdx +++ b/docs/getting-started/buildfromsource.mdx @@ -248,6 +248,7 @@ git checkout main ``` 3. Install the dependencies: ```powershell +npm install -g win-node-env set CYPRESS_INSTALL_BINARY=0 && yarn install --frozen-lockfile --network-timeout 1000000 ``` 4. Build the project: From a2c2d261fc54d766c6e1ec13523432748cb13075 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:19:06 +0500 Subject: [PATCH 20/53] docs(windows): add `win-node-env` instructions to develop build from source (#918) --- docs/getting-started/buildfromsource.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting-started/buildfromsource.mdx b/docs/getting-started/buildfromsource.mdx index 44e1f5ac..5b39912c 100644 --- a/docs/getting-started/buildfromsource.mdx +++ b/docs/getting-started/buildfromsource.mdx @@ -273,6 +273,7 @@ git checkout develop # by default, you are on the develop branch so this step is ``` 3. Install the dependencies: ```powershell +npm install -g win-node-env set CYPRESS_INSTALL_BINARY=0 && pnpm install --frozen-lockfile ``` 4. Build the project: From 74a2d25f153b07a0cae5b44adca5fa1fed5a3b9e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 5 Aug 2024 14:30:27 +0200 Subject: [PATCH 21/53] fix(api): handle non-existent ratings on IMDb (#822) --- server/api/rating/imdbRadarrProxy.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/api/rating/imdbRadarrProxy.ts b/server/api/rating/imdbRadarrProxy.ts index a4755474..f7b101cd 100644 --- a/server/api/rating/imdbRadarrProxy.ts +++ b/server/api/rating/imdbRadarrProxy.ts @@ -175,7 +175,11 @@ class IMDBRadarrProxy extends ExternalAPI { `/movie/imdb/${IMDBid}` ); - if (!data?.length || data[0].ImdbId !== IMDBid) { + if ( + !data?.length || + data[0].ImdbId !== IMDBid || + !data[0].MovieRatings.Imdb + ) { return null; } From 2348f23f433195d64dee3e6eeede296fca5fdbc9 Mon Sep 17 00:00:00 2001 From: Joaquin Olivero <66050823+JoaquinOlivero@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:46:57 -0300 Subject: [PATCH 22/53] feat: Option on item's page to add/remove from watchlist (#781) * feat: adds button on the page of a media item to add or remove it from a user's watchlist re #730 * fix: whitespace and i18n key * style: fix code format to the required standards * refactor: change axios for the fetch api --------- Co-authored-by: JoaquinOlivero --- server/models/Movie.ts | 5 +- server/models/Tv.ts | 5 +- server/routes/movie.ts | 15 ++- server/routes/tv.ts | 13 ++- src/components/MovieDetails/index.tsx | 126 +++++++++++++++++++++++- src/components/TvDetails/index.tsx | 135 +++++++++++++++++++++++++- src/i18n/locale/en.json | 10 ++ 7 files changed, 302 insertions(+), 7 deletions(-) diff --git a/server/models/Movie.ts b/server/models/Movie.ts index 0b627859..87ea7936 100644 --- a/server/models/Movie.ts +++ b/server/models/Movie.ts @@ -85,6 +85,7 @@ export interface MovieDetails { mediaUrl?: string; watchProviders?: WatchProviders[]; keywords: Keyword[]; + onUserWatchlist?: boolean; } export const mapProductionCompany = ( @@ -101,7 +102,8 @@ export const mapProductionCompany = ( export const mapMovieDetails = ( movie: TmdbMovieDetails, - media?: Media + media?: Media, + userWatchlist?: boolean ): MovieDetails => ({ id: movie.id, adult: movie.adult, @@ -148,4 +150,5 @@ export const mapMovieDetails = ( id: keyword.id, name: keyword.name, })), + onUserWatchlist: userWatchlist, }); diff --git a/server/models/Tv.ts b/server/models/Tv.ts index 24362b50..c79f9311 100644 --- a/server/models/Tv.ts +++ b/server/models/Tv.ts @@ -111,6 +111,7 @@ export interface TvDetails { keywords: Keyword[]; mediaInfo?: Media; watchProviders?: WatchProviders[]; + onUserWatchlist?: boolean; } const mapEpisodeResult = (episode: TmdbTvEpisodeResult): Episode => ({ @@ -161,7 +162,8 @@ export const mapNetwork = (network: TmdbNetwork): TvNetwork => ({ export const mapTvDetails = ( show: TmdbTvDetails, - media?: Media + media?: Media, + userWatchlist?: boolean ): TvDetails => ({ createdBy: show.created_by, episodeRunTime: show.episode_run_time, @@ -223,4 +225,5 @@ export const mapTvDetails = ( })), mediaInfo: media, watchProviders: mapWatchProviders(show['watch/providers']?.results ?? {}), + onUserWatchlist: userWatchlist, }); diff --git a/server/routes/movie.ts b/server/routes/movie.ts index b48ae9ea..833e9255 100644 --- a/server/routes/movie.ts +++ b/server/routes/movie.ts @@ -3,7 +3,9 @@ import RottenTomatoes from '@server/api/rating/rottentomatoes'; import { type RatingResponse } from '@server/api/ratings'; import TheMovieDb from '@server/api/themoviedb'; import { MediaType } from '@server/constants/media'; +import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; +import { Watchlist } from '@server/entity/Watchlist'; import logger from '@server/logger'; import { mapMovieDetails } from '@server/models/Movie'; import { mapMovieResult } from '@server/models/Search'; @@ -22,7 +24,18 @@ movieRoutes.get('/:id', async (req, res, next) => { const media = await Media.getMedia(tmdbMovie.id, MediaType.MOVIE); - return res.status(200).json(mapMovieDetails(tmdbMovie, media)); + const onUserWatchlist = await getRepository(Watchlist).exist({ + where: { + tmdbId: Number(req.params.id), + requestedBy: { + id: req.user?.id, + }, + }, + }); + + return res + .status(200) + .json(mapMovieDetails(tmdbMovie, media, onUserWatchlist)); } catch (e) { logger.debug('Something went wrong retrieving movie', { label: 'API', diff --git a/server/routes/tv.ts b/server/routes/tv.ts index cd69c13a..2f42c0dc 100644 --- a/server/routes/tv.ts +++ b/server/routes/tv.ts @@ -1,7 +1,9 @@ import RottenTomatoes from '@server/api/rating/rottentomatoes'; import TheMovieDb from '@server/api/themoviedb'; import { MediaType } from '@server/constants/media'; +import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; +import { Watchlist } from '@server/entity/Watchlist'; import logger from '@server/logger'; import { mapTvResult } from '@server/models/Search'; import { mapSeasonWithEpisodes, mapTvDetails } from '@server/models/Tv'; @@ -19,7 +21,16 @@ tvRoutes.get('/:id', async (req, res, next) => { const media = await Media.getMedia(tv.id, MediaType.TV); - return res.status(200).json(mapTvDetails(tv, media)); + const onUserWatchlist = await getRepository(Watchlist).exist({ + where: { + tmdbId: Number(req.params.id), + requestedBy: { + id: req.user?.id, + }, + }, + }); + + return res.status(200).json(mapTvDetails(tv, media, onUserWatchlist)); } catch (e) { logger.debug('Something went wrong retrieving series', { label: 'API', diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index 1c53ac0b..b565fdb1 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -3,6 +3,7 @@ import RTAudRotten from '@app/assets/rt_aud_rotten.svg'; import RTFresh from '@app/assets/rt_fresh.svg'; import RTRotten from '@app/assets/rt_rotten.svg'; import ImdbLogo from '@app/assets/services/imdb.svg'; +import Spinner from '@app/assets/spinner.svg'; import TmdbLogo from '@app/assets/tmdb_logo.svg'; import Button from '@app/components/Common/Button'; import CachedImage from '@app/components/Common/CachedImage'; @@ -41,12 +42,16 @@ import { import { ChevronDoubleDownIcon, ChevronDoubleUpIcon, + MinusCircleIcon, + StarIcon, } from '@heroicons/react/24/solid'; import { type RatingResponse } from '@server/api/ratings'; import { IssueStatus } from '@server/constants/issue'; -import { MediaStatus } from '@server/constants/media'; +import { MediaStatus, MediaType } from '@server/constants/media'; import { MediaServerType } from '@server/constants/server'; +import type { Watchlist } from '@server/entity/Watchlist'; import type { MovieDetails as MovieDetailsType } from '@server/models/Movie'; +import axios from 'axios'; import { countries } from 'country-flag-icons'; import 'country-flag-icons/3x2/flags.css'; import { uniqBy } from 'lodash'; @@ -55,6 +60,7 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; +import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; const messages = defineMessages('components.MovieDetails', { @@ -94,6 +100,12 @@ const messages = defineMessages('components.MovieDetails', { rtaudiencescore: 'Rotten Tomatoes Audience Score', tmdbuserscore: 'TMDB User Score', imdbuserscore: 'IMDB User Score', + watchlistSuccess: '{title} added to watchlist successfully!', + watchlistDeleted: + '{title} Removed from watchlist successfully!', + watchlistError: 'Something went wrong try again.', + removefromwatchlist: 'Remove From Watchlist', + addtowatchlist: 'Add To Watchlist', }); interface MovieDetailsProps { @@ -112,7 +124,12 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { const minStudios = 3; const [showMoreStudios, setShowMoreStudios] = useState(false); const [showIssueModal, setShowIssueModal] = useState(false); + const [isUpdating, setIsUpdating] = useState(false); + const [toggleWatchlist, setToggleWatchlist] = useState( + !movie?.onUserWatchlist + ); const { publicRuntimeConfig } = getConfig(); + const { addToast } = useToasts(); const { data, @@ -287,6 +304,79 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { return intl.formatMessage(messages.play4k, { mediaServerName: 'Jellyfin' }); } + const onClickWatchlistBtn = async (): Promise => { + setIsUpdating(true); + + const res = await fetch('/api/v1/watchlist', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + tmdbId: movie?.id, + mediaType: MediaType.MOVIE, + title: movie?.title, + }), + }); + + if (!res.ok) { + addToast(intl.formatMessage(messages.watchlistError), { + appearance: 'error', + autoDismiss: true, + }); + + setIsUpdating(false); + return; + } + + const data = await res.json(); + + if (data) { + addToast( + + {intl.formatMessage(messages.watchlistSuccess, { + title: movie?.title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + } + + setIsUpdating(false); + setToggleWatchlist((prevState) => !prevState); + }; + + const onClickDeleteWatchlistBtn = async (): Promise => { + setIsUpdating(true); + try { + const response = await axios.delete( + '/api/v1/watchlist/' + movie?.id + ); + + if (response.status === 204) { + addToast( + + {intl.formatMessage(messages.watchlistDeleted, { + title: movie?.title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'info', autoDismiss: true } + ); + } + } catch (e) { + addToast(intl.formatMessage(messages.watchlistError), { + appearance: 'error', + autoDismiss: true, + }); + } finally { + setIsUpdating(false); + setToggleWatchlist((prevState) => !prevState); + } + }; + return (
{
+ <> + {toggleWatchlist ? ( + + + + ) : ( + + + + )} + {title} added to watchlist successfully!', + watchlistDeleted: + '{title} Removed from watchlist successfully!', + watchlistError: 'Something went wrong try again.', + removefromwatchlist: 'Remove From Watchlist', + addtowatchlist: 'Add To Watchlist', }); interface TvDetailsProps { @@ -106,7 +122,12 @@ const TvDetails = ({ tv }: TvDetailsProps) => { router.query.manage == '1' ? true : false ); const [showIssueModal, setShowIssueModal] = useState(false); + const [isUpdating, setIsUpdating] = useState(false); + const [toggleWatchlist, setToggleWatchlist] = useState( + !tv?.onUserWatchlist + ); const { publicRuntimeConfig } = getConfig(); + const { addToast } = useToasts(); const { data, @@ -302,6 +323,82 @@ const TvDetails = ({ tv }: TvDetailsProps) => { return intl.formatMessage(messages.play4k, { mediaServerName: 'Jellyfin' }); } + const onClickWatchlistBtn = async (): Promise => { + setIsUpdating(true); + + const res = await fetch('/api/v1/watchlist', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + tmdbId: tv?.id, + mediaType: MediaType.TV, + title: tv?.name, + }), + }); + + if (!res.ok) { + addToast(intl.formatMessage(messages.watchlistError), { + appearance: 'error', + autoDismiss: true, + }); + + setIsUpdating(false); + return; + } + + const data = await res.json(); + + if (data) { + addToast( + + {intl.formatMessage(messages.watchlistSuccess, { + title: tv?.name, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + } + + setIsUpdating(false); + setToggleWatchlist((prevState) => !prevState); + }; + + const onClickDeleteWatchlistBtn = async (): Promise => { + setIsUpdating(true); + + const res = await fetch('/api/v1/watchlist/' + tv?.id, { + method: 'DELETE', + }); + + if (!res.ok) { + addToast(intl.formatMessage(messages.watchlistError), { + appearance: 'error', + autoDismiss: true, + }); + + setIsUpdating(false); + return; + } + + if (res.status === 204) { + addToast( + + {intl.formatMessage(messages.watchlistDeleted, { + title: tv?.name, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'info', autoDismiss: true } + ); + setIsUpdating(false); + setToggleWatchlist((prevState) => !prevState); + } + }; + return (
{
+ <> + {toggleWatchlist ? ( + + + + ) : ( + + + + )} + {title} Removed from watchlist successfully!", + "components.MovieDetails.watchlistError": "Something went wrong try again.", + "components.MovieDetails.watchlistSuccess": "{title} added to watchlist successfully!", "components.MovieDetails.watchtrailer": "Watch Trailer", "components.NotificationTypeSelector.adminissuecommentDescription": "Get notified when other users comment on issues.", "components.NotificationTypeSelector.adminissuereopenedDescription": "Get notified when issues are reopened by other users.", @@ -1071,6 +1076,7 @@ "components.TvDetails.Season.somethingwentwrong": "Something went wrong while retrieving season data.", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", "components.TvDetails.TvCrew.fullseriescrew": "Full Series Crew", + "components.TvDetails.addtowatchlist": "Add To Watchlist", "components.TvDetails.anime": "Anime", "components.TvDetails.cast": "Cast", "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Episode} other {# Episodes}}", @@ -1088,6 +1094,7 @@ "components.TvDetails.play4k": "Play 4K on {mediaServerName}", "components.TvDetails.productioncountries": "Production {countryCount, plural, one {Country} other {Countries}}", "components.TvDetails.recommendations": "Recommendations", + "components.TvDetails.removefromwatchlist": "Remove From Watchlist", "components.TvDetails.reportissue": "Report an Issue", "components.TvDetails.rtaudiencescore": "Rotten Tomatoes Audience Score", "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", @@ -1100,6 +1107,9 @@ "components.TvDetails.streamingproviders": "Currently Streaming On", "components.TvDetails.tmdbuserscore": "TMDB User Score", "components.TvDetails.viewfullcrew": "View Full Crew", + "components.TvDetails.watchlistDeleted": "{title} Removed from watchlist successfully!", + "components.TvDetails.watchlistError": "Something went wrong try again.", + "components.TvDetails.watchlistSuccess": "{title} added to watchlist successfully!", "components.TvDetails.watchtrailer": "Watch Trailer", "components.UserList.accounttype": "Type", "components.UserList.admin": "Admin", From 9aee8887d3cca6e018f4be1c8400c22e86bf8dab Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 7 Aug 2024 14:33:44 +0200 Subject: [PATCH 23/53] fix: rewrite request from axios to Fetch (#920) --- src/components/MovieDetails/index.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index b565fdb1..b18d506c 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -26,7 +26,7 @@ import useLocale from '@app/hooks/useLocale'; import useSettings from '@app/hooks/useSettings'; import { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; -import Error from '@app/pages/_error'; +import ErrorPage from '@app/pages/_error'; import { sortCrewPriority } from '@app/utils/creditHelpers'; import defineMessages from '@app/utils/defineMessages'; import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper'; @@ -49,9 +49,7 @@ import { type RatingResponse } from '@server/api/ratings'; import { IssueStatus } from '@server/constants/issue'; import { MediaStatus, MediaType } from '@server/constants/media'; import { MediaServerType } from '@server/constants/server'; -import type { Watchlist } from '@server/entity/Watchlist'; import type { MovieDetails as MovieDetailsType } from '@server/models/Movie'; -import axios from 'axios'; import { countries } from 'country-flag-icons'; import 'country-flag-icons/3x2/flags.css'; import { uniqBy } from 'lodash'; @@ -171,7 +169,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { } if (!data) { - return ; + return ; } const showAllStudios = data.productionCompanies.length <= minStudios + 1; @@ -351,11 +349,12 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { const onClickDeleteWatchlistBtn = async (): Promise => { setIsUpdating(true); try { - const response = await axios.delete( - '/api/v1/watchlist/' + movie?.id - ); + const res = await fetch(`/api/v1/watchlist/${movie?.id}`, { + method: 'DELETE', + }); + if (!res.ok) throw new Error(); - if (response.status === 204) { + if (res.status === 204) { addToast( {intl.formatMessage(messages.watchlistDeleted, { From 61dcd8e487d7886773ccb12501623c17838476e5 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sun, 11 Aug 2024 19:25:17 +0200 Subject: [PATCH 24/53] fix: update the filter removing existing users from Jellyfin import modal (#924) Currently import button sometimes shows already imported users and this would break it if an admin tries to import an already imported user. --- .../UserList/JellyfinImportModal.tsx | 115 +++++++++--------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx index ed549ac5..64ca1861 100644 --- a/src/components/UserList/JellyfinImportModal.tsx +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -56,14 +56,6 @@ const JellyfinImportModal: React.FC = ({ `/api/v1/user?take=${children}` ); - data?.forEach((user, pos) => { - if ( - existingUsers?.results.some((data) => data.jellyfinUserId === user.id) - ) { - data?.splice(pos, 1); - } - }); - const importUsers = async () => { setImporting(true); @@ -209,64 +201,71 @@ const JellyfinImportModal: React.FC = ({ - {data?.map((user) => ( - - - toggleUser(user.id)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === 'Space') { - toggleUser(user.id); - } - }} - className="relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none" - > + {data + ?.filter( + (user) => + !existingUsers?.results.some( + (u) => u.jellyfinUserId === user.id + ) + ) + .map((user) => ( + + - - - - -
- -
-
- {user.username} -
- {/* {user.username && + role="checkbox" + tabIndex={0} + aria-checked={isSelectedUser(user.id)} + onClick={() => toggleUser(user.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === 'Space') { + toggleUser(user.id); + } + }} + className="relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none" + > + + + + + +
+ +
+
+ {user.username} +
+ {/* {user.username && user.username.toLowerCase() !== user.email && (
{user.email}
)} */} +
-
- - - ))} + + + ))}
From 12f908de7f5fbd717a5f151858b6edee3be13ed9 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 13 Aug 2024 10:41:59 +0200 Subject: [PATCH 25/53] fix(tmdb): fallback movie/show overview to English when none is available in requested locale (#928) This PR adds a second call to TMDB to retried the overview in English if no overview is available in the requested locale fix #925 --- server/routes/movie.ts | 12 +++++++++--- server/routes/tv.ts | 10 +++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/routes/movie.ts b/server/routes/movie.ts index 833e9255..80b8a300 100644 --- a/server/routes/movie.ts +++ b/server/routes/movie.ts @@ -33,9 +33,15 @@ movieRoutes.get('/:id', async (req, res, next) => { }, }); - return res - .status(200) - .json(mapMovieDetails(tmdbMovie, media, onUserWatchlist)); + const data = mapMovieDetails(tmdbMovie, media, onUserWatchlist); + + // TMDB issue where it doesnt fallback to English when no overview is available in requested locale. + if (!data.overview) { + const tvEnglish = await tmdb.getMovie({ movieId: Number(req.params.id) }); + data.overview = tvEnglish.overview; + } + + return res.status(200).json(data); } catch (e) { logger.debug('Something went wrong retrieving movie', { label: 'API', diff --git a/server/routes/tv.ts b/server/routes/tv.ts index 2f42c0dc..4a106d60 100644 --- a/server/routes/tv.ts +++ b/server/routes/tv.ts @@ -30,7 +30,15 @@ tvRoutes.get('/:id', async (req, res, next) => { }, }); - return res.status(200).json(mapTvDetails(tv, media, onUserWatchlist)); + const data = mapTvDetails(tv, media, onUserWatchlist); + + // TMDB issue where it doesnt fallback to English when no overview is available in requested locale. + if (!data.overview) { + const tvEnglish = await tmdb.getTvShow({ tvId: Number(req.params.id) }); + data.overview = tvEnglish.overview; + } + + return res.status(200).json(data); } catch (e) { logger.debug('Something went wrong retrieving series', { label: 'API', From bd4da6d5fc8cb55c2bc3d9a8336787cbd30814d0 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 13 Aug 2024 16:01:45 +0200 Subject: [PATCH 26/53] feat(jellyfinapi): switch to API tokens instead of auth tokens (#868) * feat(jellyfinapi): create Jellyfin API key from admin user * fix(jellyfinapi): add migration script for Jellyfin API key * feat(jellyfinapi): use Jellyfin API key instead of admin auth token * fix(jellyfinapi): fix api key migration * feat(jellyfinapi): add API key field to Jellyfin settings * fix: move the API key field in the Jellyfin settings --- server/api/externalapi.ts | 11 +- server/api/jellyfin.ts | 22 +- server/index.ts | 2 +- server/lib/availabilitySync.ts | 9 +- server/lib/scanners/jellyfin/index.ts | 9 +- server/lib/settings/index.ts | 12 +- .../migrations/0002_migrate_apitokens.ts | 36 ++ server/lib/settings/migrator.ts | 16 +- server/routes/auth.ts | 15 +- server/routes/settings/index.ts | 15 +- server/routes/user/index.ts | 14 +- src/components/Settings/SettingsJellyfin.tsx | 381 ++++++++++-------- src/i18n/locale/en.json | 2 +- 13 files changed, 309 insertions(+), 235 deletions(-) create mode 100644 server/lib/settings/migrations/0002_migrate_apitokens.ts diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 2788db1e..4f0ded02 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -85,7 +85,7 @@ class ExternalAPI { protected async post( endpoint: string, - data: Record, + data?: Record, params?: Record, ttl?: number, config?: RequestInit @@ -107,7 +107,7 @@ class ExternalAPI { ...this.defaultHeaders, ...config?.headers, }, - body: JSON.stringify(data), + body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { const text = await response.text(); @@ -286,7 +286,12 @@ class ExternalAPI { ...this.params, ...params, }); - return `${href}?${searchParams.toString()}`; + return ( + href + + (searchParams.toString().length + ? '?' + searchParams.toString() + : searchParams.toString()) + ); } private serializeCacheKey( diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts index ff5144ce..f6550347 100644 --- a/server/api/jellyfin.ts +++ b/server/api/jellyfin.ts @@ -93,9 +93,7 @@ export interface JellyfinLibraryItemExtended extends JellyfinLibraryItem { } class JellyfinAPI extends ExternalAPI { - private authToken?: string; private userId?: string; - private jellyfinHost: string; constructor(jellyfinHost: string, authToken?: string, deviceId?: string) { let authHeaderVal: string; @@ -114,9 +112,6 @@ class JellyfinAPI extends ExternalAPI { }, } ); - - this.jellyfinHost = jellyfinHost; - this.authToken = authToken; } public async login( @@ -405,6 +400,23 @@ class JellyfinAPI extends ExternalAPI { throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken); } } + + public async createApiToken(appName: string): Promise { + try { + await this.post(`/Auth/Keys?App=${appName}`); + const apiKeys = await this.get(`/Auth/Keys`); + return apiKeys.Items.reverse().find( + (item: any) => item.AppName === appName + ).AccessToken; + } catch (e) { + logger.error( + `Something went wrong while creating an API key the Jellyfin server: ${e.message}`, + { label: 'Jellyfin API' } + ); + + throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + } + } } export default JellyfinAPI; diff --git a/server/index.ts b/server/index.ts index 2d90f05c..ef20674d 100644 --- a/server/index.ts +++ b/server/index.ts @@ -63,7 +63,7 @@ app } // Load Settings - const settings = getSettings(); + const settings = await getSettings().load(); restartFlag.initializeSettings(settings.main); // Migrate library types diff --git a/server/lib/availabilitySync.ts b/server/lib/availabilitySync.ts index 4ecd9107..b85d29e4 100644 --- a/server/lib/availabilitySync.ts +++ b/server/lib/availabilitySync.ts @@ -63,12 +63,7 @@ class AvailabilitySync { ) { admin = await userRepository.findOne({ where: { id: 1 }, - select: [ - 'id', - 'jellyfinAuthToken', - 'jellyfinUserId', - 'jellyfinDeviceId', - ], + select: ['id', 'jellyfinUserId', 'jellyfinDeviceId'], order: { id: 'ASC' }, }); } @@ -86,7 +81,7 @@ class AvailabilitySync { if (admin) { this.jellyfinClient = new JellyfinAPI( getHostname(), - admin.jellyfinAuthToken, + settings.jellyfin.apiKey, admin.jellyfinDeviceId ); diff --git a/server/lib/scanners/jellyfin/index.ts b/server/lib/scanners/jellyfin/index.ts index baa8d963..4ccf5485 100644 --- a/server/lib/scanners/jellyfin/index.ts +++ b/server/lib/scanners/jellyfin/index.ts @@ -582,12 +582,7 @@ class JellyfinScanner { const userRepository = getRepository(User); const admin = await userRepository.findOne({ where: { id: 1 }, - select: [ - 'id', - 'jellyfinAuthToken', - 'jellyfinUserId', - 'jellyfinDeviceId', - ], + select: ['id', 'jellyfinUserId', 'jellyfinDeviceId'], order: { id: 'ASC' }, }); @@ -597,7 +592,7 @@ class JellyfinScanner { this.jfClient = new JellyfinAPI( getHostname(), - admin.jellyfinAuthToken, + settings.jellyfin.apiKey, admin.jellyfinDeviceId ); diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 7c117c11..8c55d6c3 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -47,6 +47,7 @@ export interface JellyfinSettings { jellyfinForgotPasswordUrl?: string; libraries: Library[]; serverId: string; + apiKey: string; } export interface TautulliSettings { hostname?: string; @@ -342,6 +343,7 @@ class Settings { jellyfinForgotPasswordUrl: '', libraries: [], serverId: '', + apiKey: '', }, tautulli: {}, radarr: [], @@ -629,7 +631,7 @@ class Settings { * @param overrideSettings If passed in, will override all existing settings with these * values */ - public load(overrideSettings?: AllSettings): Settings { + public async load(overrideSettings?: AllSettings): Promise { if (overrideSettings) { this.data = overrideSettings; return this; @@ -642,7 +644,7 @@ class Settings { if (data) { const parsedJson = JSON.parse(data); - this.data = runMigrations(parsedJson); + this.data = await runMigrations(parsedJson); this.data = merge(this.data, parsedJson); @@ -656,7 +658,6 @@ class Settings { } } -let loaded = false; let settings: Settings | undefined; export const getSettings = (initialSettings?: AllSettings): Settings => { @@ -664,11 +665,6 @@ export const getSettings = (initialSettings?: AllSettings): Settings => { settings = new Settings(initialSettings); } - if (!loaded) { - settings.load(); - loaded = true; - } - return settings; }; diff --git a/server/lib/settings/migrations/0002_migrate_apitokens.ts b/server/lib/settings/migrations/0002_migrate_apitokens.ts new file mode 100644 index 00000000..46340433 --- /dev/null +++ b/server/lib/settings/migrations/0002_migrate_apitokens.ts @@ -0,0 +1,36 @@ +import JellyfinAPI from '@server/api/jellyfin'; +import { MediaServerType } from '@server/constants/server'; +import { getRepository } from '@server/datasource'; +import { User } from '@server/entity/User'; +import type { AllSettings } from '@server/lib/settings'; +import { getHostname } from '@server/utils/getHostname'; + +const migrateApiTokens = async (settings: any): Promise => { + const mediaServerType = settings.main.mediaServerType; + if ( + !settings.jellyfin.apiKey && + (mediaServerType === MediaServerType.JELLYFIN || + mediaServerType === MediaServerType.EMBY) + ) { + const userRepository = getRepository(User); + const admin = await userRepository.findOne({ + where: { id: 1 }, + select: ['id', 'jellyfinAuthToken', 'jellyfinUserId', 'jellyfinDeviceId'], + order: { id: 'ASC' }, + }); + if (!admin) { + return settings; + } + const jellyfinClient = new JellyfinAPI( + getHostname(settings.jellyfin), + admin.jellyfinAuthToken, + admin.jellyfinDeviceId + ); + jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); + const apiKey = await jellyfinClient.createApiToken('Jellyseerr'); + settings.jellyfin.apiKey = apiKey; + } + return settings; +}; + +export default migrateApiTokens; diff --git a/server/lib/settings/migrator.ts b/server/lib/settings/migrator.ts index 9d709590..856016e1 100644 --- a/server/lib/settings/migrator.ts +++ b/server/lib/settings/migrator.ts @@ -1,10 +1,13 @@ import type { AllSettings } from '@server/lib/settings'; +import logger from '@server/logger'; import fs from 'fs'; import path from 'path'; const migrationsDir = path.join(__dirname, 'migrations'); -export const runMigrations = (settings: AllSettings): AllSettings => { +export const runMigrations = async ( + settings: AllSettings +): Promise => { const migrations = fs .readdirSync(migrationsDir) .filter((file) => file.endsWith('.js') || file.endsWith('.ts')) @@ -13,8 +16,15 @@ export const runMigrations = (settings: AllSettings): AllSettings => { let migrated = settings; - for (const migration of migrations) { - migrated = migration(migrated); + try { + for (const migration of migrations) { + migrated = await migration(migrated); + } + } catch (e) { + logger.error( + `Something went wrong while running settings migrations: ${e.message}`, + { label: 'Settings Migrator' } + ); } return migrated; diff --git a/server/routes/auth.ts b/server/routes/auth.ts index 966dc269..6f01135d 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -324,7 +324,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinUsername: account.User.Name, jellyfinUserId: account.User.Id, jellyfinDeviceId: deviceId, - jellyfinAuthToken: account.AccessToken, permissions: Permission.ADMIN, avatar: account.User.PrimaryImageTag ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` @@ -335,6 +334,14 @@ authRoutes.post('/jellyfin', async (req, res, next) => { userType: UserType.JELLYFIN, }); + // Create an API key on Jellyfin from this admin user + const jellyfinClient = new JellyfinAPI( + hostname, + account.AccessToken, + deviceId + ); + const apiKey = await jellyfinClient.createApiToken('Jellyseerr'); + const serverName = await jellyfinserver.getServerName(); settings.jellyfin.name = serverName; @@ -343,6 +350,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { settings.jellyfin.port = body.port ?? 8096; settings.jellyfin.urlBase = body.urlBase ?? ''; settings.jellyfin.useSsl = body.useSsl ?? false; + settings.jellyfin.apiKey = apiKey; settings.save(); startJobs(); @@ -366,10 +374,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinUsername: account.User.Name, } ); - // Let's check if their authtoken is up to date - if (user.jellyfinAuthToken !== account.AccessToken) { - user.jellyfinAuthToken = account.AccessToken; - } // Update the users avatar with their jellyfin profile pic (incase it changed) if (account.User.PrimaryImageTag) { user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; @@ -421,7 +425,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinUsername: account.User.Name, jellyfinUserId: account.User.Id, jellyfinDeviceId: deviceId, - jellyfinAuthToken: account.AccessToken, permissions: settings.main.defaultPermissions, avatar: account.User.PrimaryImageTag ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 9e1a6220..30898d2a 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -262,7 +262,7 @@ settingsRoutes.post('/jellyfin', async (req, res, next) => { try { const admin = await userRepository.findOneOrFail({ where: { id: 1 }, - select: ['id', 'jellyfinAuthToken', 'jellyfinUserId', 'jellyfinDeviceId'], + select: ['id', 'jellyfinUserId', 'jellyfinDeviceId'], order: { id: 'ASC' }, }); @@ -270,7 +270,7 @@ settingsRoutes.post('/jellyfin', async (req, res, next) => { const jellyfinClient = new JellyfinAPI( getHostname(tempJellyfinSettings), - admin.jellyfinAuthToken ?? '', + tempJellyfinSettings.apiKey, admin.jellyfinDeviceId ?? '' ); @@ -318,13 +318,13 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => { if (req.query.sync) { const userRepository = getRepository(User); const admin = await userRepository.findOneOrFail({ - select: ['id', 'jellyfinAuthToken', 'jellyfinDeviceId', 'jellyfinUserId'], + select: ['id', 'jellyfinDeviceId', 'jellyfinUserId'], where: { id: 1 }, order: { id: 'ASC' }, }); const jellyfinClient = new JellyfinAPI( getHostname(), - admin.jellyfinAuthToken ?? '', + settings.jellyfin.apiKey, admin.jellyfinDeviceId ?? '' ); @@ -376,7 +376,8 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => { }); settingsRoutes.get('/jellyfin/users', async (req, res) => { - const { externalHostname } = getSettings().jellyfin; + const settings = getSettings(); + const { externalHostname } = settings.jellyfin; const jellyfinHost = externalHostname && externalHostname.length > 0 ? externalHostname @@ -384,13 +385,13 @@ settingsRoutes.get('/jellyfin/users', async (req, res) => { const userRepository = getRepository(User); const admin = await userRepository.findOneOrFail({ - select: ['id', 'jellyfinAuthToken', 'jellyfinDeviceId', 'jellyfinUserId'], + select: ['id', 'jellyfinDeviceId', 'jellyfinUserId'], where: { id: 1 }, order: { id: 'ASC' }, }); const jellyfinClient = new JellyfinAPI( getHostname(), - admin.jellyfinAuthToken ?? '', + settings.jellyfin.apiKey, admin.jellyfinDeviceId ?? '' ); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index 016709c6..da9b649c 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -501,17 +501,14 @@ router.post( // taken from auth.ts const admin = await userRepository.findOneOrFail({ where: { id: 1 }, - select: [ - 'id', - 'jellyfinAuthToken', - 'jellyfinDeviceId', - 'jellyfinUserId', - ], + select: ['id', 'jellyfinDeviceId', 'jellyfinUserId'], order: { id: 'ASC' }, }); + + const hostname = getHostname(); const jellyfinClient = new JellyfinAPI( - getHostname(), - admin.jellyfinAuthToken ?? '', + hostname, + settings.jellyfin.apiKey, admin.jellyfinDeviceId ?? '' ); jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); @@ -519,7 +516,6 @@ router.post( //const jellyfinUsersResponse = await jellyfinClient.getUsers(); const createdUsers: User[] = []; const { externalHostname } = getSettings().jellyfin; - const hostname = getHostname(); const jellyfinHost = externalHostname && externalHostname.length > 0 diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index 91c44e12..a627f6d3 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -1,6 +1,7 @@ import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; import LoadingSpinner from '@app/components/Common/LoadingSpinner'; +import SensitiveInput from '@app/components/Common/SensitiveInput'; import LibraryItem from '@app/components/Settings/LibraryItem'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; @@ -30,13 +31,14 @@ const messages = defineMessages('components.Settings', { jellyfinSettingsSuccess: '{mediaServerName} settings saved successfully!', jellyfinSettings: '{mediaServerName} Settings', jellyfinSettingsDescription: - 'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.', + 'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page. You can also change the Jellyfin API key, which was automatically generated previously.', externalUrl: 'External URL', hostname: 'Hostname or IP Address', port: 'Port', enablessl: 'Use SSL', urlBase: 'URL Base', jellyfinForgotPasswordUrl: 'Forgot Password URL', + apiKey: 'API key', jellyfinSyncFailedNoLibrariesFound: 'No libraries were found', jellyfinSyncFailedAutomaticGroupedFolders: 'Custom authentication with Automatic Library Grouping not supported', @@ -444,119 +446,121 @@ const SettingsJellyfin: React.FC = ({
- {showAdvancedSettings && ( - <> -
-

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Jellyfin', - })} -

-

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Jellyfin', - })} -

-
- { - try { - const res = await fetch('/api/v1/settings/jellyfin', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - ip: values.hostname, - port: Number(values.port), - useSsl: values.useSsl, - urlBase: values.urlBase, - externalHostname: values.jellyfinExternalUrl, - jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl, - } as JellyfinSettings), - }); - if (!res.ok) throw new Error(res.statusText, { cause: res }); +
+

+ {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + ? intl.formatMessage(messages.jellyfinSettings, { + mediaServerName: 'Emby', + }) + : intl.formatMessage(messages.jellyfinSettings, { + mediaServerName: 'Jellyfin', + })} +

+

+ {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + ? intl.formatMessage(messages.jellyfinSettingsDescription, { + mediaServerName: 'Emby', + }) + : intl.formatMessage(messages.jellyfinSettingsDescription, { + mediaServerName: 'Jellyfin', + })} +

+
+ { + try { + const res = await fetch('/api/v1/settings/jellyfin', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ip: values.hostname, + port: Number(values.port), + useSsl: values.useSsl, + urlBase: values.urlBase, + externalHostname: values.jellyfinExternalUrl, + jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl, + apiKey: values.apiKey, + } as JellyfinSettings), + }); + if (!res.ok) throw new Error(res.statusText, { cause: res }); - addToast( - intl.formatMessage(messages.jellyfinSettingsSuccess, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), - { - autoDismiss: true, - appearance: 'success', - } - ); - } catch (e) { - let errorData; - try { - errorData = await e.cause?.text(); - errorData = JSON.parse(errorData); - } catch { - /* empty */ - } - if (errorData?.message === ApiErrorCode.InvalidUrl) { - addToast( - intl.formatMessage(messages.invalidurlerror, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), - { - autoDismiss: true, - appearance: 'error', - } - ); - } else { - addToast( - intl.formatMessage(messages.jellyfinSettingsFailure, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), - { - autoDismiss: true, - appearance: 'error', - } - ); - } - } finally { - revalidate(); + addToast( + intl.formatMessage(messages.jellyfinSettingsSuccess, { + mediaServerName: + publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + ? 'Emby' + : 'Jellyfin', + }), + { + autoDismiss: true, + appearance: 'success', } - }} - > - {({ - errors, - touched, - values, - setFieldValue, - handleSubmit, - isSubmitting, - isValid, - }) => { - return ( - + ); + } catch (e) { + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } + if (errorData?.message === ApiErrorCode.InvalidUrl) { + addToast( + intl.formatMessage(messages.invalidurlerror, { + mediaServerName: + publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + ? 'Emby' + : 'Jellyfin', + }), + { + autoDismiss: true, + appearance: 'error', + } + ); + } else { + addToast( + intl.formatMessage(messages.jellyfinSettingsFailure, { + mediaServerName: + publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + ? 'Emby' + : 'Jellyfin', + }), + { + autoDismiss: true, + appearance: 'error', + } + ); + } + } finally { + revalidate(); + } + }} + > + {({ + errors, + touched, + values, + setFieldValue, + handleSubmit, + isSubmitting, + isValid, + }) => { + return ( + + {showAdvancedSettings && ( + <>
+ + )} +
+ +
+
+ +
+ {errors.apiKey && touched.apiKey && ( +
{errors.apiKey}
+ )} +
+
+ {showAdvancedSettings && ( + <>
-
- -
-
- -
- {errors.jellyfinExternalUrl && - touched.jellyfinExternalUrl && ( -
- {errors.jellyfinExternalUrl} -
- )} -
+ + )} +
+ +
+
+
-
-
+ )} +
+
+
+ +
+
+ +
+ {errors.jellyfinForgotPasswordUrl && + touched.jellyfinForgotPasswordUrl && ( +
+ {errors.jellyfinForgotPasswordUrl} +
+ )} +
+
+
+
+ +
-
-
- - + + + {isSubmitting + ? intl.formatMessage(globalMessages.saving) + : intl.formatMessage(globalMessages.save)} -
-
- - ); - }} - - - )} + + +
+
+ + ); + }} + ); }; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index e24e2dc5..f34aa5b4 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -955,7 +955,7 @@ "components.Settings.is4k": "4K", "components.Settings.jellyfinForgotPasswordUrl": "Forgot Password URL", "components.Settings.jellyfinSettings": "{mediaServerName} Settings", - "components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.", + "components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page. You can also change the Jellyfin API key, which was automatically generated previously.", "components.Settings.jellyfinSettingsFailure": "Something went wrong while saving {mediaServerName} settings.", "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} settings saved successfully!", "components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "Custom authentication with Automatic Library Grouping not supported", From 0c7e65267248d6cb38a4304e201b0d38e9df2fb4 Mon Sep 17 00:00:00 2001 From: jellyseerr-weblate <155525085+jellyseerr-weblate@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:02:16 +0500 Subject: [PATCH 27/53] refactor(i18n): merge weblate (#934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added translation using Weblate (Slovenian) * Translated using Weblate (German) Currently translated at 93.1% (1216 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/de/ * Translated using Weblate (Slovenian) Currently translated at 4.1% (54 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/sl/ * Translated using Weblate (Dutch) Currently translated at 99.4% (1299 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/nl/ * Added translation using Weblate (Turkish) * Translated using Weblate (Turkish) Currently translated at 7.2% (95 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (German) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/de/ * Translated using Weblate (Romanian) Currently translated at 33.5% (438 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/ro/ * Translated using Weblate (Russian) Currently translated at 96.6% (1262 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/ru/ * Translated using Weblate (Russian) Currently translated at 97.4% (1273 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/ru/ * Translated using Weblate (Russian) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/ru/ * Translated using Weblate (Dutch) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/es/ * Translated using Weblate (French) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/fr/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 96.0% (1254 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/pt_BR/ * Translated using Weblate (Hebrew) Currently translated at 15.2% (199 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/he/ * Translated using Weblate (Polish) Currently translated at 82.6% (1079 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/pl/ * Translated using Weblate (Polish) Currently translated at 83.9% (1096 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/pl/ * Translated using Weblate (Spanish) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/es/ * Translated using Weblate (Ukrainian) Currently translated at 93.7% (1225 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Ukrainian) Currently translated at 94.3% (1232 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Ukrainian) Currently translated at 94.3% (1232 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Polish) Currently translated at 84.1% (1099 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/pl/ * Translated using Weblate (Ukrainian) Currently translated at 99.8% (1304 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Ukrainian) Currently translated at 99.8% (1304 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Ukrainian) Currently translated at 99.8% (1304 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Catalan) Currently translated at 94.1% (1230 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/ca/ * Translated using Weblate (Ukrainian) Currently translated at 99.8% (1304 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/uk/ * Translated using Weblate (Hebrew) Currently translated at 23.0% (301 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/he/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/zh_Hans/ * Translated using Weblate (Hebrew) Currently translated at 26.4% (346 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/he/ * Translated using Weblate (German) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/de/ * Translated using Weblate (Polish) Currently translated at 94.4% (1233 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/pl/ * Translated using Weblate (Turkish) Currently translated at 19.2% (252 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Slovenian) Currently translated at 4.2% (56 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/sl/ * Translated using Weblate (Turkish) Currently translated at 42.7% (558 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Turkish) Currently translated at 44.6% (583 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Turkish) Currently translated at 75.6% (988 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Turkish) Currently translated at 85.0% (1111 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (1306 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/tr/ * Translated using Weblate (Swedish) Currently translated at 99.7% (1303 of 1306 strings) Translate-URL: http://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/sv/ * style(i18n): ran prettier * style(i18n): ran prettier --------- Co-authored-by: Boštjan KOLAR Co-authored-by: Alex F Co-authored-by: Bas <910100490+weblate@proton.me> Co-authored-by: N/A Co-authored-by: Ramon Stohr Co-authored-by: Cosmin Mocan Co-authored-by: Aleksandr Co-authored-by: Aleksandr Co-authored-by: Bas Muldder Co-authored-by: 宿命 <331874545@qq.com> Co-authored-by: Eduard Perez Mendez Co-authored-by: Quack6765 Co-authored-by: grayair Co-authored-by: osh Co-authored-by: uqlel Co-authored-by: Flashk Co-authored-by: michael Co-authored-by: A a Co-authored-by: Albert Einstien Co-authored-by: C W Co-authored-by: Nir Israel Hen Co-authored-by: Adrian Konopczynski Co-authored-by: Wiktor Kowalski Co-authored-by: Jakob Števanec Co-authored-by: Mattias Magnusson Co-authored-by: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> --- src/i18n/locale/ca.json | 31 +- src/i18n/locale/de.json | 1055 +++++++++++----------- src/i18n/locale/es.json | 136 ++- src/i18n/locale/fr.json | 76 +- src/i18n/locale/he.json | 1609 +++++++--------------------------- src/i18n/locale/nl.json | 191 ++-- src/i18n/locale/pl.json | 342 ++++---- src/i18n/locale/pt_BR.json | 36 +- src/i18n/locale/ro.json | 90 +- src/i18n/locale/ru.json | 129 ++- src/i18n/locale/sl.json | 59 ++ src/i18n/locale/sv.json | 660 +++++++------- src/i18n/locale/tr.json | 1308 +++++++++++++++++++++++++++ src/i18n/locale/uk.json | 409 +++++---- src/i18n/locale/zh_Hans.json | 112 ++- 15 files changed, 3582 insertions(+), 2661 deletions(-) create mode 100644 src/i18n/locale/sl.json create mode 100644 src/i18n/locale/tr.json diff --git a/src/i18n/locale/ca.json b/src/i18n/locale/ca.json index 0cca1f6c..fb9b26d9 100644 --- a/src/i18n/locale/ca.json +++ b/src/i18n/locale/ca.json @@ -191,7 +191,7 @@ "components.Discover.TvGenreSlider.tvgenres": "Gèneres de Sèries", "components.Discover.TvGenreList.seriesgenres": "Gèneres de Sèries", "components.Discover.StudioSlider.studios": "Estudis", - "components.Discover.NetworkSlider.networks": "Plataformes", + "components.Discover.NetworkSlider.networks": "Emissors", "components.Discover.MovieGenreSlider.moviegenres": "Gèneres de Pel·lícules", "components.Discover.MovieGenreList.moviegenres": "Gèneres de Pel·lícules", "components.Discover.DiscoverTvLanguage.languageSeries": "Sèries en {language}", @@ -397,7 +397,7 @@ "components.TvDetails.originaltitle": "Títol original", "components.TvDetails.originallanguage": "Idioma original", "components.TvDetails.nextAirDate": "Pròxima data d'emissió", - "components.TvDetails.network": "{networkCount, plural, one {Plataforma} other {Plataformes}}", + "components.TvDetails.network": "{networkCount, plural, one {Emissor} other {Emissors}}", "components.TvDetails.firstAirDate": "Primera data d'emissió", "components.TvDetails.episodeRuntimeMinutes": "{runtime} minuts", "components.TvDetails.episodeRuntime": "Duració de l'episodi", @@ -494,7 +494,7 @@ "components.Settings.SonarrModal.validationNameRequired": "Heu de proporcionar un nom de servidor", "components.Settings.SonarrModal.validationLanguageProfileRequired": "Heu de seleccionar un perfil d'idioma", "components.Settings.SonarrModal.validationHostnameRequired": "Heu de proporcionar un nom d’amfitrió o una adreça IP vàlides", - "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "L'URL base no ha d'acabar amb una barra inclinada final", + "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "L'URL base no pot acabar amb una barra inclinada final", "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "L'URL base ha de tenir una barra inclinada", "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "L'URL no pot acabar amb una barra inclinada final", "components.Settings.SonarrModal.validationApplicationUrl": "Heu de proporcionar un URL vàlid", @@ -1135,7 +1135,7 @@ "components.Discover.CreateSlider.needresults": "Cal tenir almenys 1 resultat.", "components.Discover.CreateSlider.nooptions": "Sense resultats.", "components.Discover.CreateSlider.providetmdbgenreid": "Proporciona un ID de categoria TMDB", - "components.Discover.CreateSlider.providetmdbnetwork": "Proporciona l'ID de la plataforma TMDB", + "components.Discover.CreateSlider.providetmdbnetwork": "Proporciona l'ID d'emissor TMDB", "components.Discover.CreateSlider.providetmdbstudio": "Proporciona l'ID d'estudi TMDB", "components.Discover.CreateSlider.searchGenres": "Cercar per gènere…", "components.Discover.CreateSlider.searchKeywords": "Cercar per paraules clau…", @@ -1167,7 +1167,7 @@ "components.Discover.networks": "Emissors", "components.Discover.resetwarning": "Restablir tots els controls lliscants al valor predeterminat. Això també suprimirà els controls lliscants personalitzats!", "components.Discover.tmdbmoviekeyword": "Paraula clau de pel·lícula TMDB", - "components.Discover.tmdbnetwork": "Plataformes TMDB", + "components.Discover.tmdbnetwork": "Emissors TMDB", "components.Discover.FilterSlideover.tmdbuserscore": "Puntuació d'usuaris TMDB", "components.Discover.tvgenres": "Gèneres de sèries", "components.Discover.DiscoverTvKeyword.keywordSeries": "Sèries {keywordTitle}", @@ -1241,18 +1241,11 @@ "components.Settings.SettingsJobsCache.availability-sync": "Sincronització de disponibilitat de contingut", "components.Discover.tmdbmoviestreamingservices": "Serveis de transmissió de pel·lícules TMDB", "components.Discover.tmdbtvstreamingservices": "Serveis de transmissió de TV TMDB", - "components.Discover.FilterSlideover.tmdbuservotecount": "Recompte de vots dels usuaris de TMDB", - "components.Discover.FilterSlideover.voteCount": "Número de vots entre {minValue} i {maxValue}", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "So per a les notificacions", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Dispositiu per defecte", - "components.Settings.Notifications.NotificationsPushover.sound": "So per a les notificacions", - "components.Settings.SonarrModal.animeSeriesType": "Tipus d'Anime", - "components.Settings.SonarrModal.seriesType": "Tipus de sèrie", - "components.Settings.SonarrModal.tagRequests": "Sol·licituds d'etiquetes", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Dispositiu per defecte", - "i18n.collection": "Col·lecció", - "components.MovieDetails.imdbuserscore": "Puntuació dels usuaris de IMDB", - "components.Settings.RadarrModal.tagRequests": "Sol·licituds d'etiqueta", - "components.Settings.RadarrModal.tagRequestsInfo": "Automàticament afegeix una etiqueta addicional amb el nom d'usuari i nom complet del sol·licitant", - "components.Settings.SonarrModal.tagRequestsInfo": "Automàticament afegeix una etiqueta addicional amb el nom d'usuari i nom complet del sol·licitant" + "components.Layout.UserWarnings.emailRequired": "És requereix un n correu electrònic.", + "components.Layout.UserWarnings.passwordRequired": "Es requereix una contrasenya.", + "components.Login.description": "Com que és la primera vegada que inicieu sessió a {applicationName}, es necessita afegir un correu electrònic vàlid.", + "components.Discover.FilterSlideover.tmdbuservotecount": "Recompte de vots d'usuaris de TMDB", + "components.Discover.FilterSlideover.voteCount": "Nombre de vots entre {minValue} i {maxValue}", + "components.Layout.UserWarnings.emailInvalid": "El correu electrònic no és vàlid.", + "components.Login.credentialerror": "El nom d'usuari o la contrasenya són incorrectes." } diff --git a/src/i18n/locale/de.json b/src/i18n/locale/de.json index 6f8cdadc..f475b6ec 100644 --- a/src/i18n/locale/de.json +++ b/src/i18n/locale/de.json @@ -6,135 +6,37 @@ "components.CollectionDetails.overview": "Übersicht", "components.CollectionDetails.requestcollection": "Sammlung anfragen", "components.CollectionDetails.requestcollection4k": "Sammlung in 4K anfragen", - "components.Discover.createnewslider": "Neuen Slider erstellen", - "components.Discover.CreateSlider.addcustomslider": "Benutzerdefinierten Slider erstellen", - "components.Discover.CreateSlider.addfail": "Neuer Slider konnte nicht erstellt werden.", - "components.Discover.CreateSlider.addSlider": "Slider hinzufügen", - "components.Discover.CreateSlider.addsuccess": "Ein neuer Slider wurde erstellt und die Einstellungen wurden gespeichert.", - "components.Discover.CreateSlider.editfail": "Slider konnte nicht bearbeitet werden.", - "components.Discover.CreateSlider.editSlider": "Slider bearbeiten", - "components.Discover.CreateSlider.editsuccess": "Slider bearbeitet und Einstellung gespeichert.", - "components.Discover.CreateSlider.needresults": "Es muss mindestens 1 Ergebnis vorhanden sein.", - "components.Discover.CreateSlider.nooptions": "Keine Ergebnisse.", - "components.Discover.CreateSlider.providetmdbgenreid": "Hinterlege eine TMDB Genre ID", - "components.Discover.CreateSlider.providetmdbkeywordid": "Hinterlege eine TMDB Keyword ID", - "components.Discover.CreateSlider.providetmdbnetwork": "Hinterlege eine TMDB Network ID", - "components.Discover.CreateSlider.providetmdbsearch": "Geben Sie eine Suchanfrage an", - "components.Discover.CreateSlider.providetmdbstudio": "TMDB Studio ID angeben", - "components.Discover.CreateSlider.searchGenres": "Genres suchen…", - "components.Discover.CreateSlider.searchKeywords": "Stichwörter suchen…", - "components.Discover.CreateSlider.searchStudios": "Studios suchen…", - "components.Discover.CreateSlider.slidernameplaceholder": "Name des Slider", - "components.Discover.CreateSlider.starttyping": "Start der Suche durch Tippen.", - "components.Discover.CreateSlider.validationDatarequired": "Du musst einen Datenwert angeben.", - "components.Discover.CreateSlider.validationTitlerequired": "Du musst einen Titel eingeben.", - "components.Discover.customizediscover": "Discover anpassen", - "components.Discover.discover": "Entdecken", "components.Discover.DiscoverMovieGenre.genreMovies": "{genre}-Filme", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filme", "components.Discover.DiscoverMovieLanguage.languageMovies": "Filme auf {language}", - "components.Discover.discovermovies": "Beliebte Filme", - "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", - "components.Discover.DiscoverMovies.discovermovies": "Filme", - "components.Discover.DiscoverMovies.sortPopularityAsc": "Beliebtheit Aufsteigend", - "components.Discover.DiscoverMovies.sortPopularityDesc": "Beliebtheit Absteigend", - "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Erscheinungsdatum Aufsteigend", - "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Erscheinungsdatum Absteigend", - "components.Discover.DiscoverMovies.sortTitleAsc": "Titel (A-Z) Aufsteigend", - "components.Discover.DiscoverMovies.sortTitleDesc": "Titel (Z-A) Absteigend", - "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMDB-Bewertung Aufsteigend", - "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMDB-Bewertung Absteigend", "components.Discover.DiscoverNetwork.networkSeries": "{network}-Serien", - "components.Discover.DiscoverSliderEdit.deletefail": "Slider konnte nicht gelöscht werden.", - "components.Discover.DiscoverSliderEdit.deletesuccess": "Slider erfolgreich entfernt.", - "components.Discover.DiscoverSliderEdit.enable": "Sichtbarkeit umschalten", - "components.Discover.DiscoverSliderEdit.remove": "Entfernen", "components.Discover.DiscoverStudio.studioMovies": "{studio}-Filme", - "components.Discover.discovertv": "Beliebte Serien", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", - "components.Discover.DiscoverTv.discovertv": "Serien", - "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Erstausstrahlung Aufsteigend", - "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Erstausstrahlung Absteigend", - "components.Discover.DiscoverTv.sortPopularityAsc": "Beliebtheit Aufsteigend", - "components.Discover.DiscoverTv.sortPopularityDesc": "Beliebtheit Absteigend", - "components.Discover.DiscoverTv.sortTitleAsc": "Titel (A-Z) Aufsteigend", - "components.Discover.DiscoverTv.sortTitleDesc": "Titel (Z-A) Absteigend", - "components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMDB-Bewertung Aufsteigend", - "components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMDB-Bewertung Absteigend", "components.Discover.DiscoverTvGenre.genreSeries": "{genre}-Serien", - "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Serien", "components.Discover.DiscoverTvLanguage.languageSeries": "Serien auf {language}", - "components.Discover.DiscoverWatchlist.discoverwatchlist": "Deine Plex-Watchlist", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "Deine Beobachtungsliste", "components.Discover.DiscoverWatchlist.watchlist": "Plex Merkliste", - "components.Discover.emptywatchlist": "Hier erscheinen deine zur Plex Watchlist hinzugefügte Medien.", - "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", - "components.Discover.FilterSlideover.clearfilters": "Aktive Filter löschen", - "components.Discover.FilterSlideover.filters": "Filter", - "components.Discover.FilterSlideover.firstAirDate": "Datum der Erstausstrahlung", - "components.Discover.FilterSlideover.from": "Vom", - "components.Discover.FilterSlideover.genres": "Genres", - "components.Discover.FilterSlideover.keywords": "Stichwörter", - "components.Discover.FilterSlideover.originalLanguage": "Originalsprache", - "components.Discover.FilterSlideover.ratingText": "Bewertungen zwischen {minValue} und {maxValue}", - "components.Discover.FilterSlideover.releaseDate": "Erscheinungsdatum", - "components.Discover.FilterSlideover.runtime": "Laufzeit", - "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} Minuten Laufzeit", - "components.Discover.FilterSlideover.streamingservices": "Streaming-Dienste", - "components.Discover.FilterSlideover.studio": "Studio", - "components.Discover.FilterSlideover.tmdbuserscore": "TMDB-Benutzerbewertung", - "components.Discover.FilterSlideover.tmdbuservotecount": "Anzahl an TMDB Benutzerbewertungen", - "components.Discover.FilterSlideover.to": "Bis", - "components.Discover.FilterSlideover.voteCount": "Anzahl Abstimmungen zwischen {minValue} und {maxValue}", "components.Discover.MovieGenreList.moviegenres": "Filmgenres", - "components.Discover.moviegenres": "Film Genre", "components.Discover.MovieGenreSlider.moviegenres": "Filmgenres", - "components.Discover.networks": "Sender", "components.Discover.NetworkSlider.networks": "Sender", + "components.Discover.StudioSlider.studios": "Filmstudio", + "components.Discover.TvGenreList.seriesgenres": "Seriengenres", + "components.Discover.TvGenreSlider.tvgenres": "Seriengenres", + "components.Discover.discover": "Entdecken", + "components.Discover.discovermovies": "Beliebte Filme", + "components.Discover.discovertv": "Beliebte Serien", + "components.Discover.emptywatchlist": "Hier erscheinen deine zur Plex Watchlist hinzugefügte Medien.", "components.Discover.noRequests": "Keine Anfragen.", "components.Discover.plexwatchlist": "Deine Plex Watchlist", - "components.Discover.PlexWatchlistSlider.emptywatchlist": "Medien in deiner Plex Watchlist erscheinen hier.", - "components.Discover.PlexWatchlistSlider.plexwatchlist": "Deine Plex Watchlist", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "Kürzlich hinzugefügt", "components.Discover.popularmovies": "Beliebte Filme", "components.Discover.populartv": "Beliebte Serien", "components.Discover.recentlyAdded": "Kürzlich hinzugefügt", - "components.Discover.RecentlyAddedSlider.recentlyAdded": "Kürzlich hinzugefügt", "components.Discover.recentrequests": "Vorherige Anfragen", - "components.Discover.resetfailed": "Beim Zurücksetzen der Entdecken-Einstellungen ist etwas schief gegangen.", - "components.Discover.resetsuccess": "Die Entdecken-Einstellungen wurden erfolgreich zurückgesetzt.", - "components.Discover.resettodefault": "Zurücksetzen auf Standard", - "components.Discover.resetwarning": "Setzt alle Slider auf die Standardwerte zurück. Dadurch werden auch alle benutzerdefinierten Slider gelöscht!", - "components.Discover.stopediting": "Bearbeitung stoppen", - "components.Discover.studios": "Studios", - "components.Discover.StudioSlider.studios": "Filmstudio", - "components.Discover.tmdbmoviegenre": "TMDB Film Genre", - "components.Discover.tmdbmoviekeyword": "TMDB Film Keyword", - "components.Discover.tmdbmoviestreamingservices": "TMDB Film-Streaming-Dienste", - "components.Discover.tmdbnetwork": "TMDB Sender", - "components.Discover.tmdbsearch": "TMDB Suche", - "components.Discover.tmdbstudio": "TMDB Studio", - "components.Discover.tmdbtvgenre": "TMDB Serien Genre", - "components.Discover.tmdbtvkeyword": "TMDB Serien Keyword", - "components.Discover.tmdbtvstreamingservices": "TMDB TV-Streaming-Dienste", "components.Discover.trending": "Trends", - "components.Discover.TvGenreList.seriesgenres": "Seriengenres", - "components.Discover.tvgenres": "Serien Genre", - "components.Discover.TvGenreSlider.tvgenres": "Seriengenres", "components.Discover.upcoming": "Kommende Filme", "components.Discover.upcomingmovies": "Kommende Filme", "components.Discover.upcomingtv": "Kommende Serien", - "components.Discover.updatefailed": "Bei der Aktualisierung der Entdecken-Einstellungen ist ein Fehler aufgetreten.", - "components.Discover.updatesuccess": "Die Einstellungen für die Entdecken-Anpassung wurden aktualisiert.", "components.DownloadBlock.estimatedtime": "Geschätzte {time}", "components.DownloadBlock.formattedTitle": "{title}: Staffel {seasonNumber} Episode {episodeNumber}", - "components.IssueDetails.allepisodes": "Alle Folgen", - "components.IssueDetails.allseasons": "Alle Staffeln", - "components.IssueDetails.closeissue": "Problem schließen", - "components.IssueDetails.closeissueandcomment": "Schließen mit Kommentar", - "components.IssueDetails.commentplaceholder": "Kommentar hinzufügen…", - "components.IssueDetails.comments": "Kommentare", - "components.IssueDetails.deleteissue": "Problem löschen", - "components.IssueDetails.deleteissueconfirm": "Soll dieses Problem wirklich gelöscht werden?", - "components.IssueDetails.episode": "Folge {episodeNumber}", "components.IssueDetails.IssueComment.areyousuredelete": "Soll dieser Kommentar wirklich gelöscht werden?", "components.IssueDetails.IssueComment.delete": "Kommentar löschen", "components.IssueDetails.IssueComment.edit": "Kommentar bearbeiten", @@ -144,6 +46,15 @@ "components.IssueDetails.IssueDescription.deleteissue": "Problem löschen", "components.IssueDetails.IssueDescription.description": "Beschreibung", "components.IssueDetails.IssueDescription.edit": "Beschreibung bearbeiten", + "components.IssueDetails.allepisodes": "Alle Folgen", + "components.IssueDetails.allseasons": "Alle Staffeln", + "components.IssueDetails.closeissue": "Problem schließen", + "components.IssueDetails.closeissueandcomment": "Schließen mit Kommentar", + "components.IssueDetails.commentplaceholder": "Kommentar hinzufügen…", + "components.IssueDetails.comments": "Kommentare", + "components.IssueDetails.deleteissue": "Problem löschen", + "components.IssueDetails.deleteissueconfirm": "Soll dieses Problem wirklich gelöscht werden?", + "components.IssueDetails.episode": "Folge {episodeNumber}", "components.IssueDetails.issuepagetitle": "Problem", "components.IssueDetails.issuetype": "Art", "components.IssueDetails.lastupdated": "Letzte Aktualisierung", @@ -202,8 +113,6 @@ "components.LanguageSelector.originalLanguageDefault": "Alle Sprachen", "components.Layout.LanguagePicker.displaylanguage": "Sprache darstellen", "components.Layout.SearchInput.searchPlaceholder": "Nach Filmen und Serien suchen", - "components.Layout.Sidebar.browsemovies": "Filme", - "components.Layout.Sidebar.browsetv": "Serien", "components.Layout.Sidebar.dashboard": "Entdecken", "components.Layout.Sidebar.issues": "Probleme", "components.Layout.Sidebar.requests": "Anfragen", @@ -215,42 +124,21 @@ "components.Layout.UserDropdown.requests": "Anfragen", "components.Layout.UserDropdown.settings": "Einstellungen", "components.Layout.UserDropdown.signout": "Abmelden", - "components.Layout.UserWarnings.emailInvalid": "E-Mail-Adresse ist nicht gültig.", - "components.Layout.UserWarnings.emailRequired": "Eine E-Mail-Adresse ist erforderlich.", - "components.Layout.UserWarnings.passwordRequired": "Ein Passwort ist erforderlich.", "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {Version} other {Versionen}} hinterher", "components.Layout.VersionStatus.outofdate": "Veraltet", "components.Layout.VersionStatus.streamdevelop": "Jellyseerr Entwicklung", "components.Layout.VersionStatus.streamstable": "Jellyseerr Stable", - "components.Login.adminerror": "Es ist ein Admin Account für den Login erforderlich.", - "components.Login.credentialerror": "Der Benutzername oder das Passwort ist falsch.", - "components.Login.description": "Da es sich um deine erste Anmeldung bei {applicationName} handelt, musst du eine E-Mail-Addresse hinterlegen.", "components.Login.email": "E-Mail-Adresse", - "components.Login.emailtooltip": "Die Adresse muss nicht mit Ihrer {mediaServerName}-Instanz verbunden sein.", "components.Login.forgotpassword": "Passwort vergessen?", - "components.Login.host": "{mediaServerName} URL", - "components.Login.initialsignin": "Verbinde", - "components.Login.initialsigningin": "Verbindet…", "components.Login.loginerror": "Beim Anmelden ist etwas schief gelaufen.", "components.Login.password": "Passwort", - "components.Login.save": "Speichern", - "components.Login.saving": "Speichert…", "components.Login.signin": "Anmelden", "components.Login.signingin": "Anmelden …", "components.Login.signinheader": "Anmelden um fortzufahren", - "components.Login.signinwithjellyfin": "Verwende dein {mediaServerName}-Konto", "components.Login.signinwithoverseerr": "Verwende dein {applicationTitle}-Konto", "components.Login.signinwithplex": "Benutze dein Plex-Konto", - "components.Login.title": "E-Mail-Adresse hinzufügen", - "components.Login.username": "Benutzername", - "components.Login.validationemailformat": "Gültige E-Mail-Adresse benötigt", - "components.Login.validationEmailFormat": "Ungültige E-Mail-Adresse", "components.Login.validationemailrequired": "Du musst eine gültige E-Mail-Adresse angeben", - "components.Login.validationEmailRequired": "Du musst eine E-Mail-Adresse angeben", - "components.Login.validationhostformat": "Gültige URL benötigt", - "components.Login.validationhostrequired": "{mediaServerName} URL benötigt", "components.Login.validationpasswordrequired": "Du musst ein Passwort angeben", - "components.Login.validationusernamerequired": "Benutzername benötigt", "components.ManageSlideOver.alltime": "Gesamte Zeit", "components.ManageSlideOver.downloadstatus": "Downloads", "components.ManageSlideOver.manageModalAdvanced": "Fortgeschrittene", @@ -260,7 +148,6 @@ "components.ManageSlideOver.manageModalMedia": "Medien", "components.ManageSlideOver.manageModalMedia4k": "4K Medien", "components.ManageSlideOver.manageModalNoRequests": "Keine Anfragen.", - "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Dadurch wird {mediaType} unwiderruflich aus {arr} entfernt, einschließlich aller Dateien.", "components.ManageSlideOver.manageModalRequests": "Anfragen", "components.ManageSlideOver.manageModalTitle": "{mediaType} verwalten", "components.ManageSlideOver.mark4kavailable": "Als in 4K verfügbar markieren", @@ -274,29 +161,23 @@ "components.ManageSlideOver.pastdays": "Vergangene {days, number} Tage", "components.ManageSlideOver.playedby": "Abgespielt von", "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {abgespielt} other {abgespielt}}", - "components.ManageSlideOver.removearr": "Aus {arr} entfernen", - "components.ManageSlideOver.removearr4k": "4K aus {arr} entfernen", "components.ManageSlideOver.tvshow": "Serie", - "components.MediaSlider.ShowMoreCard.seemore": "Mehr anzeigen", + "components.MediaSlider.ShowMoreCard.seemore": "Mehr anschauen", + "components.MovieDetails.MovieCast.fullcast": "Komplette Besetzung", + "components.MovieDetails.MovieCrew.fullcrew": "Komplette Crew", "components.MovieDetails.budget": "Budget", "components.MovieDetails.cast": "Besetzung", "components.MovieDetails.digitalrelease": "Digitale Veröffentlichung", - "components.MovieDetails.downloadstatus": "Download Status", - "components.MovieDetails.imdbuserscore": "IMDB Nutzer Bewertung", "components.MovieDetails.managemovie": "Film verwalten", "components.MovieDetails.mark4kavailable": "4K als verfügbar markieren", "components.MovieDetails.markavailable": "Als verfügbar markieren", - "components.MovieDetails.openradarr": "Film in Radarr öffnen", - "components.MovieDetails.openradarr4k": "Film in 4K Radarr öffnen", - "components.MovieDetails.MovieCast.fullcast": "Komplette Besetzung", - "components.MovieDetails.MovieCrew.fullcrew": "Komplette Crew", "components.MovieDetails.originallanguage": "Originalsprache", "components.MovieDetails.originaltitle": "Originaltitel", "components.MovieDetails.overview": "Übersicht", "components.MovieDetails.overviewunavailable": "Übersicht nicht verfügbar.", "components.MovieDetails.physicalrelease": "DVD/Bluray-Veröffentlichungen", - "components.MovieDetails.play": "Auf {mediaServerName} abspielen", - "components.MovieDetails.play4k": "Auf {mediaServerName} in 4K abspielen", + "components.MovieDetails.play4konplex": "In 4K auf Plex abspielen", + "components.MovieDetails.playonplex": "Auf Plex abspielen", "components.MovieDetails.productioncountries": "Produktions {countryCount, plural, one {Land} other {Länder}}", "components.MovieDetails.recommendations": "Empfehlungen", "components.MovieDetails.releasedate": "{releaseCount, plural, one {Veröffentlichungstermin} other {Veröffentlichungstermine}}", @@ -325,10 +206,10 @@ "components.NotificationTypeSelector.issuereopenedDescription": "Sende eine Benachrichtigung, wenn Probleme wieder geöffnet werden.", "components.NotificationTypeSelector.issueresolved": "Problem gelöst", "components.NotificationTypeSelector.issueresolvedDescription": "Senden Benachrichtigungen, wenn Probleme gelöst sind.", - "components.NotificationTypeSelector.mediaapproved": "Anfrage genehmigt", - "components.NotificationTypeSelector.mediaapprovedDescription": "Sende Benachrichtigungen, wenn angeforderte Medien manuell genehmigt wurden.", "components.NotificationTypeSelector.mediaAutoApproved": "Anfrage automatisch genehmigt", "components.NotificationTypeSelector.mediaAutoApprovedDescription": "Sende eine Benachrichtigung, wenn das angeforderte Medium automatisch genehmigt wird.", + "components.NotificationTypeSelector.mediaapproved": "Anfrage genehmigt", + "components.NotificationTypeSelector.mediaapprovedDescription": "Sende Benachrichtigungen, wenn angeforderte Medien manuell genehmigt wurden.", "components.NotificationTypeSelector.mediaautorequested": "Automatisch übermittelte Anfrage", "components.NotificationTypeSelector.mediaautorequestedDescription": "Erhalten eine Benachrichtigung, wenn neue Medienanfragen für Objekte auf deiner Plex Watchlist automatisch übermittelt werden.", "components.NotificationTypeSelector.mediaavailable": "Anfrage verfügbar", @@ -344,8 +225,8 @@ "components.NotificationTypeSelector.userissuecreatedDescription": "Lassen dich benachrichtigen, wenn andere Benutzer Probleme melden.", "components.NotificationTypeSelector.userissuereopenedDescription": "Sende eine Benachrichtigung, wenn die von dir gemeldeten Probleme wieder geöffnet werden.", "components.NotificationTypeSelector.userissueresolvedDescription": "Sende eine Benachrichtigung, wenn andere Benutzer Kommentare zu Problemen abgeben.", - "components.NotificationTypeSelector.usermediaapprovedDescription": "Werde benachrichtigt, wenn Ihre Medienanfrage angenommen wurde.", "components.NotificationTypeSelector.usermediaAutoApprovedDescription": "Werde benachrichtigt, wenn andere Nutzer Medien anfordern, welche automatisch angenommen werden.", + "components.NotificationTypeSelector.usermediaapprovedDescription": "Werde benachrichtigt, wenn Ihre Medienanfrage angenommen wurde.", "components.NotificationTypeSelector.usermediaavailableDescription": "Sende eine Benachrichtigung, wenn Ihre Medienanfragen verfügbar sind.", "components.NotificationTypeSelector.usermediadeclinedDescription": "Werde benachrichtigt, wenn deine Medienanfrage abgelehnt wurde.", "components.NotificationTypeSelector.usermediafailedDescription": "Werde benachrichtigt, wenn die angeforderten Medien bei der Hinzufügung zu Radarr oder Sonarr fehlschlagen.", @@ -431,14 +312,14 @@ "components.RequestBlock.rootfolder": "Stammordner", "components.RequestBlock.seasons": "{seasonCount, plural, one {Staffel} other {Staffeln}}", "components.RequestBlock.server": "Zielserver", - "components.RequestButton.approve4krequests": "Genehmige {requestCount, plural, one {4K Anfrage} other {{requestCount} 4K Anfragen}}", + "components.RequestButton.approve4krequests": "Genehmige {requestCount, plural, one {4K Anfrage} other {{requestCount} 4K Requests}}", "components.RequestButton.approverequest": "Anfrage genehmigen", "components.RequestButton.approverequest4k": "4K Anfrage genehmigen", - "components.RequestButton.approverequests": "Genehmige {requestCount, plural, one {Anfrage} other {{requestCount} Anfragen}}", - "components.RequestButton.decline4krequests": "Lehne {requestCount, plural, one {4K Anfrage} other {{requestCount} 4K Anfragen}} ab", + "components.RequestButton.approverequests": "Genehmige {requestCount, plural, one {Anfrage} other {{requestCount} Requests}}", + "components.RequestButton.decline4krequests": "Lehne {requestCount, plural, one {4K Anfrage} other {{requestCount} 4K Requests}} ab", "components.RequestButton.declinerequest": "Anfrage ablehnen", "components.RequestButton.declinerequest4k": "4K Anfrage ablehnen", - "components.RequestButton.declinerequests": "Lehne {requestCount, plural, one {Anfrage} other {{requestCount} Anfragen}} ab", + "components.RequestButton.declinerequests": "Lehne {requestCount, plural, one {Anfrage} other {{requestCount} Requests}} ab", "components.RequestButton.requestmore": "Mehr anfragen", "components.RequestButton.requestmore4k": "Mehr in 4K anfragen", "components.RequestButton.viewrequest": "Anfrage anzeigen", @@ -483,17 +364,6 @@ "components.RequestModal.AdvancedRequester.rootfolder": "Stammordner", "components.RequestModal.AdvancedRequester.selecttags": "Wähle Tags aus", "components.RequestModal.AdvancedRequester.tags": "Tags", - "components.RequestModal.alreadyrequested": "Bereits Angefragt", - "components.RequestModal.approve": "Anfrage genehmigen", - "components.RequestModal.autoapproval": "Automatische Genehmigung", - "components.RequestModal.cancel": "Anfrage abbrechen", - "components.RequestModal.edit": "Anfrage bearbeiten", - "components.RequestModal.errorediting": "Beim Bearbeiten der Anfrage ist etwas schief gelaufen.", - "components.RequestModal.extras": "Extras", - "components.RequestModal.numberofepisodes": "Anzahl der Folgen", - "components.RequestModal.pending4krequest": "Ausstehende 4K Anfrage", - "components.RequestModal.pendingapproval": "Deine Anfrage steht noch aus.", - "components.RequestModal.pendingrequest": "Ausstehende Anfrage", "components.RequestModal.QuotaDisplay.allowedRequests": "Du darfst {limit} {type} Anfragen alle {days} Tage machen.", "components.RequestModal.QuotaDisplay.allowedRequestsUser": "Dieser Benutzer darf {limit} {type} Anfragen alle {days} Tage machen.", "components.RequestModal.QuotaDisplay.movie": "Filme", @@ -506,9 +376,23 @@ "components.RequestModal.QuotaDisplay.requiredquotaUser": "Dieser Benutzer muss mindestens {seasons} {seasons, plural, one {Staffel Anfrage} other {Staffel Anfragen}} verbleibend haben, um eine Anfrage für diese Serie einzureichen.", "components.RequestModal.QuotaDisplay.season": "Staffeln", "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {Staffel} other {Staffeln}}", - "components.RequestModal.requestadmin": "Diese Anfrage wird automatisch genehmigt.", + "components.RequestModal.SearchByNameModal.nomatches": "Wir konnten keine Übereinstimmung für diese Serie finden.", + "components.RequestModal.SearchByNameModal.notvdbiddescription": "Wir konnten diese Serie nicht automatisch zuordnen. Bitte wähle unten eine korrekte Übereinstimmung aus.", + "components.RequestModal.alreadyrequested": "Bereits Angefragt", + "components.RequestModal.approve": "Anfrage genehmigen", + "components.RequestModal.autoapproval": "Automatische Genehmigung", + "components.RequestModal.cancel": "Anfrage abbrechen", + "components.RequestModal.edit": "Anfrage bearbeiten", + "components.RequestModal.errorediting": "Beim Bearbeiten der Anfrage ist etwas schief gelaufen.", + "components.RequestModal.extras": "Extras", + "components.RequestModal.numberofepisodes": "Anzahl der Folgen", + "components.RequestModal.pending4krequest": "Ausstehende 4K Anfrage", + "components.RequestModal.pendingapproval": "Deine Anfrage steht noch aus.", + "components.RequestModal.pendingrequest": "Ausstehende Anfrage", "components.RequestModal.requestApproved": "Anfrage für {title} genehmigt!", "components.RequestModal.requestCancel": "Anfrage für {title} abgebrochen.", + "components.RequestModal.requestSuccess": "{title} erfolgreich angefragt!", + "components.RequestModal.requestadmin": "Diese Anfrage wird automatisch genehmigt.", "components.RequestModal.requestcancelled": "Anfrage für {title} abgebrochen.", "components.RequestModal.requestcollection4ktitle": "Sammlung in 4k anfragen", "components.RequestModal.requestcollectiontitle": "Sammlung anfragen", @@ -523,9 +407,6 @@ "components.RequestModal.requestseasons4k": "Anfrage {seasonCount} {seasonCount, plural, one {Serie} other {Serien}} in 4K", "components.RequestModal.requestseries4ktitle": "Serie in 4k anfragen", "components.RequestModal.requestseriestitle": "Serie anfragen", - "components.RequestModal.requestSuccess": "{title} erfolgreich angefragt!", - "components.RequestModal.SearchByNameModal.nomatches": "Wir konnten keine Übereinstimmung für diese Serie finden.", - "components.RequestModal.SearchByNameModal.notvdbiddescription": "Wir konnten diese Serie nicht automatisch zuordnen. Bitte wähle unten eine korrekte Übereinstimmung aus.", "components.RequestModal.season": "Staffel", "components.RequestModal.seasonnumber": "Staffel {number}", "components.RequestModal.selectmovies": "Wähle Film(e)", @@ -545,101 +426,6 @@ "components.ResetPassword.validationpasswordrequired": "Du musst ein Passwort angeben", "components.Search.search": "Suchen", "components.Search.searchresults": "Suchergebnisse", - "components.Selector.nooptions": "Keine Ergebnisse.", - "components.Selector.searchGenres": "Genres auswählen…", - "components.Selector.searchKeywords": "Stichwörter suchen…", - "components.Selector.searchStudios": "Studios suchen…", - "components.Selector.showless": "Weniger anzeigen", - "components.Selector.showmore": "Mehr anzeigen", - "components.Selector.starttyping": "Start der Suche durch Tippen.", - "components.Settings.activeProfile": "Aktives Profil", - "components.Settings.addradarr": "Radarr-Server hinzufügen", - "components.Settings.address": "Adresse", - "components.Settings.addsonarr": "Sonarr-Server hinzufügen", - "components.Settings.advancedTooltip": "Bei falscher Konfiguration dieser Einstellung, kann dies zu einer Funktionsstörung führen", - "components.Settings.apikey": "API-Schlüssel", - "components.Settings.applicationTitle": "Anwendungstitel", - "components.Settings.applicationurl": "Anwendungs-URL", - "components.Settings.cacheImages": "Bild-Caching aktivieren", - "components.Settings.cacheImagesTip": "Alle Bilder Optimieren und lokal speichern (verbraucht viel Speicherplatz)", - "components.Settings.cancelscan": "Durchsuchung abbrechen", - "components.Settings.copied": "API-Schlüssel in die Zwischenablage kopiert.", - "components.Settings.csrfProtection": "Aktiviere CSRF Schutz", - "components.Settings.csrfProtectionHoverTip": "Aktiviere diese Option NICHT, es sei denn du weißt, was du tust!", - "components.Settings.csrfProtectionTip": "Macht den externen API Zugang schreibgeschützt (setzt HTTPS voraus und Jellyseerr muss neu gestartet werden, damit die Änderungen wirksam werden)", - "components.Settings.currentlibrary": "Aktuelle Bibliothek: {name}", - "components.Settings.default": "Standardmäßig", - "components.Settings.default4k": "Standard-4K", - "components.Settings.deleteServer": "{serverType} Server löschen", - "components.Settings.deleteserverconfirm": "Bist du sicher, dass du diesen Server löschen möchtest?", - "components.Settings.email": "E-Mail", - "components.Settings.enablessl": "SSL aktivieren", - "components.Settings.experimentalTooltip": "Die Aktivierung dieser Einstellung kann zu einem unerwarteten Verhalten der Anwendung führen", - "components.Settings.externalUrl": "Externe URL", - "components.Settings.general": "Allgemein", - "components.Settings.generalsettings": "Allgemeine Einstellungen", - "components.Settings.generalsettingsDescription": "Konfiguriere Globale und Standard Jellyseerr-Einstellungen.", - "components.Settings.hideAvailable": "Verfügbare Medien ausblenden", - "components.Settings.hostname": "Hostname oder IP-Adresse", - "components.Settings.internalUrl": "Interne URL", - "components.Settings.is4k": "4K", - "components.Settings.jellyfinSettings": "{mediaServerName} Einstellungen", - "components.Settings.jellyfinSettingsDescription": "Konfiguriere optional die internen ud externen Endpunkte für deinen {mediaServerName}-Server. In den meisten Fällen ist die externe URL nicht die gleiche wie die interne URL. Außerdem kann eine eigene URL zum Zurücksetzen des Passworts angegeben werden, falls du an eine andere Seite weitergeleitet werden möchtest.", - "components.Settings.jellyfinSettingsFailure": "Etwas ist beim Speichern der {mediaServerName} Einstellungen schiefgelaufen.", - "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} Einstellungen erfolgreich gespeichert!", - "components.Settings.jellyfinlibraries": "{mediaServerName} Bibliotheken", - "components.Settings.jellyfinlibrariesDescription": "Die Bibliotheken in denen {mediaServerName} nach Titeln sucht. Klicke auf den Knopf unten, falls keine Bibliotheken gelistet werden.", - "components.Settings.jellyfinsettings": "{mediaServerName} Einstellungen", - "components.Settings.jellyfinsettingsDescription": "Konfiguriere die Einstellungen für deinen {mediaServerName}-Server. {mediaServerName} wird deine {mediaServerName}-Bibliothek durchsuchen um zu schauen, was verfügbar ist.", - "components.Settings.jellyfinSyncFailedNoLibrariesFound": "Keine Bibliotheken gefunden", - "components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "Automatische Bibliotheksgruppierung wird mit Benutzerdefinierter Anmeldung nicht unterstützt", - "components.Settings.jellyfinSyncFailedGenericError": "Etwas ist beim synchronisieren der Bibliotheken schiefgelaufen", - "components.Settings.librariesRemaining": "Verbleibende Bibliotheken: {count}", - "components.Settings.locale": "Sprache darstellen", - "components.Settings.manualscan": "Manuelle Bibliotheksdurchsuchung", - "components.Settings.manualscanDescription": "Normalerweise wird dies nur einmal alle 24 Stunden ausgeführt. Jellyseerr überprüft die kürzlich hinzugefügten Plex-Server aggressiver. Falls du Plex zum ersten Mal konfigurierst, wird eine einmalige vollständige manuelle Bibliotheksdurchsuchung empfohlen!", - "components.Settings.manualscanDescriptionJellyfin": "Normalerweise wird dies nur einmal alle 24 Stunden ausgeführt. Jellyseerr überprüft die kürzlich hinzugefügten {mediaServerName}-Server aggressiver. Falls du Plex zum ersten Mal konfigurierst, wird eine einmalige vollständige manuelle Bibliotheksdurchsuchung empfohlen!", - "components.Settings.manualscanJellyfin": "Manuelle Bibliotheksdurchsuchung", - "components.Settings.mediaTypeMovie": "Film", - "components.Settings.mediaTypeSeries": "Serie", - "components.Settings.menuAbout": "Über", - "components.Settings.menuGeneralSettings": "Allgemein", - "components.Settings.menuJellyfinSettings": "{mediaServerName}", - "components.Settings.menuJobs": "Aufgaben und Cache", - "components.Settings.menuLogs": "Protokolle", - "components.Settings.menuNotifications": "Benachrichtigungen", - "components.Settings.menuPlexSettings": "Plex", - "components.Settings.menuServices": "Dienste", - "components.Settings.menuUsers": "Benutzer", - "components.Settings.noDefault4kServer": "Ein 4K {serverType} Server muss als Standart markiert werden um Nutzern zu ermöglichen 4K {mediaType} anfragen zu senden.", - "components.Settings.noDefaultNon4kServer": "Wenn du nur einen einzigen {serverType}-Server für Nicht-4K- und 4K-Inhalte hast (oder wenn du nur 4K-Inhalte herunterlädst), solltest du den {serverType}-Server NICHT als 4K-Server festgelegen.", - "components.Settings.noDefaultServer": "Mindestens ein {serverType}-Server muss als Standard markiert sein, damit {mediaType}-Anfragen verarbeitet werden können.", - "components.Settings.notificationAgentSettingsDescription": "Konfiguriere und aktiviere Benachrichtigungsagenten.", - "components.Settings.notifications": "Benachrichtigungen", - "components.Settings.Notifications.agentenabled": "Agent aktivieren", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Geräte Standard", - "components.Settings.Notifications.allowselfsigned": "Selbstsignierte Zertifikate erlauben", - "components.Settings.Notifications.authPass": "SMTP-Passwort", - "components.Settings.Notifications.authUser": "SMTP-Benutzername", - "components.Settings.Notifications.botAPI": "Bot-Autorisierungstoken", - "components.Settings.Notifications.botApiTip": "Erstelle einen Bot für die Verwendung mit Jellyseerr", - "components.Settings.Notifications.botAvatarUrl": "Bot Avatar URL", - "components.Settings.Notifications.botUsername": "Bot Benutzername", - "components.Settings.Notifications.botUsernameTip": "Benutzern erlauben, einen Chat mit dem Bot zu starten und ihre eigenen Benachrichtigungen konfigurieren", - "components.Settings.Notifications.chatId": "Chat-ID", - "components.Settings.Notifications.chatIdTip": "Starte einen Chat mit dem Bot, füge @get_id_bot hinzu, und erteile den /my_id Befehl", - "components.Settings.Notifications.discordsettingsfailed": "Discord-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", - "components.Settings.Notifications.discordsettingssaved": "Discord-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.Settings.Notifications.emailsender": "Absenderadresse", - "components.Settings.Notifications.emailsettingsfailed": "E-Mail-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", - "components.Settings.Notifications.emailsettingssaved": "E-Mail-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.Settings.Notifications.enableMentions": "Erwähnungen aktivieren", - "components.Settings.Notifications.encryption": "Verschlüsselungsmethode", - "components.Settings.Notifications.encryptionDefault": "Verwende STARTTLS wenn verfügbar", - "components.Settings.Notifications.encryptionImplicitTls": "Benutze Implizit TLS", - "components.Settings.Notifications.encryptionNone": "Keine", - "components.Settings.Notifications.encryptionOpportunisticTls": "STARTTLS immer verwenden", - "components.Settings.Notifications.encryptionTip": "Im Regelfall verwendet Implicit TLS Port 465 und STARTTLS Port 587", "components.Settings.Notifications.NotificationsGotify.agentenabled": "Agent aktivieren", "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": "Die Gotify-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "Gotify Benachrichtigungseinstellungen erfolgreich gespeichert!", @@ -680,7 +466,6 @@ "components.Settings.Notifications.NotificationsPushover.agentenabled": "Agent aktivieren", "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": "Pushover-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", "components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "Pushover-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.Settings.Notifications.NotificationsPushover.sound": "Benachrichtigungston", "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": "Pushover Test Benachrichtigung fehlgeschlagen.", "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "Pushover Test Benachrichtigung wird gesendet…", "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSuccess": "Pushover Test Benachrichtigung gesendet!", @@ -699,6 +484,13 @@ "components.Settings.Notifications.NotificationsSlack.validationWebhookUrl": "Du musst eine gültige URL angeben", "components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook URL", "components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Erstelle eine Eingehende Webhook integration", + "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Agent aktivieren", + "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "Um web push Benachrichtigungen zu erhalten, muss Jellyseerr über HTTPS gehen.", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web push Test Benachrichtigung fehlgeschlagen.", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Web push test Benachrichtigung wird gesendet…", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Web push test Benachrichtigung gesendet!", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": "Web push Benachrichtigungseinstellungen konnten nicht gespeichert werden.", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "Web push Benachrichtigungseinstellungen erfolgreich gespeichert!", "components.Settings.Notifications.NotificationsWebhook.agentenabled": "Dienst aktivieren", "components.Settings.Notifications.NotificationsWebhook.authheader": "Autorisierungsüberschrift", "components.Settings.Notifications.NotificationsWebhook.customJson": "JSON-Inhalt", @@ -711,23 +503,39 @@ "components.Settings.Notifications.NotificationsWebhook.validationJsonPayloadRequired": "Du musst einen gültigen JSON-Inhalt angeben", "components.Settings.Notifications.NotificationsWebhook.validationTypes": "Sie müssen mindestens einen Benachrichtigungstypen auswählen", "components.Settings.Notifications.NotificationsWebhook.validationWebhookUrl": "Du musst eine gültige URL angeben", + "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook-URL", "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Webhook-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Webhook-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook-URL", - "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Agent aktivieren", - "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "Um web push Benachrichtigungen zu erhalten, muss Jellyseerr über HTTPS gehen.", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web push Test Benachrichtigung fehlgeschlagen.", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Web push test Benachrichtigung wird gesendet…", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Web push test Benachrichtigung gesendet!", - "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": "Web push Benachrichtigungseinstellungen konnten nicht gespeichert werden.", - "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "Web push Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.Settings.Notifications.agentenabled": "Agent aktivieren", + "components.Settings.Notifications.allowselfsigned": "Selbstsignierte Zertifikate erlauben", + "components.Settings.Notifications.authPass": "SMTP-Passwort", + "components.Settings.Notifications.authUser": "SMTP-Benutzername", + "components.Settings.Notifications.botAPI": "Bot-Autorisierungstoken", + "components.Settings.Notifications.botApiTip": "Erstelle einen Bot für die Verwendung mit Jellyseerr", + "components.Settings.Notifications.botAvatarUrl": "Bot Avatar URL", + "components.Settings.Notifications.botUsername": "Bot Benutzername", + "components.Settings.Notifications.botUsernameTip": "Benutzern erlauben, einen Chat mit dem Bot zu starten und ihre eigenen Benachrichtigungen konfigurieren", + "components.Settings.Notifications.chatId": "Chat-ID", + "components.Settings.Notifications.chatIdTip": "Starte einen Chat mit dem Bot, füge @get_id_bot hinzu, und erteile den /my_id Befehl", + "components.Settings.Notifications.discordsettingsfailed": "Discord-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", + "components.Settings.Notifications.discordsettingssaved": "Discord-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.Settings.Notifications.emailsender": "Absenderadresse", + "components.Settings.Notifications.emailsettingsfailed": "E-Mail-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", + "components.Settings.Notifications.emailsettingssaved": "E-Mail-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.Settings.Notifications.enableMentions": "Erwähnungen aktivieren", + "components.Settings.Notifications.encryption": "Verschlüsselungsmethode", + "components.Settings.Notifications.encryptionDefault": "Verwende STARTTLS wenn verfügbar", + "components.Settings.Notifications.encryptionImplicitTls": "Benutze Implizit TLS", + "components.Settings.Notifications.encryptionNone": "Keine", + "components.Settings.Notifications.encryptionOpportunisticTls": "STARTTLS immer verwenden", + "components.Settings.Notifications.encryptionTip": "Im Regelfall verwendet Implicit TLS Port 465 und STARTTLS Port 587", "components.Settings.Notifications.pgpPassword": "PGP Passwort", "components.Settings.Notifications.pgpPasswordTip": "Signiere verschlüsselte E-Mail-Nachrichten mit OpenPGP", "components.Settings.Notifications.pgpPrivateKey": "PGP Privater Schlüssel", "components.Settings.Notifications.pgpPrivateKeyTip": "Signiere verschlüsselte E-Mail-Nachrichten mit OpenPGP", - "components.Settings.Notifications.senderName": "Absendername", "components.Settings.Notifications.sendSilently": "Sende stumm", "components.Settings.Notifications.sendSilentlyTip": "Sende Benachrichtigungen ohne Ton", + "components.Settings.Notifications.senderName": "Absendername", "components.Settings.Notifications.smtpHost": "SMTP-Host", "components.Settings.Notifications.smtpPort": "SMTP-Port", "components.Settings.Notifications.telegramsettingsfailed": "Telegram-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", @@ -741,7 +549,6 @@ "components.Settings.Notifications.toastTelegramTestFailed": "Telegram test Benachrichtigung fehlgeschlagen.", "components.Settings.Notifications.toastTelegramTestSending": "Telegram test Benachrichtigung wird gesendet…", "components.Settings.Notifications.toastTelegramTestSuccess": "Telegram test Benachrichtigung gesendet!", - "components.Settings.Notifications.userEmailRequired": "Benötigt Benutzer E-Mail-Adresse", "components.Settings.Notifications.validationBotAPIRequired": "Du musst ein Bot-Autorisierungstoken angeben", "components.Settings.Notifications.validationChatIdRequired": "Du musst eine gültige Chat-ID angeben", "components.Settings.Notifications.validationEmail": "Du musst eine gültige E-Mail-Adresse angeben", @@ -753,17 +560,6 @@ "components.Settings.Notifications.validationUrl": "Du musst eine gültige URL angeben", "components.Settings.Notifications.webhookUrl": "Webhook-URL", "components.Settings.Notifications.webhookUrlTip": "Erstelle eine webhook Integration auf dem Server", - "components.Settings.notificationsettings": "Benachrichtigungseinstellungen", - "components.Settings.notrunning": "Nicht aktiv", - "components.Settings.originallanguage": "Sprache Entdecken", - "components.Settings.originallanguageTip": "Filtere Inhalte nach Originalsprache", - "components.Settings.partialRequestsEnabled": "Teilserienanfragen erlauben", - "components.Settings.plex": "Plex", - "components.Settings.plexlibraries": "Plex-Bibliotheken", - "components.Settings.plexlibrariesDescription": "Die Bibliotheken, welche Jellyseerr nach Titeln durchsucht. Richte deine Plex-Verbindungseinstellungen ein und speichere sie, klicke auf die Schaltfläche unten, wenn keine aufgeführt sind.", - "components.Settings.plexsettings": "Plex-Einstellungen", - "components.Settings.plexsettingsDescription": "Konfiguriere die Einstellungen für deinen Plex-Server. Jellyseerr durchsucht deine Plex-Bibliotheken, um festzustellen welche Inhalte verfügbar sind.", - "components.Settings.port": "Port", "components.Settings.RadarrModal.add": "Server hinzufügen", "components.Settings.RadarrModal.announced": "Angekündigt", "components.Settings.RadarrModal.apiKey": "API-Schlüssel", @@ -778,9 +574,9 @@ "components.Settings.RadarrModal.externalUrl": "Externe URL", "components.Settings.RadarrModal.hostname": "Hostname oder IP-Adresse", "components.Settings.RadarrModal.inCinemas": "Im Kino", + "components.Settings.RadarrModal.loadingTags": "Lade Tags…", "components.Settings.RadarrModal.loadingprofiles": "Qualitätsprofile werden geladen …", "components.Settings.RadarrModal.loadingrootfolders": "Stammordner werden geladen …", - "components.Settings.RadarrModal.loadingTags": "Lade Tags…", "components.Settings.RadarrModal.minimumAvailability": "Mindestverfügbarkeit", "components.Settings.RadarrModal.notagoptions": "Keine Tags.", "components.Settings.RadarrModal.port": "Port", @@ -795,8 +591,6 @@ "components.Settings.RadarrModal.servername": "Servername", "components.Settings.RadarrModal.ssl": "SSL aktivieren", "components.Settings.RadarrModal.syncEnabled": "Scannen aktivieren", - "components.Settings.RadarrModal.tagRequests": "Tagge Anfragen", - "components.Settings.RadarrModal.tagRequestsInfo": "Füge automatisch ein Tag hinzu mit der ID und dem Namen des anfordernden Nutzers", "components.Settings.RadarrModal.tags": "Tags", "components.Settings.RadarrModal.testFirstQualityProfiles": "Teste die Verbindung, um Qualitätsprofile zu laden", "components.Settings.RadarrModal.testFirstRootFolders": "Teste die Verbindung, um Stammordner zu laden", @@ -814,23 +608,13 @@ "components.Settings.RadarrModal.validationPortRequired": "Du musst einen Port angeben", "components.Settings.RadarrModal.validationProfileRequired": "Du musst ein Qualitätsprofil auswählen", "components.Settings.RadarrModal.validationRootFolderRequired": "Du musst einen Stammordner auswählen", - "components.Settings.radarrsettings": "Radarr-Einstellungen", - "components.Settings.region": "Region Entdecken", - "components.Settings.regionTip": "Filtere Inhalte nach regionaler Verfügbarkeit", - "components.Settings.restartrequiredTooltip": "Jellyseerr muss neu gestartet werden, damit Änderungen an dieser Einstellung wirksam werden", - "components.Settings.save": "Änderungen Speichern", - "components.Settings.saving": "Wird gespeichert...", - "components.Settings.scan": "Bibliotheken synchronisieren", - "components.Settings.scanning": "Synchronisieren…", - "components.Settings.serverLocal": "lokal", - "components.Settings.serverpreset": "Server", - "components.Settings.serverpresetLoad": "Drück den Knopf, um verfügbare Server zu laden", - "components.Settings.serverpresetManualMessage": "Manuelle Konfiguration", - "components.Settings.serverpresetRefreshing": "Rufe Server ab …", - "components.Settings.serverRemote": "entfernt", - "components.Settings.serverSecure": "Sicher", - "components.Settings.services": "Dienstleistungen", - "components.Settings.serviceSettingsDescription": "Konfiguriere unten deine {serverType}-Server. Du kannst mehrere {serverType}-Server verbinden, aber nur zwei davon können als Standard markiert werden (ein Nicht-4K- und ein 4K-Server). Administratoren können den Server überschreiben, auf dem neue Anfragen vor der Genehmigung verarbeitet werden.", + "components.Settings.SettingsAbout.Releases.currentversion": "Aktuell", + "components.Settings.SettingsAbout.Releases.latestversion": "Neuste", + "components.Settings.SettingsAbout.Releases.releasedataMissing": "Informationen der Version nicht verfügbar.", + "components.Settings.SettingsAbout.Releases.releases": "Veröffentlichungen", + "components.Settings.SettingsAbout.Releases.versionChangelog": "Änderungsprotokoll {version}", + "components.Settings.SettingsAbout.Releases.viewchangelog": "Änderungsprotokoll anzeigen", + "components.Settings.SettingsAbout.Releases.viewongithub": "Auf GitHub anzeigen", "components.Settings.SettingsAbout.about": "Über", "components.Settings.SettingsAbout.appDataPath": "Datenverzeichnis", "components.Settings.SettingsAbout.betawarning": "Dies ist eine BETA Software. Einige Funktionen könnten nicht funktionieren oder nicht stabil funktionieren. Bitte auf GitHub alle Fehler melden!", @@ -841,22 +625,13 @@ "components.Settings.SettingsAbout.outofdate": "Veraltet", "components.Settings.SettingsAbout.overseerrinformation": "Über Jellyseerr", "components.Settings.SettingsAbout.preferredmethod": "Bevorzugt", - "components.Settings.SettingsAbout.Releases.currentversion": "Aktuell", - "components.Settings.SettingsAbout.Releases.latestversion": "Neuste", - "components.Settings.SettingsAbout.Releases.releasedataMissing": "Informationen der Version nicht verfügbar.", - "components.Settings.SettingsAbout.Releases.releases": "Veröffentlichungen", - "components.Settings.SettingsAbout.Releases.versionChangelog": "Änderungsprotokoll {version}", - "components.Settings.SettingsAbout.Releases.viewchangelog": "Änderungsprotokoll anzeigen", - "components.Settings.SettingsAbout.Releases.viewongithub": "Auf GitHub anzeigen", "components.Settings.SettingsAbout.runningDevelop": "Sie benutzen den develop von Jellyseerr, der nur für diejenigen empfohlen wird, die an der Entwicklung mitwirken oder bei den neuesten Tests helfen.", - "components.Settings.SettingsAbout.supportoverseerr": "Unterstütze Overseerr", - "components.Settings.SettingsAbout.supportjellyseerr": "Unterstütze Jellyseerr", + "components.Settings.SettingsAbout.supportoverseerr": "Unterstütze Jellyseerr", "components.Settings.SettingsAbout.timezone": "Zeitzone", "components.Settings.SettingsAbout.totalmedia": "Medien insgesamt", "components.Settings.SettingsAbout.totalrequests": "Anfragen insgesamt", "components.Settings.SettingsAbout.uptodate": "Auf dem neusten Stand", "components.Settings.SettingsAbout.version": "Version", - "components.Settings.SettingsJobsCache.availability-sync": "Medienverfügbarkeit Sync", "components.Settings.SettingsJobsCache.cache": "Cache", "components.Settings.SettingsJobsCache.cacheDescription": "Jellyseerr speichert Anfragen an externe API Endpunkte zwischen, um die Leistung zu optimieren und unnötige API Aufrufe zu minimieren.", "components.Settings.SettingsJobsCache.cacheflushed": "{cachename} Cache geleert.", @@ -875,22 +650,21 @@ "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Häufigkeit", "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Alle {jobScheduleHours, plural, one {Stunde} other {{jobScheduleHours} Stunden}}", "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Alle {jobScheduleMinutes, plural, one {Minute} other {{jobScheduleMinutes} Minuten}}", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Alle {jobScheduleSeconds, plural, one {Sekunde} other {{jobScheduleSeconds} Sekunden}}", "components.Settings.SettingsJobsCache.flushcache": "Cache leeren", "components.Settings.SettingsJobsCache.image-cache-cleanup": "Bild-Cache-Bereinigung", "components.Settings.SettingsJobsCache.imagecache": "Bild-Cache", - "components.Settings.SettingsJobsCache.imagecachecount": "Bilder im Cache", "components.Settings.SettingsJobsCache.imagecacheDescription": "Wenn diese Funktion in den Einstellungen aktiviert ist, wird Jellyseerr Bilder aus vorkonfigurierten externen Quellen im Proxy-Cache zwischenspeichern. Bilder im Zwischenspeicher werden in deinem Konfigurationsordner gespeichert. Du findest die Dateien unter {appDataPath}/cache/images.", + "components.Settings.SettingsJobsCache.imagecachecount": "Bilder im Cache", "components.Settings.SettingsJobsCache.imagecachesize": "Gesamtgröße des Caches", - "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Vollständiger Jellyfin Bibliotheken Scan", "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Scan der zuletzt hinzugefügten Jellyfin Medien", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Vollständiger Jellyfin Bibliotheken Scan", + "components.Settings.SettingsJobsCache.jobScheduleEditFailed": "Beim Speichern des Auftrags ging etwas schief.", + "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "Auftrag erfolgreich bearbeitet!", "components.Settings.SettingsJobsCache.jobcancelled": "{jobname} abgebrochen.", "components.Settings.SettingsJobsCache.jobname": "Aufgabenname", "components.Settings.SettingsJobsCache.jobs": "Aufgaben", - "components.Settings.SettingsJobsCache.jobsandcache": "Aufgaben und Cache", - "components.Settings.SettingsJobsCache.jobScheduleEditFailed": "Beim Speichern des Auftrags ging etwas schief.", - "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "Auftrag erfolgreich bearbeitet!", "components.Settings.SettingsJobsCache.jobsDescription": "Jellyseerr führt bestimmte Wartungsaufgaben als regulär geplante Aufgaben durch, aber sie können auch manuell ausgeführt werden. Manuelles Ausführen einer Aufgabe ändert ihren Zeitplan nicht.", + "components.Settings.SettingsJobsCache.jobsandcache": "Aufgaben und Cache", "components.Settings.SettingsJobsCache.jobstarted": "{jobname} gestartet.", "components.Settings.SettingsJobsCache.jobtype": "Art", "components.Settings.SettingsJobsCache.nextexecution": "Nächste Ausführung", @@ -920,33 +694,6 @@ "components.Settings.SettingsLogs.showall": "Alle Protokolle anzeigen", "components.Settings.SettingsLogs.time": "Zeitstempel", "components.Settings.SettingsLogs.viewdetails": "Details anzeigen", - "components.Settings.SettingsMain.apikey": "API Schlüssel", - "components.Settings.SettingsMain.applicationTitle": "Anwendungstitel", - "components.Settings.SettingsMain.applicationurl": "Anwendung URL", - "components.Settings.SettingsMain.cacheImages": "Bild-Caching aktivieren", - "components.Settings.SettingsMain.cacheImagesTip": "Cache extern gehostete Bilder (erfordert eine beträchtliche Menge an Speicherplatz)", - "components.Settings.SettingsMain.csrfProtection": "Aktivere CSRF Schutz", - "components.Settings.SettingsMain.csrfProtectionHoverTip": "Aktiviere diese Einstellung nur wenn du weißt was du tust!", - "components.Settings.SettingsMain.csrfProtectionTip": "Limitiere externen API Zugriff auf Lese-Operationen (erfordert HTTPS)", - "components.Settings.SettingsMain.general": "Allgemein", - "components.Settings.SettingsMain.generalsettings": "Allgemeine Einstellungen", - "components.Settings.SettingsMain.generalsettingsDescription": "Konfiguration der globalen und Standardeinstellungen für Jellyseerr.", - "components.Settings.SettingsMain.hideAvailable": "Verfügbare Medien ausblenden", - "components.Settings.SettingsMain.locale": "Anzeigesprache", - "components.Settings.SettingsMain.originallanguage": "\"Entdecken\" Sprache", - "components.Settings.SettingsMain.originallanguageTip": "Inhalt nach Originalsprache filtern", - "components.Settings.SettingsMain.partialRequestsEnabled": "Teilweise Serienanfragen zulassen", - "components.Settings.SettingsMain.region": "\"Entdecken\" Region", - "components.Settings.SettingsMain.regionTip": "Inhalte nach regionaler Verfügbarkeit filtern", - "components.Settings.SettingsMain.toastApiKeyFailure": "Etwas ist schiefgelaufen während der Generierung eines neuen API Schlüssels.", - "components.Settings.SettingsMain.toastApiKeySuccess": "Neuer API Schlüssel erfolgreich generiert!", - "components.Settings.SettingsMain.toastSettingsFailure": "Beim Speichern der Einstellungen ist ein Fehler aufgetreten.", - "components.Settings.SettingsMain.toastSettingsSuccess": "Einstellungen erfolgreich gespeichert!", - "components.Settings.SettingsMain.trustProxy": "Proxyunterstützung aktivieren", - "components.Settings.SettingsMain.trustProxyTip": "Erlaube Jellyseerr, Client-IP-Adressen hinter einem Proxy korrekt zu registrieren", - "components.Settings.SettingsMain.validationApplicationTitle": "Du musst einen Anwendungstitel spezifizieren", - "components.Settings.SettingsMain.validationApplicationUrl": "Du musst eine valide URL spezifizieren", - "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "Die URL darf nicht mit einem Slash \"/\" enden", "components.Settings.SettingsUsers.defaultPermissions": "Standardberechtigungen", "components.Settings.SettingsUsers.defaultPermissionsTip": "Iniziale Berechtigungen für neue Nutzer", "components.Settings.SettingsUsers.localLogin": "Lokale Anmeldung aktivieren", @@ -957,16 +704,14 @@ "components.Settings.SettingsUsers.toastSettingsFailure": "Beim Speichern der Einstellungen ist ein Fehler aufgetreten.", "components.Settings.SettingsUsers.toastSettingsSuccess": "Benutzereinstellungen erfolgreich gespeichert!", "components.Settings.SettingsUsers.tvRequestLimitLabel": "Globales Serienanfragenlimit", - "components.Settings.SettingsUsers.users": "Benutzer", "components.Settings.SettingsUsers.userSettings": "Benutzereinstellungen", "components.Settings.SettingsUsers.userSettingsDescription": "Globale und Standardbenutzereinstellungen konfigurieren.", - "components.Settings.settingUpPlexDescription": "Um Plex einzurichten, können die Daten manuell eintragen oder einen Server ausgewählt werden, welcher von plex.tv abgerufen wurde. Drück den Knopf rechts neben dem Dropdown-Menü, um die Liste der verfügbaren Server abzurufen.", + "components.Settings.SettingsUsers.users": "Benutzer", "components.Settings.SonarrModal.add": "Server hinzufügen", - "components.Settings.SonarrModal.animeSeriesType": "Anime Serien Typ", + "components.Settings.SonarrModal.animeTags": "Anime Tags", "components.Settings.SonarrModal.animelanguageprofile": "Anime-Sprachprofil", "components.Settings.SonarrModal.animequalityprofile": "Animequalitätsprofil", "components.Settings.SonarrModal.animerootfolder": "Animestammverzeichnis", - "components.Settings.SonarrModal.animeTags": "Anime Tags", "components.Settings.SonarrModal.apiKey": "API-Schlüssel", "components.Settings.SonarrModal.baseUrl": "Basis-URL", "components.Settings.SonarrModal.create4ksonarr": "Neuen 4K Sonarr Server hinzufügen", @@ -979,10 +724,10 @@ "components.Settings.SonarrModal.externalUrl": "Externe URL", "components.Settings.SonarrModal.hostname": "Hostname oder IP-Adresse", "components.Settings.SonarrModal.languageprofile": "Sprachprofil", + "components.Settings.SonarrModal.loadingTags": "Lade Tags…", "components.Settings.SonarrModal.loadinglanguageprofiles": "Sprachprofile werden geladen …", "components.Settings.SonarrModal.loadingprofiles": "Qualitätsprofile werden geladen …", "components.Settings.SonarrModal.loadingrootfolders": "Stammordner werden geladen …", - "components.Settings.SonarrModal.loadingTags": "Lade Tags…", "components.Settings.SonarrModal.notagoptions": "Keine Tags.", "components.Settings.SonarrModal.port": "Port", "components.Settings.SonarrModal.qualityprofile": "Qualitätsprofil", @@ -992,13 +737,10 @@ "components.Settings.SonarrModal.selectQualityProfile": "Wähle Qualitätsprofil", "components.Settings.SonarrModal.selectRootFolder": "Wähle Stammordner", "components.Settings.SonarrModal.selecttags": "Wähle Tags", - "components.Settings.SonarrModal.seriesType": "Serien Typ", "components.Settings.SonarrModal.server4k": "4K-Server", "components.Settings.SonarrModal.servername": "Servername", "components.Settings.SonarrModal.ssl": "SSL aktivieren", "components.Settings.SonarrModal.syncEnabled": "Scannen aktivieren", - "components.Settings.SonarrModal.tagRequests": "Tag Anforderungen", - "components.Settings.SonarrModal.tagRequestsInfo": "Füge automatisch einen zusätzlichen Tag mit der ID & Namen des anfordernden Nutzers", "components.Settings.SonarrModal.tags": "Tags", "components.Settings.SonarrModal.testFirstLanguageProfiles": "Teste die Verbindung zum Laden von Sprachprofilen", "components.Settings.SonarrModal.testFirstQualityProfiles": "Teste die Verbindung, um Qualitätsprofile zu laden", @@ -1017,15 +759,88 @@ "components.Settings.SonarrModal.validationPortRequired": "Du musst einen Port angeben", "components.Settings.SonarrModal.validationProfileRequired": "Du musst ein Qualitätsprofil auswählen", "components.Settings.SonarrModal.validationRootFolderRequired": "Du musst einen Stammordner auswählen", + "components.Settings.activeProfile": "Aktives Profil", + "components.Settings.addradarr": "Radarr-Server hinzufügen", + "components.Settings.address": "Adresse", + "components.Settings.addsonarr": "Sonarr-Server hinzufügen", + "components.Settings.advancedTooltip": "Bei falscher Konfiguration dieser Einstellung, kann dies zu einer Funktionsstörung führen", + "components.Settings.apikey": "API-Schlüssel", + "components.Settings.applicationTitle": "Anwendungstitel", + "components.Settings.applicationurl": "Anwendungs-URL", + "components.Settings.cacheImages": "Bild-Caching aktivieren", + "components.Settings.cacheImagesTip": "Alle Bilder Optimieren und lokal speichern (verbraucht viel Speicherplatz)", + "components.Settings.cancelscan": "Durchsuchung abbrechen", + "components.Settings.copied": "API-Schlüssel in die Zwischenablage kopiert.", + "components.Settings.csrfProtection": "Aktiviere CSRF Schutz", + "components.Settings.csrfProtectionHoverTip": "Aktiviere diese Option NICHT, es sei denn du weißt, was du tust!", + "components.Settings.csrfProtectionTip": "Macht den externen API Zugang schreibgeschützt (setzt HTTPS voraus und Jellyseerr muss neu gestartet werden, damit die Änderungen wirksam werden)", + "components.Settings.currentlibrary": "Aktuelle Bibliothek: {name}", + "components.Settings.default": "Standardmäßig", + "components.Settings.default4k": "Standard-4K", + "components.Settings.deleteServer": "{serverType} Server löschen", + "components.Settings.deleteserverconfirm": "Bist du sicher, dass du diesen Server löschen möchtest?", + "components.Settings.email": "E-Mail", + "components.Settings.enablessl": "SSL aktivieren", + "components.Settings.experimentalTooltip": "Die Aktivierung dieser Einstellung kann zu einem unerwarteten Verhalten der Anwendung führen", + "components.Settings.externalUrl": "Externe URL", + "components.Settings.general": "Allgemein", + "components.Settings.generalsettings": "Allgemeine Einstellungen", + "components.Settings.generalsettingsDescription": "Konfiguriere Globale und Standard Jellyseerr-Einstellungen.", + "components.Settings.hideAvailable": "Verfügbare Medien ausblenden", + "components.Settings.hostname": "Hostname oder IP-Adresse", + "components.Settings.is4k": "4K", + "components.Settings.librariesRemaining": "Verbleibende Bibliotheken: {count}", + "components.Settings.locale": "Sprache darstellen", + "components.Settings.manualscan": "Manuelle Bibliotheksdurchsuchung", + "components.Settings.manualscanDescription": "Normalerweise wird dies nur einmal alle 24 Stunden ausgeführt. Jellyseerr überprüft die kürzlich hinzugefügten Plex-Server aggressiver. Falls du Plex zum ersten Mal konfigurierst, wird eine einmalige vollständige manuelle Bibliotheksdurchsuchung empfohlen!", + "components.Settings.mediaTypeMovie": "Film", + "components.Settings.mediaTypeSeries": "Serie", + "components.Settings.menuAbout": "Über", + "components.Settings.menuGeneralSettings": "Allgemein", + "components.Settings.menuJobs": "Aufgaben und Cache", + "components.Settings.menuLogs": "Protokolle", + "components.Settings.menuNotifications": "Benachrichtigungen", + "components.Settings.menuPlexSettings": "Plex", + "components.Settings.menuServices": "Dienste", + "components.Settings.menuUsers": "Benutzer", + "components.Settings.noDefault4kServer": "Ein 4K {serverType} Server muss als Standart markiert werden um Nutzern zu ermöglichen 4K {mediaType} anfragen zu senden.", + "components.Settings.noDefaultNon4kServer": "Wenn du nur einen einzigen {serverType}-Server für Nicht-4K- und 4K-Inhalte hast (oder wenn du nur 4K-Inhalte herunterlädst), solltest du den {serverType}-Server NICHT als 4K-Server festgelegen.", + "components.Settings.noDefaultServer": "Mindestens ein {serverType}-Server muss als Standard markiert sein, damit {mediaType}-Anfragen verarbeitet werden können.", + "components.Settings.notificationAgentSettingsDescription": "Konfiguriere und aktiviere Benachrichtigungsagenten.", + "components.Settings.notifications": "Benachrichtigungen", + "components.Settings.notificationsettings": "Benachrichtigungseinstellungen", + "components.Settings.notrunning": "Nicht aktiv", + "components.Settings.originallanguage": "Sprache Entdecken", + "components.Settings.originallanguageTip": "Filtere Inhalte nach Originalsprache", + "components.Settings.partialRequestsEnabled": "Teilserienanfragen erlauben", + "components.Settings.plex": "Plex", + "components.Settings.plexlibraries": "Plex-Bibliotheken", + "components.Settings.plexlibrariesDescription": "Die Bibliotheken, welche Jellyseerr nach Titeln durchsucht. Richte deine Plex-Verbindungseinstellungen ein und speichere sie, klicke auf die Schaltfläche unten, wenn keine aufgeführt sind.", + "components.Settings.plexsettings": "Plex-Einstellungen", + "components.Settings.plexsettingsDescription": "Konfiguriere die Einstellungen für deinen Plex-Server. Jellyseerr durchsucht deine Plex-Bibliotheken, um festzustellen welche Inhalte verfügbar sind.", + "components.Settings.port": "Port", + "components.Settings.radarrsettings": "Radarr-Einstellungen", + "components.Settings.region": "Region Entdecken", + "components.Settings.regionTip": "Filtere Inhalte nach regionaler Verfügbarkeit", + "components.Settings.restartrequiredTooltip": "Jellyseerr muss neu gestartet werden, damit Änderungen an dieser Einstellung wirksam werden", + "components.Settings.scan": "Bibliotheken synchronisieren", + "components.Settings.scanning": "Synchronisieren…", + "components.Settings.serverLocal": "lokal", + "components.Settings.serverRemote": "entfernt", + "components.Settings.serverSecure": "Sicher", + "components.Settings.serverpreset": "Server", + "components.Settings.serverpresetLoad": "Drück den Knopf, um verfügbare Server zu laden", + "components.Settings.serverpresetManualMessage": "Manuelle Konfiguration", + "components.Settings.serverpresetRefreshing": "Rufe Server ab …", + "components.Settings.serviceSettingsDescription": "Konfiguriere unten deine {serverType}-Server. Du kannst mehrere {serverType}-Server verbinden, aber nur zwei davon können als Standard markiert werden (ein Nicht-4K- und ein 4K-Server). Administratoren können den Server überschreiben, auf dem neue Anfragen vor der Genehmigung verarbeitet werden.", + "components.Settings.services": "Dienstleistungen", + "components.Settings.settingUpPlexDescription": "Um Plex einzurichten, können die Daten manuell eintragen oder einen Server ausgewählt werden, welcher von plex.tv abgerufen wurde. Drück den Knopf rechts neben dem Dropdown-Menü, um die Liste der verfügbaren Server abzurufen.", "components.Settings.sonarrsettings": "Sonarr-Einstellungen", "components.Settings.ssl": "SSL", "components.Settings.startscan": "Durchsuchung starten", - "components.Settings.syncJellyfin": "Bibliotheken Synchronisieren", - "components.Settings.syncing": "Wird synchronisiert", "components.Settings.tautulliApiKey": "API-Schlüssel", "components.Settings.tautulliSettings": "Tautulli Einstellungen", "components.Settings.tautulliSettingsDescription": "Optional die Einstellungen für den Tautulli-Server konfigurieren. Jellyseerr holt die Überwachungsdaten für Ihre Plex-Medien von Tautulli.", - "components.Settings.timeout": "Zeitüberschreitung", "components.Settings.toastApiKeyFailure": "Bei der Generierung eines neuen API-Schlüssels ist etwas schief gelaufen.", "components.Settings.toastApiKeySuccess": "Neuer API-Schlüssel erfolgreich generiert!", "components.Settings.toastPlexConnecting": "Versuche mit Plex zu verbinden …", @@ -1055,43 +870,39 @@ "components.Settings.webAppUrlTip": "Leite Benutzer optional zur Web-App auf deinen Server statt zur „gehosteten“ Web-App weiter", "components.Settings.webhook": "Webhook", "components.Settings.webpush": "Web Push", - "components.Setup.configuremediaserver": "Konfiguriere Medien-Server", + "components.Setup.configureplex": "Plex konfigurieren", "components.Setup.configureservices": "Dienste konfigurieren", "components.Setup.continue": "Fortfahren", "components.Setup.finish": "Konfiguration beenden", - "components.Setup.finishing": "Fertigstellung…", + "components.Setup.finishing": "Fertigstellung …", "components.Setup.loginwithplex": "Mit Plex anmelden", "components.Setup.scanbackground": "Das Scannen läuft im Hintergrund. Du kannst in der Zwischenzeit das Setup fortsetzen.", "components.Setup.setup": "Einrichtung", - "components.Setup.signin": "Anmeldung", - "components.Setup.signinMessage": "Starte indem du dich anmeldest", - "components.Setup.signinWithJellyfin": "Nutze deinen {mediaServerName}-Account", - "components.Setup.signinWithPlex": "Nutze deinen Plex account", + "components.Setup.signinMessage": "Melde dich zunächst mit deinem Plex-Konto an", "components.Setup.tip": "Tipp", "components.Setup.welcome": "Willkommen bei Jellyseerr", "components.StatusBadge.managemedia": "{mediaType} verwalten", "components.StatusBadge.openinarr": "In {arr} öffnen", - "components.StatusBadge.playonplex": "Auf {mediaServerName} abspielen", + "components.StatusBadge.playonplex": "Auf Plex abspielen", "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}F{episodeNumber}", "components.StatusBadge.status": "{status}", "components.StatusBadge.status4k": "4K {status}", - "components.StatusChacker.newversionavailable": "Anwendungsaktualisierung", "components.StatusChacker.newversionDescription": "Jellyseerr wurde aktualisiert! Bitte klicke auf die Schaltfläche unten, um die Seite neu zu laden.", - "components.StatusChacker.reloadOverseerr": "Jellyseerr neu laden", + "components.StatusChacker.newversionavailable": "Anwendungsaktualisierung", + "components.StatusChacker.reloadJellyseerr": "Jellyseerr neu laden", "components.StatusChecker.appUpdated": "{applicationTitle} aktualisiert", "components.StatusChecker.appUpdatedDescription": "Klicke bitte auf die Schaltfläche unten, um die Anwendung neu zu laden.", "components.StatusChecker.reloadApp": "{applicationTitle} neu laden", "components.StatusChecker.restartRequired": "Server Neustart erforderlich", "components.StatusChecker.restartRequiredDescription": "Starte bitte den Server neu, um die aktualisierten Einstellungen zu übernehmen.", - "components.TitleCard.addToWatchList": "Zur Watchlist hinzufügen", "components.TitleCard.cleardata": "Daten löschen", "components.TitleCard.mediaerror": "{mediaType} wurde nicht gefunden", "components.TitleCard.tmdbid": "TMDB-ID", "components.TitleCard.tvdbid": "TheTVDB-ID", - "components.TitleCard.watchlistCancel": "watchlist für {title} abgebrochen.", - "components.TitleCard.watchlistDeleted": "{title} wurde erfolgreich von der Watchlist entfernt!", - "components.TitleCard.watchlistError": "Etwas ist schiefgelaufen. Versuche es erneut.", - "components.TitleCard.watchlistSuccess": "{title} wurde erfolgreich zur Watchlist hinzugefügt!", + "components.TvDetails.Season.noepisodes": "Liste der Episoden nicht verfügbar.", + "components.TvDetails.Season.somethingwentwrong": "Beim Datenabruf der Staffel ist etwas schief gelaufen.", + "components.TvDetails.TvCast.fullseriescast": "Komplette Serien Besetzung", + "components.TvDetails.TvCrew.fullseriescrew": "Komplette Serien-Crew", "components.TvDetails.anime": "Anime", "components.TvDetails.cast": "Besetzung", "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Episode} other {# Episoden}}", @@ -1105,15 +916,13 @@ "components.TvDetails.originaltitle": "Originaltitel", "components.TvDetails.overview": "Übersicht", "components.TvDetails.overviewunavailable": "Übersicht nicht verfügbar.", - "components.TvDetails.play": "Auf {mediaServerName} abspielen", - "components.TvDetails.play4k": "In 4K auf {mediaServerName} abspielen", + "components.TvDetails.play4konplex": "In 4K auf Plex abspielen", + "components.TvDetails.playonplex": "Auf Plex abspielen", "components.TvDetails.productioncountries": "Produktions {countryCount, plural, one {Land} other {Länder}}", "components.TvDetails.recommendations": "Empfehlungen", "components.TvDetails.reportissue": "Problem melden", "components.TvDetails.rtaudiencescore": "Rotten Tomatoes Publikumswertung", "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", - "components.TvDetails.Season.noepisodes": "Liste der Episoden nicht verfügbar.", - "components.TvDetails.Season.somethingwentwrong": "Beim Datenabruf der Staffel ist etwas schief gelaufen.", "components.TvDetails.seasonnumber": "Staffel {seasonNumber}", "components.TvDetails.seasons": "{seasonCount, plural, one {# Staffel} other {# Staffeln}}", "components.TvDetails.seasonstitle": "Staffeln", @@ -1122,8 +931,6 @@ "components.TvDetails.status4k": "4K {status}", "components.TvDetails.streamingproviders": "Streamt derzeit auf", "components.TvDetails.tmdbuserscore": "TMDB-Nutzerwertung", - "components.TvDetails.TvCast.fullseriescast": "Komplette Serien Besetzung", - "components.TvDetails.TvCrew.fullseriescrew": "Komplette Serien-Crew", "components.TvDetails.viewfullcrew": "Komplette Crew anzeigen", "components.TvDetails.watchtrailer": "Trailer ansehen", "components.UserList.accounttype": "Art", @@ -1140,19 +947,13 @@ "components.UserList.displayName": "Anzeigename", "components.UserList.edituser": "Benutzerberechtigungen Bearbeiten", "components.UserList.email": "E-Mail-Adresse", - "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {Benutzer} other {Benutzer}} erfolgreich importiert!", "components.UserList.importedfromplex": "{userCount} {userCount, Plural, ein {Benutzer} other {Benutzer}} Plex-Benutzer erfolgreich importiert!", - "components.UserList.importfromJellyfin": "Importiere {mediaServerName} Benutzer", - "components.UserList.importfromJellyfinerror": "Etwas ist schiefgelaufen beim Importieren von {mediaServerName}-Benutzern.", "components.UserList.importfrommediaserver": "{mediaServerName}-Benutzer importieren", "components.UserList.importfromplex": "Plex-Benutzer importieren", "components.UserList.importfromplexerror": "Beim Importieren von Plex-Benutzern ist etwas schief gelaufen.", "components.UserList.localLoginDisabled": "Die Einstellung Lokale Anmeldung aktivieren ist derzeit deaktiviert.", "components.UserList.localuser": "Lokaler Benutzer", - "components.UserList.mediaServerUser": "{mediaServerName} Benutzer", - "components.UserList.newJellyfinsigninenabled": "Die Aktiviere neuen {mediaServerName} Log-In Einstellung ist aktuell aktiviert. {mediaServerName}-Benutzer mit Zugriff auf die Bibliothek müssen nicht importiert werden um sich anzumelden.", "components.UserList.newplexsigninenabled": "Die Einstellung Aktiviere neuen Plex Log-In ist derzeit aktiviert. Plex-Benutzer mit Bibliothekszugang müssen nicht importiert werden, um sich anmelden zu können.", - "components.UserList.noJellyfinuserstoimport": "Es sind keine {mediaServerName}-Benutzer zum Importieren vorhanden.", "components.UserList.nouserstoimport": "Es gibt keine zu importierenden Plex-Benutzer.", "components.UserList.owner": "Besitzer", "components.UserList.password": "Passwort", @@ -1165,184 +966,384 @@ "components.UserList.totalrequests": "Anfragen", "components.UserList.user": "Benutzer", "components.UserList.usercreatedfailed": "Beim Erstellen des Benutzers ist etwas schief gelaufen.", - "components.UserList.usercreatedfailedexisting": "Die angegebene E-Mail-Adresse wird bereits von einem anderen Benutzer verwendet.", - "components.UserList.usercreatedsuccess": "Benutzer erfolgreich erstellt!", - "components.UserList.userdeleted": "Benutzer erfolgreich entfernt!", - "components.UserList.userdeleteerror": "Beim Löschen des Benutzers ist etwas schiefgelaufen.", - "components.UserList.userfail": "Beim Speichern der Benutzerberechtigungen ist ein Fehler aufgetreten.", - "components.UserList.userlist": "Benutzerliste", - "components.UserList.users": "Benutzer", + "i18n.edit": "Bearbeiten", + "i18n.experimental": "Experimentell", "components.UserList.userssaved": "Benutzerberechtigungen erfolgreich gespeichert!", + "i18n.advanced": "Erweitert", "components.UserList.validationEmail": "Du musst eine gültige E-Mail-Adresse angeben", - "components.UserList.validationpasswordminchars": "Passwort ist zu kurz. Es sind mindestens 8 Zeichen notwendig", - "components.UserProfile.emptywatchlist": "Hier erscheinen deine zur Plex Watchlist hinzugefügte Medien.", - "components.UserProfile.limit": "{remaining} von {limit}", - "components.UserProfile.localWatchlist": "{username}s Watchlist", - "components.UserProfile.movierequests": "Filmanfragen", - "components.UserProfile.pastdays": "{type} (vergangene {days} Tage)", - "components.UserProfile.plexwatchlist": "Plex Watchlist", - "components.UserProfile.ProfileHeader.joindate": "Mitglied seit dem {joindate}", - "components.UserProfile.ProfileHeader.profile": "Profil anzeigen", - "components.UserProfile.ProfileHeader.settings": "Einstellungen bearbeiten", - "components.UserProfile.ProfileHeader.userid": "Benutzer-ID: {userid}", - "components.UserProfile.recentlywatched": "Kürzlich angesehen", + "components.UserList.users": "Benutzer", "components.UserProfile.recentrequests": "Kürzliche Anfragen", - "components.UserProfile.requestsperdays": "{limit} verbleibend", - "components.UserProfile.seriesrequest": "Serienanfragen", - "components.UserProfile.totalrequests": "Anfragen insgesamt", - "components.UserProfile.unlimited": "Unbegrenzt", - "components.UserProfile.UserSettings.menuChangePass": "Passwort", - "components.UserProfile.UserSettings.menuGeneralSettings": "Allgemein", - "components.UserProfile.UserSettings.menuNotifications": "Benachrichtigungen", "components.UserProfile.UserSettings.menuPermissions": "Berechtigungen", - "components.UserProfile.UserSettings.unauthorizedDescription": "Sie haben keine Berechtigung, die Einstellungen dieses Benutzers zu ändern.", - "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "Kontotyp", + "components.UserProfile.UserSettings.menuNotifications": "Benachrichtigungen", + "components.UserProfile.UserSettings.menuGeneralSettings": "Allgemein", + "components.UserProfile.UserSettings.menuChangePass": "Passwort", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "Berechtigungen erfolgreich gespeichert!", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Beim Speichern der Einstellungen ist etwas schief gelaufen.", + "components.UserProfile.UserSettings.UserPermissions.permissions": "Berechtigungen", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Passwort ist zu kurz; es sollte mindestens 8 Zeichen lang sein", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "Du musst ein neues Passwort angeben", + "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "Du musst dein aktuelles Passwort angeben", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Passwörter mussen übereinstimmen", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "Du musst das neue Passwort bestätigen", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Passwort erfolgreich geändert!", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Beim Speichern des Passworts ist ein Fehler aufgetreten.", + "components.UserProfile.UserSettings.UserPasswordChange.password": "Passwort", + "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "Neues Passwort", + "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Aktuelles Passwort", + "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Passwort bestätigen", + "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Benachrichtigungseinstellungen", + "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "Benutzer-ID", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Einstellungen erfolgreich gespeichert!", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Beim Speichern der Einstellungen ist etwas schief gelaufen.", + "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex-Benutzer", + "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Lokaler Benutzer", + "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "Allgemeine Einstellungen", + "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Anzeigename", + "components.UserProfile.ProfileHeader.settings": "Einstellungen bearbeiten", + "components.UserProfile.ProfileHeader.profile": "Profil anzeigen", + "components.UserList.userfail": "Beim Speichern der Benutzerberechtigungen ist ein Fehler aufgetreten.", + "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Du musst eine gültige Benutzer-ID angeben", + "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Die ID Nummer für dein Benutzerkonto", + "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filtere Inhalte nach regionaler Verfügbarkeit", + "components.UserProfile.UserSettings.UserGeneralSettings.region": "Region Entdecken", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filtere Inhalte nach Originalsprache", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Sprache Entdecken", + "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "Sie haben keine Berechtigung, das Kennwort dieses Benutzers zu ändern.", + "components.UserProfile.UserSettings.UserGeneralSettings.user": "Benutzer", + "components.UserProfile.UserSettings.UserGeneralSettings.role": "Rolle", + "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Besitzer", "components.UserProfile.UserSettings.UserGeneralSettings.admin": "Admin", + "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "Kontotyp", + "i18n.loading": "Lade …", + "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "Du musst eine gültige Chat-ID angeben", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Starte einen Chat, füge @get_id_bot hinzu, und führe den Befehl /my_id aus", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat-ID", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Sende Benachrichtigungen ohne Ton", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Lautlos senden", + "components.UserProfile.ProfileHeader.userid": "Benutzer-ID: {userid}", + "components.UserProfile.ProfileHeader.joindate": "Mitglied seit dem {joindate}", + "components.UserProfile.UserSettings.unauthorizedDescription": "Sie haben keine Berechtigung, die Einstellungen dieses Benutzers zu ändern.", + "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": "Sie können Ihre eigenen Berechtigungen nicht ändern.", + "pages.errormessagewithcode": "{statusCode} - {error}", + "pages.somethingwentwrong": "Etwas ist schief gelaufen", + "pages.serviceunavailable": "Dienst nicht verfügbar", + "pages.pagenotfound": "Seite nicht gefunden", + "pages.internalservererror": "Interner Serverfehler", + "i18n.usersettings": "Benutzereinstellungen", + "i18n.settings": "Einstellungen", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Beim Speichern des Passworts ist ein Fehler aufgetreten. Wurde dein aktuelles Passwort korrekt eingegeben?", + "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Benachrichtigungen", + "components.UserProfile.UserSettings.UserGeneralSettings.general": "Allgemein", + "i18n.delimitedlist": "{a}, {b}", + "i18n.view": "Anzeigen", + "i18n.tvshow": "Serie", + "i18n.testing": "Testen…", + "i18n.test": "Test", + "i18n.status": "Status", + "i18n.showingresults": "Zeige {from} bis {to} von {total} Ergebnissen", + "i18n.saving": "Speichern…", + "i18n.save": "Änderungen speichern", + "i18n.retrying": "Wiederholen…", + "i18n.resultsperpage": "Zeige {pageSize} Ergebnisse pro Seite", + "i18n.requesting": "Anfordern…", + "i18n.request4k": "In 4K anfragen", + "i18n.previous": "Bisherige", + "i18n.notrequested": "Nicht Angefragt", + "i18n.noresults": "Keine Ergebnisse.", + "i18n.next": "Weiter", + "i18n.movie": "Film", + "i18n.canceling": "Abbrechen…", + "i18n.back": "Zurück", + "i18n.areyousure": "Bist du sicher?", + "i18n.all": "Alle", + "components.UserProfile.totalrequests": "Anfragen insgesamt", + "components.UserProfile.seriesrequest": "Serienanfragen", + "components.UserProfile.pastdays": "{type} (vergangene {days} Tage)", + "components.UserProfile.movierequests": "Filmanfragen", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Für dein Konto ist derzeit kein Passwort festgelegt. Konfiguriere unten ein Passwort, um die Anmeldung als \"lokaler Benutzer\" mit deiner E-Mail-Adresse zu aktivieren.", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Für dieses Benutzerkonto ist derzeit kein Kennwort festgelegt. Konfiguriere unten ein Kennwort, damit sich dieses Konto als \"lokaler Benutzer\" anmelden kann", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "Du musst einen gültigen öffentlichen PGP-Schlüssel angeben", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Telegram-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Die Einstellungen für die Telegram-Benachrichtigung konnten nicht gespeichert werden.", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "Verschlüssele E-Mail-Nachrichten mit OpenPGP", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "Öffentlicher PGP-Schlüssel", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "E-Mail-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": "E-Mail-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", + "components.UserProfile.UserSettings.UserNotificationSettings.email": "E-Mail", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "Discord-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Die Einstellungen für die Discord-Benachrichtigung konnten nicht gespeichert werden.", + "components.UserProfile.unlimited": "Unbegrenzt", + "components.UserProfile.requestsperdays": "{limit} verbleibend", + "components.UserProfile.limit": "{remaining} von {limit}", + "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Serienanfragenlimit", + "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Filmanfragenlimit", + "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Überschreibe globales Limit", + "components.UserList.usercreatedfailedexisting": "Die angegebene E-Mail-Adresse wird bereits von einem anderen Benutzer verwendet.", + "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Standard ({language})", + "components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push", "components.UserProfile.UserSettings.UserGeneralSettings.applanguage": "Sprache darstellen", "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "Discord User ID", "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "Die mehrstellige ID-Nummer Deines Discord-Accounts", - "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Anzeigename", - "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-Mail-Adresse", - "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Überschreibe globales Limit", - "components.UserProfile.UserSettings.UserGeneralSettings.general": "Allgemein", - "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "Allgemeine Einstellungen", - "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Standard ({language})", - "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Lokaler Benutzer", - "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} Benutzer", - "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Filmanfragenlimit", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Sprache Entdecken", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filtere Inhalte nach Originalsprache", - "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Besitzer", - "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex-Benutzer", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Filme automatisch anfragen", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Automatisch Filme auf deiner Plex Watchlist anfordern", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Serien automatisch anfragen", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Automatisch Serien auf deiner Plex Watchlist anfragen", - "components.UserProfile.UserSettings.UserGeneralSettings.region": "Region Entdecken", - "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filtere Inhalte nach regionaler Verfügbarkeit", - "components.UserProfile.UserSettings.UserGeneralSettings.role": "Rolle", - "components.UserProfile.UserSettings.UserGeneralSettings.save": "Änderungen Speichern", - "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Wird gespeichert...", - "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Serienanfragenlimit", - "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Beim Speichern der Einstellungen ist etwas schief gelaufen.", - "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Einstellungen erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserGeneralSettings.user": "Benutzer", "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "Du musst eine gültige Discord User ID angeben", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Geräte Standard", - "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "Benutzer-ID", - "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Die ID Nummer für dein Benutzerkonto", - "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Die Einstellungen für die Discord-Benachrichtigung konnten nicht gespeichert werden.", - "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "Discord-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserNotificationSettings.email": "E-Mail", - "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": "E-Mail-Benachrichtigungseinstellungen konnten nicht gespeichert werden.", - "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "E-Mail-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Benachrichtigungen", - "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Benachrichtigungseinstellungen", - "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "Öffentlicher PGP-Schlüssel", - "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "Verschlüssele E-Mail-Nachrichten mit OpenPGP", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessToken": "Zugangs-Token", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "Erstelle ein Token aus deinen Kontoeinstellungen", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": "Die Einstellungen für Pushbullet-Benachrichtigungen konnten nicht gespeichert werden.", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Pushbullet-Benachrichtigungseinstellungen erfolgreich gespeichert!", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Anwendungs-API-Token", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "Register eine Anwendung zur Verwendung mit {applicationTitle}", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Die Einstellungen für die Pushover-Benachrichtigung konnten nicht gespeichert werden.", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "Pushover-Benachrichtigungseinstellungen erfolgreich gespeichert!", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "Benutzer- oder Gruppenschlüssel", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "Die 30-stellige Benutzer- oder Gruppenkennung", - "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Lautlos senden", - "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Sende Benachrichtigungen ohne Ton", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Benachrichtigungston", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat-ID", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Starte einen Chat, füge @get_id_bot hinzu, und führe den Befehl /my_id aus", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Die Einstellungen für die Telegram-Benachrichtigung konnten nicht gespeichert werden.", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Telegram-Benachrichtigungseinstellungen erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Du musst eine gültige Benutzer-ID angeben", - "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "Du musst einen gültigen öffentlichen PGP-Schlüssel angeben", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Die Einstellungen für die Pushover-Benachrichtigung konnten nicht gespeichert werden.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "Pushover-Benachrichtigungseinstellungen erfolgreich gespeichert!", "components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "Ein Zugriffstoken muss angegeben werden", "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "Sie müssen ein gültiges Anwendungs-Token angeben", "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Du musst einen gültigen Benutzer- oder Gruppenschlüssel angeben", - "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "Du musst eine gültige Chat-ID angeben", - "components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Web push Benachrichtigungseinstellungen konnten nicht gespeichert werden.", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Web push Benachrichtigungseinstellungen wurden erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Passwort bestätigen", - "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Aktuelles Passwort", - "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "Neues Passwort", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Für dieses Benutzerkonto ist derzeit kein Kennwort festgelegt. Konfiguriere unten ein Kennwort, damit sich dieses Konto als \"lokaler Benutzer\" anmelden kann", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Für dein Konto ist derzeit kein Passwort festgelegt. Konfiguriere unten ein Passwort, um die Anmeldung als \"lokaler Benutzer\" mit deiner E-Mail-Adresse zu aktivieren.", - "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "Sie haben keine Berechtigung, das Kennwort dieses Benutzers zu ändern.", - "components.UserProfile.UserSettings.UserPasswordChange.password": "Passwort", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Beim Speichern des Passworts ist ein Fehler aufgetreten.", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Beim Speichern des Passworts ist ein Fehler aufgetreten. Wurde dein aktuelles Passwort korrekt eingegeben?", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Passwort erfolgreich geändert!", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "Du musst das neue Passwort bestätigen", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Passwörter mussen übereinstimmen", - "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "Du musst dein aktuelles Passwort angeben", - "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "Du musst ein neues Passwort angeben", - "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Passwort ist zu kurz; es sollte mindestens 8 Zeichen lang sein", - "components.UserProfile.UserSettings.UserPermissions.permissions": "Berechtigungen", - "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Beim Speichern der Einstellungen ist etwas schief gelaufen.", - "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "Berechtigungen erfolgreich gespeichert!", - "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": "Sie können Ihre eigenen Berechtigungen nicht ändern.", - "i18n.advanced": "Erweitert", - "i18n.all": "Alle", - "i18n.approve": "Genehmigen", - "i18n.approved": "Genehmigt", - "i18n.areyousure": "Bist du sicher?", - "i18n.available": "Vorhanden", - "i18n.back": "Zurück", - "i18n.cancel": "Abbrechen", - "i18n.canceling": "Wird abgebrochen...", - "i18n.close": "Schließen", + "i18n.resolved": "Gelöst", + "i18n.importing": "Importieren…", + "i18n.import": "Importieren", + "components.UserProfile.recentlywatched": "Kürzlich angesehen", + "i18n.restartRequired": "Neustart erforderlich", + "components.UserProfile.emptywatchlist": "Hier erscheinen deine zur Plex Watchlist hinzugefügte Medien.", + "components.UserProfile.plexwatchlist": "Plex Watchlist", + "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Serien", + "components.Discover.moviegenres": "Film Genre", + "components.Discover.studios": "Studios", + "components.Discover.tmdbmoviegenre": "TMDB Film Genre", + "components.Discover.tmdbtvgenre": "TMDB Serien Genre", + "components.Discover.tmdbtvkeyword": "TMDB Serien Keyword", + "components.Discover.tvgenres": "Serien Genre", + "components.Settings.SettingsMain.apikey": "API Schlüssel", + "components.Settings.SettingsMain.csrfProtection": "Aktivere CSRF Schutz", + "components.Settings.SettingsMain.applicationTitle": "Anwendungstitel", + "components.Settings.SettingsMain.csrfProtectionTip": "Limitiere externen API Zugriff auf Lese-Operationen (erfordert HTTPS)", + "components.Settings.SettingsMain.general": "Allgemein", + "components.Settings.SettingsMain.generalsettings": "Allgemeine Einstellungen", + "components.Settings.SettingsMain.locale": "Anzeigesprache", + "components.Settings.SettingsMain.hideAvailable": "Verfügbare Medien ausblenden", + "components.Settings.SettingsMain.toastApiKeySuccess": "Neuer API Schlüssel erfolgreich generiert!", + "components.Settings.SettingsMain.trustProxy": "Proxyunterstützung aktivieren", + "components.Settings.SettingsMain.validationApplicationUrl": "Du musst eine valide URL spezifizieren", + "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "Die URL darf nicht mit einem Slash \"/\" enden", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filme", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "Deine Plex Watchlist", + "components.Discover.tmdbsearch": "TMDB Suche", + "components.Settings.SettingsMain.toastApiKeyFailure": "Etwas ist schiefgelaufen während der Generierung eines neuen API Schlüssels.", + "components.Settings.SettingsMain.toastSettingsSuccess": "Einstellungen erfolgreich gespeichert!", + "components.Discover.tmdbmoviekeyword": "TMDB Film Keyword", + "components.Settings.SettingsMain.validationApplicationTitle": "Du musst einen Anwendungstitel spezifizieren", + "components.Discover.PlexWatchlistSlider.emptywatchlist": "Medien in deiner Plex Watchlist erscheinen hier.", + "components.Settings.SettingsMain.cacheImagesTip": "Cache extern gehostete Bilder (erfordert eine beträchtliche Menge an Speicherplatz)", + "components.Settings.SettingsMain.csrfProtectionHoverTip": "Aktiviere diese Einstellung nur wenn du weißt was du tust!", + "components.Discover.networks": "Sender", + "components.Discover.tmdbstudio": "TMDB Studio", + "components.Settings.SettingsMain.applicationurl": "Anwendung URL", + "components.Settings.SettingsMain.cacheImages": "Bild-Caching aktivieren", + "components.Settings.SettingsMain.generalsettingsDescription": "Konfiguration der globalen und Standardeinstellungen für Jellyseerr.", + "components.Settings.SettingsMain.originallanguage": "\"Entdecken\" Sprache", + "components.Settings.SettingsMain.partialRequestsEnabled": "Teilweise Serienanfragen zulassen", + "components.Settings.SettingsMain.region": "\"Entdecken\" Region", + "components.Settings.SettingsMain.toastSettingsFailure": "Beim Speichern der Einstellungen ist ein Fehler aufgetreten.", + "components.Settings.SettingsMain.regionTip": "Inhalte nach regionaler Verfügbarkeit filtern", + "components.Discover.tmdbnetwork": "TMDB Sender", + "components.Settings.SettingsMain.originallanguageTip": "Inhalt nach Originalsprache filtern", + "components.Settings.SettingsMain.trustProxyTip": "Erlaube Jellyseerr, Client-IP-Adressen hinter einem Proxy korrekt zu registrieren", + "components.Discover.CreateSlider.addSlider": "Slider hinzufügen", + "components.Discover.CreateSlider.addcustomslider": "Benutzerdefinierten Slider erstellen", + "components.Discover.CreateSlider.addfail": "Neuer Slider konnte nicht erstellt werden.", + "components.Discover.CreateSlider.addsuccess": "Ein neuer Slider wurde erstellt und die Einstellungen wurden gespeichert.", + "components.Discover.CreateSlider.editSlider": "Slider bearbeiten", + "components.Discover.CreateSlider.editfail": "Slider konnte nicht bearbeitet werden.", + "components.Discover.CreateSlider.editsuccess": "Slider bearbeitet und Einstellung gespeichert.", + "components.Discover.CreateSlider.needresults": "Es muss mindestens 1 Ergebnis vorhanden sein.", + "components.Layout.Sidebar.browsemovies": "Filme", + "components.Layout.Sidebar.browsetv": "Serien", + "components.Discover.CreateSlider.nooptions": "Keine Ergebnisse.", + "components.Discover.CreateSlider.providetmdbgenreid": "Hinterlege eine TMDB Genre ID", + "components.Discover.CreateSlider.providetmdbkeywordid": "Hinterlege eine TMDB Keyword ID", + "components.Discover.CreateSlider.providetmdbnetwork": "Hinterlege eine TMDB Network ID", + "components.Discover.CreateSlider.providetmdbsearch": "Geben Sie eine Suchanfrage an", + "components.Discover.CreateSlider.validationTitlerequired": "Du musst einen Titel eingeben.", + "components.Discover.DiscoverSliderEdit.remove": "Entfernen", + "components.Discover.DiscoverSliderEdit.deletefail": "Slider konnte nicht gelöscht werden.", + "components.Discover.DiscoverSliderEdit.deletesuccess": "Slider erfolgreich entfernt.", + "components.Discover.DiscoverMovies.discovermovies": "Filme", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Erscheinungsdatum Aufsteigend", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Erscheinungsdatum Absteigend", + "components.Discover.DiscoverMovies.sortTitleAsc": "Titel (A-Z) Aufsteigend", + "components.Discover.DiscoverMovies.sortTitleDesc": "Titel (Z-A) Absteigend", + "components.Discover.DiscoverTv.discovertv": "Serien", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Erstausstrahlung (Absteigend)", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Erstausstrahlung (Aufsteigend)", + "components.Discover.DiscoverTv.sortPopularityAsc": "Beliebtheit (Aufsteigend)", + "components.Discover.DiscoverTv.sortPopularityDesc": "Beliebtheit (Absteigend)", + "components.Discover.CreateSlider.slidernameplaceholder": "Name des Slider", + "components.Settings.SettingsJobsCache.availability-sync": "Medienverfügbarkeit Sync", + "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", + "components.Discover.FilterSlideover.originalLanguage": "Originalsprache", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Alle {jobScheduleSeconds, plural, one {Sekunde} other {{jobScheduleSeconds} Sekunden}}", + "components.Discover.updatefailed": "Bei der Aktualisierung der Entdecken-Einstellungen ist ein Fehler aufgetreten.", + "components.Selector.searchGenres": "Genres auswählen…", + "components.Discover.updatesuccess": "Die Einstellungen für die Entdecken-Anpassung wurden aktualisiert.", + "components.Selector.showmore": "Mehr anzeigen", + "components.Selector.starttyping": "Start der Suche durch Tippen.", + "components.Selector.showless": "Weniger anzeigen", + "components.Discover.CreateSlider.providetmdbstudio": "TMDB Studio ID angeben", + "components.Discover.CreateSlider.searchGenres": "Genres suchen…", + "components.Discover.CreateSlider.searchKeywords": "Stichwörter suchen…", + "components.Discover.CreateSlider.searchStudios": "Studios suchen…", + "components.Discover.CreateSlider.starttyping": "Start der Suche durch Tippen.", + "components.Discover.CreateSlider.validationDatarequired": "Du musst einen Datenwert angeben.", + "components.Discover.DiscoverSliderEdit.enable": "Sichtbarkeit umschalten", + "components.Discover.customizediscover": "Discover anpassen", + "components.Discover.resetfailed": "Beim Zurücksetzen der Entdecken-Einstellungen ist etwas schief gegangen.", + "components.Discover.resetsuccess": "Die Entdecken-Einstellungen wurden erfolgreich zurückgesetzt.", + "components.Discover.stopediting": "Bearbeitung stoppen", + "components.Discover.resettodefault": "Zurücksetzen auf Standard", + "components.Discover.resetwarning": "Setzt alle Slider auf die Standardwerte zurück. Dadurch werden auch alle benutzerdefinierten Slider gelöscht!", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", + "components.Discover.DiscoverMovies.sortPopularityAsc": "Beliebtheit aufsteigend", + "components.Discover.DiscoverMovies.sortPopularityDesc": "Beliebtheit absteigend", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMDB-Bewertung aufsteigend", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMDB-Bewertung Absteigend", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Aktiver Filter} other {# Aktive Filter}}", + "components.Discover.DiscoverTv.sortTitleAsc": "Titel (A-Z) Aufsteigend", + "components.Discover.DiscoverTv.sortTitleDesc": "Titel (Z-A) Absteigend", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMDB-Bewertung aufsteigend", + "components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMDB-Bewertung Absteigend", + "components.Discover.FilterSlideover.clearfilters": "Aktive Filter löschen", + "components.Discover.FilterSlideover.filters": "Filter", + "components.Discover.FilterSlideover.firstAirDate": "Datum der Erstausstrahlung", + "components.Discover.FilterSlideover.from": "Vom", + "components.Discover.FilterSlideover.genres": "Genres", + "components.Discover.FilterSlideover.keywords": "Stichwörter", + "components.Discover.FilterSlideover.ratingText": "Bewertungen zwischen {minValue} und {maxValue}", + "components.Discover.FilterSlideover.releaseDate": "Erscheinungsdatum", + "components.Discover.FilterSlideover.runtime": "Laufzeit", + "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} Minuten Laufzeit", + "components.Discover.FilterSlideover.tmdbuserscore": "TMDB-Benutzerbewertung", + "components.Discover.FilterSlideover.to": "Bis", + "components.Discover.createnewslider": "Neuen Slider erstellen", + "components.Discover.FilterSlideover.studio": "Studio", + "components.Discover.FilterSlideover.streamingservices": "Streaming-Dienste", + "components.Selector.nooptions": "Keine Ergebnisse.", + "components.Selector.searchKeywords": "Stichwörter suchen…", + "components.Selector.searchStudios": "Studios suchen…", + "components.Discover.tmdbmoviestreamingservices": "TMDB Film-Streaming-Dienste", + "components.Discover.tmdbtvstreamingservices": "TMDB TV-Streaming-Dienste", "i18n.collection": "Sammlung", + "components.Discover.FilterSlideover.tmdbuservotecount": "Anzahl an TMDB Benutzerbewertungen", + "components.Settings.RadarrModal.tagRequestsInfo": "Füge automatisch ein Tag hinzu mit der ID und dem Namen des anfordernden Nutzers", + "components.MovieDetails.imdbuserscore": "IMDB Nutzer Bewertung", + "components.Settings.SonarrModal.tagRequests": "Tag Anforderungen", + "components.Discover.FilterSlideover.voteCount": "Anzahl Abstimmungen zwischen {minValue} und {maxValue}", + "components.Settings.SonarrModal.tagRequestsInfo": "Füge automatisch einen zusätzlichen Tag mit der ID & Namen des anfordernden Nutzers", + "components.Layout.UserWarnings.passwordRequired": "Ein Passwort ist erforderlich.", + "components.Login.description": "Da du dich zum ersten Mal bei {applicationName} anmeldest, musst du eine gültige E-Mail-Adresse angeben.", + "components.Layout.UserWarnings.emailRequired": "E-Mail ist erforderlich.", + "components.Layout.UserWarnings.emailInvalid": "E-Mail ist nicht valide.", + "components.Login.credentialerror": "Der Benutzername oder das Passwort ist falsch.", + "components.Login.emailtooltip": "Die Adresse muss nicht mit Ihrer {mediaServerName}-Instanz verbunden sein.", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Verbinde", + "components.Login.initialsigningin": "Verbinden…", + "components.Login.save": "hinzufügen", + "components.Login.saving": "Hinzufügen…", + "components.Login.signinwithjellyfin": "Verwende dein {mediaServerName} Konto", + "components.Login.title": "E-Mail hinzufügen", + "components.Login.username": "Benutzername", + "components.Login.validationEmailFormat": "Ungültige E-Mail", + "components.Login.validationEmailRequired": "Du musst eine E-Mail angeben", + "components.Login.validationemailformat": "Gültige E-Mail erforderlich", + "components.Login.validationhostformat": "Gültige URL erforderlich", + "components.Login.validationhostrequired": "{mediaServerName} URL erforderlich", + "components.Login.validationusernamerequired": "Benutzername erforderlich", + "components.ManageSlideOver.removearr": "Aus {arr} entfernen", + "components.ManageSlideOver.removearr4k": "Aus 4K {arr} entfernen", + "components.MovieDetails.downloadstatus": "Download-Status", + "components.MovieDetails.openradarr4k": "Film in 4K Radarr öffnen", + "components.MovieDetails.play": "Wiedergabe auf {mediaServerName}", + "components.MovieDetails.play4k": "4K abspielen auf {mediaServerName}", + "components.Settings.SonarrModal.animeSeriesType": "Anime-Serien Typ", + "components.Settings.jellyfinSettings": "{mediaServerName} Einstellungen", + "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} Einstellungen erfolgreich gespeichert!", + "components.Settings.jellyfinlibraries": "{mediaServerName} Bibliotheken", + "components.Settings.jellyfinsettings": "{mediaServerName} Einstellungen", + "components.Settings.jellyfinsettingsDescription": "Konfiguriere die Einstellungen für deinen {mediaServerName} Server. {mediaServerName} scannt deine {mediaServerName} Bibliotheken, um zu sehen, welche Inhalte verfügbar sind.", + "components.Settings.manualscanJellyfin": "Manuelles Scannen der Bibliothek", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.saving": "Speichern…", + "components.Settings.syncing": "Synchronisierung", + "components.Settings.timeout": "Zeitüberschreitung", + "components.Setup.signin": "Anmelden", + "components.Setup.signinWithJellyfin": "Verwende dein {mediaServerName} Konto", + "components.Setup.signinWithPlex": "Verwende dein Plex-Konto", + "components.TitleCard.watchlistDeleted": "{title} Erfolgreich von der Beobachtungsliste entfernt!", + "components.TitleCard.watchlistError": "Etwas ist schief gelaufen, versuche es noch einmal.", + "components.TitleCard.watchlistSuccess": "{title} erfolgreich zur Beobachtungsliste hinzugefügt!", + "components.TvDetails.play": "Wiedergabe auf {mediaServerName}", + "components.TvDetails.play4k": "4K abspielen auf {mediaServerName}", + "components.UserList.importfromJellyfin": "Importieren von {mediaServerName} Benutzern", + "components.UserList.mediaServerUser": "{mediaServerName} Benutzer", + "components.UserList.noJellyfinuserstoimport": "Es gibt keine {mediaServerName} Benutzer zu importieren.", + "components.UserList.userdeleted": "Benutzer erfolgreich gelöscht!", + "components.UserList.userdeleteerror": "Beim Löschen des Benutzers ist etwas schief gelaufen.", + "components.UserList.userlist": "Benutzerliste", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-Mail", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} Benutzer", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Speichern…", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Web-Push-Benachrichtigungseinstellungen erfolgreich gespeichert!", + "i18n.close": "Schließen", "i18n.decline": "Ablehnen", "i18n.declined": "Abgelehnt", "i18n.delete": "Löschen", - "i18n.deleting": "Wird gelöscht...", - "i18n.delimitedlist": "{a}, {b}", - "i18n.edit": "Bearbeiten", - "i18n.experimental": "Experimentell", + "i18n.deleting": "Löschen…", "i18n.failed": "Fehlgeschlagen", - "i18n.import": "Importieren", - "i18n.importing": "Wird importiert…", - "i18n.loading": "Wird geladen…", - "i18n.movie": "Film", "i18n.movies": "Filme", - "i18n.next": "Weiter", - "i18n.noresults": "Keine Ergebnisse.", - "i18n.notrequested": "Nicht Angefragt", "i18n.open": "Öffnen", - "i18n.partiallyavailable": "Teilweise Verfügbar", "i18n.pending": "Ausstehend", - "i18n.previous": "Bisherige", - "i18n.processing": "Wird verarbeitet...", - "i18n.request": "Anfragen", - "i18n.request4k": "In 4K anfragen", + "i18n.processing": "Verarbeitung", + "i18n.request": "Anfrage", "i18n.requested": "Angefragt", - "i18n.requesting": "Wird angefordert...", - "i18n.resolved": "Gelöst", - "i18n.restartRequired": "Neustart erforderlich", - "i18n.resultsperpage": "Zeige {pageSize} Ergebnisse pro Seite", "i18n.retry": "Wiederholen", - "i18n.retrying": "Wird wiederholt…", - "i18n.save": "Änderungen speichern", - "i18n.saving": "Wird gespeichert…", - "i18n.settings": "Einstellungen", - "i18n.showingresults": "Zeige {from} bis {to} von {total} Ergebnissen", - "i18n.status": "Status", - "i18n.test": "Testen", - "i18n.testing": "Wird getestet…", - "i18n.tvshow": "Serie", - "i18n.tvshows": "Serien", - "i18n.unavailable": "Nicht Verfügbar", - "i18n.usersettings": "Benutzereinstellungen", - "i18n.view": "Anzeigen", - "pages.errormessagewithcode": "{statusCode} - {error}", - "pages.internalservererror": "Interner Serverfehler", - "pages.oops": "Hoppla", - "pages.pagenotfound": "Seite nicht gefunden", - "pages.returnHome": "Zurück zur Startseite", - "pages.serviceunavailable": "Dienst nicht verfügbar", - "pages.somethingwentwrong": "Etwas ist schief gelaufen" + "i18n.tvshows": "Serie", + "i18n.unavailable": "Nicht verfügbar", + "pages.oops": "Huch!", + "components.MovieDetails.openradarr": "Film in Radarr öffnen", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Gerätestandard", + "components.Settings.RadarrModal.tagRequests": "Tag-Anfragen", + "components.Settings.SettingsAbout.supportjellyseerr": "Jellyseerr unterstützen", + "components.Settings.internalUrl": "Interne URL", + "components.Settings.jellyfinSettingsDescription": "Konfiguriere optional die internen und externen Endpunkte für deinen {mediaServerName} Server. In den meisten Fällen ist die externe URL eine andere als die interne URL. Für die Anmeldung bei {mediaServerName} kann auch eine benutzerdefinierte URL zum Zurücksetzen des Passworts festgelegt werden, falls du auf eine andere Seite zum Zurücksetzen des Passworts umleiten möchtest.", + "components.Settings.jellyfinSettingsFailure": "Beim Speichern der Einstellungen von {mediaServerName} ist ein Fehler aufgetreten.", + "components.Settings.manualscanDescriptionJellyfin": "Normalerweise wird dieser Vorgang nur einmal alle 24 Stunden durchgeführt. Jellyseerr wird die kürzlich hinzugefügten Bibliotheken deines {mediaServerName} Servers aggressiver überprüfen. Wenn dies das erste Mal ist, dass du Jellyseerr konfigurierst, wird ein einmaliger vollständiger manueller Bibliotheks-Scan empfohlen!", + "components.Settings.save": "Änderungen speichern", + "components.Settings.Notifications.userEmailRequired": "Benutzer-E-Mai", + "components.Settings.Notifications.NotificationsPushover.sound": "Benachrichtigungston", + "components.Settings.SonarrModal.seriesType": "TV-Serie Typ", + "components.Settings.jellyfinlibrariesDescription": "Die Bibliotheken {mediaServerName} werden nach Titeln durchsucht. Klicke auf die Schaltfläche unten, wenn keine Bibliotheken aufgelistet sind.", + "components.UserList.importfromJellyfinerror": "Beim Importieren von {mediaServerName} Benutzern ist etwas schief gelaufen.", + "components.Settings.syncJellyfin": "Bibliotheken synchronisieren", + "components.Setup.configuremediaserver": "Medienserver konfigurieren", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Änderungen speichern", + "i18n.available": "Verfügbar", + "i18n.cancel": "Abbrechen", + "components.TitleCard.addToWatchList": "Zur Beobachtungsliste hinzufügen", + "components.TitleCard.watchlistCancel": "Überwachungsliste für {title} abgebrochen.", + "components.UserList.usercreatedsuccess": "Benutzer erfolgreich angelegt!", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Dadurch wird dieser {mediaType} unwiderruflich aus {arr} entfernt, einschließlich aller Dateien.", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} erfolgreich importiert!", + "components.UserList.validationpasswordminchars": "Das Passwort ist zu kurz; es sollte mindestens 8 Zeichen lang sein", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Gerätestandard", + "i18n.approve": "Genehmigen", + "i18n.partiallyavailable": "Teilweise verfügbar", + "components.UserList.newJellyfinsigninenabled": "Die Einstellung Enable New {mediaServerName} Sign-In ist derzeit aktiviert. {mediaServerName}-Benutzer mit Bibliothekszugang müssen nicht importiert werden, um sich anmelden zu können.", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Benachrichtigungston", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Die Einstellungen für Web-Push-Benachrichtigungen konnten nicht gespeichert werden.", + "components.UserProfile.localWatchlist": "Beobachtungsliste von {username}", + "i18n.approved": "Genehmigt", + "pages.returnHome": "Zurück nach Hause" } diff --git a/src/i18n/locale/es.json b/src/i18n/locale/es.json index cf82f69c..2790e6a0 100644 --- a/src/i18n/locale/es.json +++ b/src/i18n/locale/es.json @@ -201,7 +201,7 @@ "components.Settings.SonarrModal.animerootfolder": "Carpeta raíz de anime", "components.Settings.SonarrModal.animequalityprofile": "Perfil de calidad de anime", "components.Settings.SettingsAbout.timezone": "Zona horaria", - "components.Settings.SettingsAbout.supportoverseerr": "Apoya a Jellyseerr", + "components.Settings.SettingsAbout.supportoverseerr": "Apoya a Overseerr", "components.Settings.SettingsAbout.helppaycoffee": "Ayúdame invitándome a un café", "components.Settings.SettingsAbout.Releases.viewongithub": "Ver en GitHub", "components.Settings.SettingsAbout.Releases.viewchangelog": "Ver registro de cambios", @@ -299,14 +299,14 @@ "components.RequestButton.viewrequest": "Ver Solicitud", "components.RequestButton.requestmore4k": "Solicitar más en 4K", "components.RequestButton.requestmore": "Solicitar más", - "components.RequestButton.declinerequests": "Rechazar {requestCount, plural, one {solicitud} other {{requestCount} solicitudes}}", + "components.RequestButton.declinerequests": "Rechazar {requestCount, plural, one {Request} other {{requestCount} Requests}}", "components.RequestButton.declinerequest4k": "Rechazar Solicitud 4K", "components.RequestButton.declinerequest": "Rechazar Solicitud", - "components.RequestButton.decline4krequests": "Rechazar {requestCount, plural, one {solicitud en 4K} other {{requestCount} solicitudes en 4K}}", - "components.RequestButton.approverequests": "Aprobar {requestCount, plural, one {solicitud} other {{requestCount} solicitudes}}", + "components.RequestButton.decline4krequests": "Rechazar {requestCount, plural, one {4K Request} other {{requestCount} 4K Requests}}", + "components.RequestButton.approverequests": "Aprobar {requestCount, plural, one {Request} other {{requestCount} Requests}}", "components.RequestButton.approverequest4k": "Aprobar Solicitud 4K", "components.RequestButton.approverequest": "Aprobar Solicitud", - "components.RequestButton.approve4krequests": "Aprobar {requestCount, plural, one {petición en 4K} other {requestCount} peticiones en 4K}}", + "components.RequestButton.approve4krequests": "Aprobar {requestCount, plural, one {4K Request} other {{requestCount} 4K Requests}}", "components.RequestBlock.server": "Servidor de Destino", "components.RequestBlock.rootfolder": "Carpeta Raíz", "components.RequestBlock.profilechanged": "Perfil de Calidad", @@ -656,7 +656,7 @@ "components.QuotaSelector.unlimited": "Ilimitadas", "components.MovieDetails.originaltitle": "Título Original", "components.LanguageSelector.originalLanguageDefault": "Todos los Idiomas", - "components.LanguageSelector.languageServerDefault": "({Language}) por defecto", + "components.LanguageSelector.languageServerDefault": "({language}) por defecto", "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {cambio} other {cambios}} por detrás", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Tu cuenta no tiene configurada una contraseña actualmente. Configure una contraseña a continuación para habilitar el acceso como \"usuario local\" utilizando tu dirección de email.", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Esta cuenta de usuario no tiene configurada una contraseña actualmente. Configure una contraseña a continuación para habilitar el acceso como \"usuario local\"", @@ -784,7 +784,7 @@ "components.DownloadBlock.estimatedtime": "Estimación de {time}", "components.Settings.Notifications.encryptionOpportunisticTls": "Usa siempre STARTTLS", "components.TvDetails.streamingproviders": "Emisión Actual en", - "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "{{Language}} por defecto", + "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "({language}) por defecto", "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "Para recibir notificaciones web push, Jellyseerr debe servirse mediante HTTPS.", "components.Settings.Notifications.NotificationsWebhook.validationTypes": "Debes seleccionar, al menos, un tipo de notificación", "components.Settings.Notifications.validationTypes": "Debes seleccionar, al menos, un tipo de notificación", @@ -816,10 +816,10 @@ "components.Settings.Notifications.encryptionTip": "Normalmente, TLS Implícito usa el puerto 465 y STARTTLS usa el puerto 587", "components.UserList.localLoginDisabled": "El ajuste para Habilitar el Inicio de Sesión Local está actualmente deshabilitado.", "components.Settings.SettingsUsers.defaultPermissionsTip": "Permisos iniciales asignados a nuevos usuarios", - "components.Settings.SettingsAbout.runningDevelop": "Estás utilizando la rama de develop de Jellyseerr, la cual solo se recomienda para aquellos que contribuyen al desarrollo o al soporte de las pruebas de nuevos desarrollos.", + "components.Settings.SettingsAbout.runningDevelop": "Estás utilizando la rama de desarrollo de Jellyseerr, la cual solo se recomienda para aquellos que contribuyen al desarrollo o al soporte de las pruebas de nuevos desarrollos.", "components.StatusBadge.status": "{status}", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Cada {jobScheduleMinutes, plural, one {minuto} other {{jobScheduleMinutes} minutos}}", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Cada {jobScheduleHours, plural, one {hora} other {{jobScheduleHours} horas}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Cada {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Cada {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}", "components.Settings.SettingsJobsCache.jobScheduleEditFailed": "Algo fue mal al guardar la tarea programada.", "components.Settings.SettingsJobsCache.editJobSchedule": "Modificar tarea programada", "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Nueva frecuencia", @@ -848,7 +848,7 @@ "components.IssueDetails.nocomments": "Sin comentarios.", "components.IssueDetails.openedby": "#{issueId} abierta {relativeTime} por {username}", "components.IssueDetails.openin4karr": "Abrir en {arr} 4K", - "components.IssueDetails.openinarr": "Abierta en {arr}", + "components.IssueDetails.openinarr": "Abrir en {arr}", "components.IssueDetails.play4konplex": "Ver en 4K en {mediaServerName}", "components.IssueDetails.playonplex": "Ver en {mediaServerName}", "components.IssueDetails.problemepisode": "Episodio Afectado", @@ -1193,7 +1193,7 @@ "components.RequestList.RequestItem.tvdbid": "Identificador de TheTVDB", "components.Settings.SettingsJobsCache.image-cache-cleanup": "Limpieza de la caché de imágenes", "components.Settings.SettingsJobsCache.imagecache": "Caché de imágenes", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Cada {jobScheduleSeconds, plural, one {segundo} other {{jobScheduleSeconds} segundos}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Cada {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}", "components.Settings.SettingsJobsCache.availability-sync": "Sincronización de la disponibilidad de medios", "components.Discover.tmdbmoviestreamingservices": "Servicios de streaming de películas TMDB", "components.Discover.tmdbtvstreamingservices": "Servicios de TV en streaming TMDB", @@ -1205,10 +1205,110 @@ "components.Settings.RadarrModal.tagRequestsInfo": "Añadir automáticamente una etiqueta adicional con el nombre de usuario y el nombre para mostrar del solicitante", "components.Settings.SonarrModal.tagRequestsInfo": "Añadir automáticamente una etiqueta adicional con el nombre de usuario y el nombre para mostrar del solicitante", "components.MovieDetails.imdbuserscore": "Puntuación de los usuarios de IMDB", - "components.Settings.SonarrModal.animeSeriesType": "Tipo de anime", - "components.Settings.SonarrModal.seriesType": "Tipo de series", - "components.Settings.Notifications.NotificationsPushover.sound": "Sonido para las notificaciones", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Dispositivo predeterminado", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Sonido para las notificaciones", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Dispositivo predeterminado" + "components.Layout.UserWarnings.passwordRequired": "Se requiere una contraseña.", + "components.Login.credentialerror": "El usuario o contraseña es incorrecto.", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Conectar", + "components.Login.initialsigningin": "Conectando…", + "components.Login.emailtooltip": "No es necesario asociar la dirección con su instancia de {mediaServerName}.", + "components.Login.saving": "Añadiendo…", + "components.Login.title": "Añadir Email", + "components.Login.username": "Nombre de usuario", + "components.Login.validationEmailFormat": "El email es inválido", + "components.Login.validationEmailRequired": "Debes proporcional un email", + "components.Login.validationemailformat": "Se requiere de un email válido", + "components.Login.validationhostformat": "Se requiere de una URL válida", + "components.Login.validationusernamerequired": "Se requiere un nombre de usuario", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Esto eliminará de manera irreversible esta {mediaType} de {arr}, incluyendo todos los archivos.", + "i18n.open": "Abierto", + "components.MovieDetails.downloadstatus": "Estado de la descarga", + "components.MovieDetails.openradarr": "Abrir Película en Radarr", + "components.MovieDetails.openradarr4k": "Abrir Película 4K en Radarr", + "components.MovieDetails.play": "Reproducir en {mediaServerName}", + "components.MovieDetails.play4k": "Reproducir 4K en {mediaServerName}", + "components.NotificationTypeSelector.issueresolved": "Incidencia Resuelta", + "components.NotificationTypeSelector.userissuecommentDescription": "Notificame cuando haya nuevos comentarios en incidencias que haya abierto.", + "components.NotificationTypeSelector.userissuecreatedDescription": "Notificame cuando otros usuarios reporten incidencias.", + "components.PermissionEdit.viewissues": "Ver incidencias", + "components.PermissionEdit.manageissuesDescription": "Dar permiso para administrar incidencias.", + "components.PermissionEdit.viewissuesDescription": "Dar permiso para ver incidencias reportadas por otros usuarios.", + "components.Settings.Notifications.NotificationsPushover.sound": "Sonido de Notificacion", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Escanear Añadidos Recientemente de Jellyfin", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Escaneo Completo de la libreria de Jellyfin", + "components.Settings.jellyfinSettings": "Ajustes de {mediaServerName}", + "components.Settings.jellyfinsettings": "Ajustes de {mediaServerName}", + "components.Settings.jellyfinSettingsFailure": "Algo salió mal al guardar la configuración de {mediaServerName}.", + "components.Settings.jellyfinSettingsSuccess": "¡La configuración de {mediaServerName} se guardó correctamente!", + "components.Settings.jellyfinlibraries": "Bibliotecas {mediaServerName}", + "components.Settings.jellyfinlibrariesDescription": "La biblioteca {mediaServerName} busca títulos. Haga clic en el botón a continuación si no aparece ninguna biblioteca.", + "components.Settings.manualscanDescriptionJellyfin": "Normalmente, esto sólo se ejecutará una vez cada 24 horas. Jellyseerr comprobará de forma más agresiva los añadidos recientemente de su servidor {mediaServerName}. ¡Si es la primera vez que configura Jellyseerr, se recomienda un escaneo manual completo de la biblioteca!", + "components.Settings.save": "Guardar Cambios", + "components.Settings.saving": "Guardando…", + "components.Settings.syncing": "Sincronizando", + "components.Settings.timeout": "Tiempo agotado", + "components.Setup.signinWithPlex": "Usa tu cuenta de Plex", + "components.Setup.configuremediaserver": "Configurar servidor multimedia", + "components.TitleCard.addToWatchList": "Añadir a lista de seguimiento", + "components.TitleCard.watchlistCancel": "Lista de seguimiento para {title} cancelada.", + "components.TitleCard.watchlistError": "Algo salió mal, intenta de nuevo.", + "components.TitleCard.watchlistSuccess": "{title} añadido correctamente a la lista de seguimiento!", + "components.TvDetails.play": "Reproducir en {mediaServerName}", + "components.UserList.importfromJellyfin": "Importar Usuarios de {mediaServerName}", + "components.UserList.mediaServerUser": "Usuario de {mediaServerName}", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} importado correctamente!", + "components.UserList.importfromJellyfinerror": "Se produjo un error al importar usuarios de {mediaServerName}.", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Guardar Cambios", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "Email", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Dispositivo Predeterminado", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Sonido de Notificacion", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": "Fallo al guardar los ajustes de la notificación Pushbullet.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "¡Los ajustes de notificación Pushbullet se han guardado con éxito!", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Token de aplicación API", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "Clave de usuario o grupo", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "Tu identificador de usuario o grupo de 30 caracteres", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "¡Se han guardado los ajustes de notificación de Pushover!", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "Registrar una aplicación para usar con {applicationTitle}", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Debes proporcionar una clave de usuario o grupo válida", + "components.Login.validationhostrequired": "{mediaServerName} URL requerida", + "i18n.resolved": "Resuelto", + "components.UserList.importfrommediaserver": "Importar Usuarios de {mediaServerName}", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Dispositivo Predeterminado", + "components.ManageSlideOver.removearr": "Eliminar de {arr}", + "components.NotificationTypeSelector.issuereopenedDescription": "Enviar notificación cuando se reabran incidencias.", + "components.Layout.UserWarnings.emailRequired": "Se requiere una dirección de email.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessToken": "Token de Acceso", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "Debes proporcionar un token de aplicación válido", + "components.Settings.syncJellyfin": "Sincronizar Bibliotecas", + "components.Layout.UserWarnings.emailInvalid": "La dirección de correo es inválida.", + "components.Login.description": "Como es la primera vez que inicias sesión en {applicationName}, es necesario añadir una dirección de email válida.", + "components.Login.save": "Añadir", + "components.NotificationTypeSelector.issueresolvedDescription": "Enviar notificación cuando se resuelvan incidencias.", + "components.PermissionEdit.manageissues": "Administrar incidencias", + "components.PermissionEdit.createissues": "Notificar incidencia", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Guardando…", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "Debes indicar un token de acceso", + "components.Login.signinwithjellyfin": "Utiliza tu cuenta de {mediaServerName}", + "components.ManageSlideOver.removearr4k": "Eliminar de {arr} 4K", + "components.Settings.internalUrl": "URL Interna", + "components.TvDetails.play4k": "Reproducir 4K en {mediaServerName}", + "components.Setup.signin": "Iniciar Sesión", + "components.Setup.signinWithJellyfin": "Utiliza tu cuenta de {mediaServerName}", + "components.UserList.noJellyfinuserstoimport": "No hay usuarios de {mediaServerName} que importar.", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "Usuario de {mediaServerName}", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "Crea un token desde tu Opciones de Cuenta", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "No se pudo guardar la configuración de notificaciones de Pushover.", + "components.NotificationTypeSelector.userissuereopenedDescription": "Recibir notificaciones cuando se vuelvan a abrir incidencias que haya reportado.", + "components.NotificationTypeSelector.userissueresolvedDescription": "Reciba notificaciones cuando se resuelvan las incidencias que haya reportado.", + "components.PermissionEdit.createissuesDescription": "Dar permiso para informar incidencias.", + "components.Settings.SettingsAbout.supportjellyseerr": "Apoya a Jellyseerr", + "components.Settings.Notifications.userEmailRequired": "Requerir email de usuario", + "components.Settings.SonarrModal.animeSeriesType": "Tipo de Serie Anime", + "components.Settings.SonarrModal.seriesType": "Tipo Serie", + "components.Settings.jellyfinSettingsDescription": "Opcionalmente, configure los puntos finales internos y externos para su servidor {mediaServerName}. En la mayoría de los casos, la URL externa es diferente a la URL interna. También se puede configurar una URL de restablecimiento de contraseña personalizada para el inicio de sesión de {mediaServerName}, en caso de que desee redirigir a una página de restablecimiento de contraseña diferente.", + "components.Settings.jellyfinsettingsDescription": "Configure los ajustes para su servidor {mediaServerName}. {mediaServerName} escanea sus bibliotecas de {mediaServerName} para ver qué contenido está disponible.", + "components.Settings.manualscanJellyfin": "Escanear Libreria Manualmente", + "components.TitleCard.watchlistDeleted": "{title} Eliminado correctamente de la lista de seguimiento!", + "components.UserList.newJellyfinsigninenabled": "La configuración Habilitar nuevo inicio de sesión de {mediaServerName} está actualmente habilitada. Los usuarios de {mediaServerName} con acceso a la biblioteca no necesitan ser importados para poder iniciar sesión.", + "components.UserProfile.localWatchlist": "Lista de seguimiento de {username}" } diff --git a/src/i18n/locale/fr.json b/src/i18n/locale/fr.json index 45e62c8b..5fa1a0f5 100644 --- a/src/i18n/locale/fr.json +++ b/src/i18n/locale/fr.json @@ -59,7 +59,7 @@ "components.Discover.DiscoverTvGenre.genreSeries": "Séries {genre}", "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Séries", "components.Discover.DiscoverTvLanguage.languageSeries": "Séries en {language}", - "components.Discover.DiscoverWatchlist.discoverwatchlist": "Votre watchlist Plex", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "Votre watchlist", "components.Discover.DiscoverWatchlist.watchlist": "Watchlist Plex", "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Filtre actif} other {# Filtres actifs}}", "components.Discover.FilterSlideover.clearfilters": "Effacer les filtres actifs", @@ -176,7 +176,7 @@ "components.Settings.RadarrModal.port": "Port", "components.Settings.RadarrModal.qualityprofile": "Profil de qualité", "components.Settings.RadarrModal.rootfolder": "Dossier racine", - "components.Settings.RadarrModal.selectMinimumAvailability": "Sélectionner une disponibilité minimale", + "components.Settings.RadarrModal.selectMinimumAvailability": "Sélectionner une disponibilté minimale", "components.Settings.RadarrModal.selectQualityProfile": "Sélectionner un profil qualité", "components.Settings.RadarrModal.selectRootFolder": "Sélectionner un dossier racine", "components.Settings.RadarrModal.server4k": "Serveur 4K", @@ -277,7 +277,7 @@ "i18n.tvshows": "Séries", "i18n.unavailable": "Indisponible", "pages.oops": "Oups", - "pages.returnHome": "Retourner à l'accueil", + "pages.returnHome": "Retourner à l'acceuil", "components.TvDetails.TvCast.fullseriescast": "Casting complet de la série", "components.MovieDetails.MovieCast.fullcast": "Casting complet", "components.Settings.Notifications.emailsettingssaved": "Paramètres de notification par e-mail enregistrés avec succès !", @@ -323,7 +323,7 @@ "components.Settings.SettingsAbout.Releases.viewchangelog": "Voir le journal des modifications", "components.Settings.SettingsAbout.Releases.versionChangelog": "Journal des modifications de la version {version}", "components.Settings.SettingsAbout.Releases.releases": "Versions", - "components.Settings.SettingsAbout.Releases.releasedataMissing": "Les données de version sont actuellement indisponibles.", + "components.Settings.SettingsAbout.Releases.releasedataMissing": "Les données de version sont actuellement indisponible.", "components.Settings.SettingsAbout.Releases.latestversion": "Dernière version", "components.Settings.SettingsAbout.Releases.currentversion": "Actuelle", "components.UserList.importfromplexerror": "Une erreur s'est produite durant l'importation des utilisateurs de Plex.", @@ -1071,7 +1071,7 @@ "components.ManageSlideOver.manageModalMedia4k": "Média(s) 4K", "components.ManageSlideOver.markallseasons4kavailable": "Marquer toutes les saisons comme disponibles en 4K", "components.ManageSlideOver.playedby": "Joué par", - "components.Settings.validationUrlTrailingSlash": "L'URL ne doit pas se terminer par un slash", + "components.Settings.validationUrlTrailingSlash": "L'URL ne doit pas ce terminer par un slash", "components.Settings.externalUrl": "URL externe", "components.Settings.tautulliApiKey": "Clé API", "components.Settings.tautulliSettings": "Paramètres Tautulli", @@ -1252,10 +1252,70 @@ "components.Settings.SonarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'ID utilisateur et le nom d'affichage du demandeur", "i18n.collection": "Collection", "components.Settings.RadarrModal.tagRequestsInfo": "Ajouter automatiquement un tag supplémentaire avec l'ID utilisateur et le nom d'affichage du demandeur", - "components.Settings.SonarrModal.seriesType": "Type de série", - "components.Settings.SonarrModal.animeSeriesType": "Types d'anime", + "components.IssueModal.issueVideo": "Vidéo", + "components.Settings.Notifications.NotificationsPushover.sound": "Son de notification", + "components.Settings.jellyfinSettings": "Paramètres pour {mediaServerName}", + "components.Settings.jellyfinSettingsFailure": "Une erreur est survenue lors de l'enregistrement des paramètres pour {mediaServerName}.", + "components.Settings.jellyfinSettingsSuccess": "Les paramètres pour {mediaServerName} ont été enregistrés avec succès !", + "components.Settings.jellyfinlibraries": "Bibliothèques {mediaServerName}", + "components.Settings.jellyfinlibrariesDescription": "Les bibliothèques de {mediaServerName} sont en cours d'analyze. Cliquez sur le bouton ci-dessous si aucune bibliothèque n'est répertoriée.", + "components.Settings.jellyfinsettings": "Paramètres pour {mediaServerName}", + "components.Settings.manualscanJellyfin": "Analyse manuelle de la bibliothèque", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.save": "Enregistrer les modifications", + "components.Settings.saving": "Sauvegarde en cours…", + "components.Settings.syncing": "Synchronisation en cours", + "components.Setup.signin": "Se connecter", + "components.Setup.signinWithPlex": "Utilisez votre compte Plex", + "components.StatusBadge.managemedia": "Gérer {mediaType}", + "components.StatusBadge.openinarr": "Ouvrir dans {arr}", + "components.StatusBadge.playonplex": "Lire sur {mediaServerName}", + "components.TitleCard.addToWatchList": "Ajouter à votre watchlist", + "components.TitleCard.watchlistCancel": "Watchlist pour {title} annulée.", + "components.TitleCard.watchlistError": "Une erreur est survenue. Veuillez réessayer.", + "components.TitleCard.watchlistSuccess": "{title} a été ajouté à votre watchlist avec succès !", + "components.TvDetails.Season.somethingwentwrong": "Une erreur est survenue lors de la récupération des données de la saison.", + "components.TvDetails.manageseries": "Gérer les séries", + "components.TvDetails.play": "Jouer sur {mediaServerName}", + "components.TvDetails.play4k": "Jouer en 4K sur {mediaServerName}", + "components.TvDetails.rtcriticsscore": "Tomatometer sur Rotten Tomatoes", + "components.TvDetails.seasonnumber": "Saison {seasonNumber}", + "components.TvDetails.seasonstitle": "Saisons", + "components.TvDetails.status4k": "{status} 4K", + "components.TvDetails.tmdbuserscore": "Score utilisateur sur TMDB", + "components.UserList.importfromJellyfin": "Importer les utilisateurs de {mediaServerName}", + "components.UserList.importfromJellyfinerror": "Une erreur est survenue lors de l'importation des utilisateurs de {mediaServerName}.", + "components.UserList.importfrommediaserver": "Importer les utilisateurs de {mediaServerName}", + "components.UserList.noJellyfinuserstoimport": "Il n'y a aucun utilisateur à importer pour {mediaServerName}.", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Sauvegarde en cours…", + "components.UserProfile.plexwatchlist": "Watchlist Plex", + "components.Settings.syncJellyfin": "Synchroniser les bibliothèques", + "components.TitleCard.watchlistDeleted": "{title} a été retiré de votre watchlist avec succès !", + "components.IssueModal.issueSubtitles": "Sous-titre", + "components.Login.emailtooltip": "L'adresse ne nécessite pas d'être associée avec votre instance {mediaServerName}.", "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Appareil par défaut", + "components.Settings.Notifications.userEmailRequired": "E-mail utilisateur requis", + "components.Settings.SettingsAbout.supportjellyseerr": "Soutenez Jellyseerr", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Scan complet des bibliothèques Jellyfin", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Scan des ajouts récents aux bibliothèques Jellyfin", + "components.Settings.SonarrModal.animeSeriesType": "Type de série anime", + "components.Settings.SonarrModal.seriesType": "Type de série", + "components.Settings.internalUrl": "URL interne", + "components.Settings.jellyfinsettingsDescription": "Configurez les paramètres de votre serveur {mediaServerName}. {mediaServerName} analyse vos bibliothèques {mediaServerName} pour voir quel contenu est disponible.", + "components.Settings.jellyfinSettingsDescription": "Configurez facultativement les URL internes et externes pour votre serveur {mediaServerName}. Dans la plupart des cas, l'URL externe est différente de l'URL interne. Vous pouvez également définir une URL de réinitialisation de mot de passe personnalisée pour la connexion à {mediaServerName}, au cas où vous souhaiteriez rediriger vers une page de réinitialisation de mot de passe différente.", + "components.Settings.manualscanDescriptionJellyfin": "Normalement, cette tâche est executée qu'une fois toutes les 24 heures. Jellyseerr vérifiera plus agressivement les éléments récemment ajoutés à votre serveur {mediaServerName}. Si c'est la première fois que vous configurez Jellyseerr, une analyse complète manuelle de la bibliothèque est recommandée !", + "components.Settings.timeout": "Temps écoulé", + "components.Setup.configuremediaserver": "Configurer le serveur multimédia", + "components.TvDetails.rtaudiencescore": "Score de l'audience sur Rotten Tomatoes", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "Utilisateur pour {mediaServerName}", + "components.Setup.signinWithJellyfin": "Utilisez votre compte {mediaServerName}", + "components.UserList.mediaServerUser": "Utilisateur de {mediaServerName}", + "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Épisode} other {# Épisodes}}", + "components.UserList.newJellyfinsigninenabled": "Le paramètre Activer la nouvelle connexion à {mediaServerName} est actuellement activé. Les utilisateurs de {mediaServerName} avec accès à la bibliothèque n'ont pas besoin d'être importés pour se connecter.", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-mail", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Enregistrer les modifications", "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Appareil par défaut", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} importé(s) avec succès !", "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Son de notification", - "components.Settings.Notifications.NotificationsPushover.sound": "Son de notification" + "components.UserProfile.localWatchlist": "Watchlist de {username}" } diff --git a/src/i18n/locale/he.json b/src/i18n/locale/he.json index 3dfa734b..cb39d9eb 100644 --- a/src/i18n/locale/he.json +++ b/src/i18n/locale/he.json @@ -1,1324 +1,359 @@ { - "components.AirDateBadge.airedrelative": "שודר {relativeTime}", - "components.AirDateBadge.airsrelative": "ישודר {relativeTime}", - "components.AppDataWarning.dockerVolumeMissingDescription": ".בנתיב בדיקה אינה מוגדרת באופן תקין. כל המידע יימחק כאשר הקונטיינר ייעצר או יופעל מחדש {appDataPath} המחיצה", - "components.CollectionDetails.numberofmovies": "{count} סרטים", - "components.CollectionDetails.overview": "תקציר", - "components.CollectionDetails.requestcollection": "בקשת האוסף", - "components.CollectionDetails.requestcollection4k": "4K בקשת האוסף ב", - "components.Discover.CreateSlider.addSlider": "הוספת סליידר", - "components.Discover.CreateSlider.addcustomslider": "יצירת סליידר מותאם", - "components.Discover.CreateSlider.addfail": ".אין אפשרות ליצור סליידר", - "components.Discover.CreateSlider.addsuccess": ".הסליידר חדש נוצר בהצלחה", - "components.Discover.CreateSlider.editSlider": "עריכת שורת תצוגה", - "components.Discover.CreateSlider.editfail": ".אירעה שגיאה בעריכת הסליידר", - "components.Discover.CreateSlider.editsuccess": ".הסליידר נערך בהצלחה", - "components.Discover.CreateSlider.needresults": ".חייבת להיות תוצאה אחת לפחות", - "components.Discover.CreateSlider.nooptions": "אין תוצאות", - "components.Discover.CreateSlider.providetmdbgenreid": "TMDB יש לרשום מזהה ג׳אנר של", - "components.Discover.CreateSlider.providetmdbkeywordid": "TMDB יש לרשום מזהה מילת מפתח של", - "components.Discover.CreateSlider.providetmdbnetwork": "TMDB יש לרשום מזהה רשת של", - "components.Discover.CreateSlider.providetmdbsearch": "יש לרשום שאילתת חיפוש", - "components.Discover.CreateSlider.providetmdbstudio": "TMDB יש לרשום מזהה אולפן של", - "components.Discover.CreateSlider.searchGenres": "…חיפוש ג׳אנרים", - "components.Discover.CreateSlider.searchKeywords": "…מילות מפתח לחיפוש", - "components.Discover.CreateSlider.searchStudios": "…חיפוש אולפנים", - "components.Discover.CreateSlider.slidernameplaceholder": "שם הסליידר", - "components.Discover.CreateSlider.starttyping": ".יש להקליד כדי להתחיל חיפוש", - "components.Discover.CreateSlider.validationDatarequired": ".יש לרשום ערך", - "components.Discover.CreateSlider.validationTitlerequired": ".יש לרשום כותרת", - "components.Discover.DiscoverMovieGenre.genreMovies": "{genre} סרטי", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} סרטי", - "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} סרטי", - "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", - "components.Discover.DiscoverMovies.discovermovies": "סרטים", - "components.Discover.DiscoverMovies.sortPopularityAsc": "פופולריות בסדר עולה", - "components.Discover.DiscoverMovies.sortPopularityDesc": "פופולריות בסדר יורד", - "components.Discover.DiscoverMovies.sortTitleAsc": "כותרת (א-ת) בסדר עולה", - "components.Discover.DiscoverMovies.sortTitleDesc": "כותרת (א-ת) בסדר יורד", - "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "בסדר עולה TMDB דירוג", - "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "בסדר יורד TMDB דירוג", - "components.Discover.DiscoverMovies.sortReleaseDateAsc": "תאריך שיחרור בסדר עולה", - "components.Discover.DiscoverMovies.sortReleaseDateDesc": "תאריך שיחרור בסדר יורד", - "components.Discover.DiscoverNetwork.networkSeries": "{network} סדרות ברשת", - "components.Discover.DiscoverSliderEdit.deletefail": ".מחיקת הסליידר נכשלה", - "components.Discover.DiscoverSliderEdit.deletesuccess": ".הסליידר נמחק בהצלחה", - "components.Discover.DiscoverSliderEdit.enable": "הצגה או הסתרה", - "components.Discover.DiscoverSliderEdit.remove": "הסרה", - "components.Discover.DiscoverStudio.studioMovies": "{studio} סרטים מאופלני", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", - "components.Discover.DiscoverTv.discovertv": "סדרות", - "components.Discover.DiscoverTv.sortFirstAirDateAsc": "שודר לראשונה בסדר עולה", - "components.Discover.DiscoverTv.sortFirstAirDateDesc": "שודר לראשונה בסדר יורד", - "components.Discover.DiscoverTv.sortPopularityAsc": "פופולריות בסדר עולה", - "components.Discover.DiscoverTv.sortPopularityDesc": "פופולריות בסדר יורד", - "components.Discover.DiscoverTv.sortTitleAsc": "כותרת (א-ת) בסדר עולה", - "components.Discover.DiscoverTv.sortTitleDesc": "כותרת (א-ת) בסדר יורד", - "components.Discover.DiscoverTv.sortTmdbRatingAsc": "בסדר עולה TMDB דירוג", - "components.Discover.DiscoverTv.sortTmdbRatingDesc": "בסדר יורד TMDB דירוג", - "components.Discover.DiscoverTvGenre.genreSeries": "{genre} סדרת", - "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} סדרות", - "components.Discover.DiscoverTvLanguage.languageSeries": "{language}סדרות ב", + "components.ManageSlideOver.alltime": "כל הזמנים", + "components.Login.validationemailrequired": "יש לספק מייל חוקי", + "components.NotificationTypeSelector.userissuereopenedDescription": "קבל התראה כשבעיות שפתחת נפתחות מחדש.", + "components.AppDataWarning.dockerVolumeMissingDescription": "ה {appDataPath} אחסון לא הוגדר כראוי. כל המידע יוסר כאשר הקונטיינר יעצור או יותחל מחדש.", + "components.CollectionDetails.overview": "תצוגה כללית", + "components.CollectionDetails.numberofmovies": "{כמות} סרטים", + "components.CollectionDetails.requestcollection": "אוסף בקשות", + "components.CollectionDetails.requestcollection4k": "אוסף בקשות ב4K", + "components.Discover.DiscoverMovieGenre.genreMovies": "סרטי {genre}", + "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} סרטים", + "components.Discover.DiscoverNetwork.networkSeries": "{network} סדרות", + "components.Discover.DiscoverStudio.studioMovies": "{studio} סרטים", + "components.Discover.DiscoverTvGenre.genreSeries": "{genre} סדרות", + "components.Discover.DiscoverTvLanguage.languageSeries": "{language} סדרות", + "components.IssueDetails.commentplaceholder": "הוסף תגובה …", + "components.IssueDetails.comments": "תגובות", + "components.IssueDetails.deleteissue": "מחק מקרה", + "components.IssueDetails.deleteissueconfirm": "האם אתה בטוח שאתה רוצה למחוק את המקרה?", + "components.AirDateBadge.airsrelative": "ישודר בעוד {relativeTime}", "components.Discover.DiscoverWatchlist.discoverwatchlist": "רשימת הצפייה שלך", - "components.Discover.DiscoverWatchlist.watchlist": "Plex רשימת הצפייה של", - "components.Discover.FilterSlideover.activefilters": "{count, plural, one {פילטר פעיל #} other {פילטרים פעילים #}}", - "components.Discover.FilterSlideover.clearfilters": "ניקוי מסננים פעילים", - "components.Discover.FilterSlideover.filters": "מסננים", - "components.Discover.FilterSlideover.firstAirDate": "שודר לראשונה", - "components.Discover.FilterSlideover.from": "מאת", - "components.Discover.FilterSlideover.genres": "ג׳אנרים", - "components.Discover.FilterSlideover.keywords": "מילות מפתח", - "components.Discover.FilterSlideover.originalLanguage": "שפת מקור", - "components.Discover.FilterSlideover.ratingText": "{maxValue} לבין {minValue} דירוגים בין", - "components.Discover.FilterSlideover.releaseDate": "תאריך יציאה", - "components.Discover.FilterSlideover.runtime": "זמן ריצה", - "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} זמן ריצה בין", - "components.Discover.FilterSlideover.streamingservices": "שירותי הזרמה", - "components.Discover.FilterSlideover.studio": "אולפן", - "components.Discover.FilterSlideover.tmdbuserscore": "TMDB ניקוד משתמשי", - "components.Discover.FilterSlideover.tmdbuservotecount": "TMDB הצבעות", - "components.Discover.FilterSlideover.to": "אל", - "components.Discover.FilterSlideover.voteCount": "{maxValue} ל {minValue} מספר הצבעות בין", - "components.Discover.MovieGenreList.moviegenres": "סרטים לפי ג׳אנר", - "components.Discover.MovieGenreSlider.moviegenres": "ג׳אנר סרטים", - "components.Discover.NetworkSlider.networks": "רשתות", - "components.Discover.PlexWatchlistSlider.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", - "components.Discover.PlexWatchlistSlider.plexwatchlist": "רשימת הצפייה שלך", - "components.Discover.RecentlyAddedSlider.recentlyAdded": "התווסף לאחרונה", + "components.Discover.MovieGenreList.moviegenres": "סוגי סרטים", "components.Discover.StudioSlider.studios": "אולפנים", - "components.Discover.TvGenreList.seriesgenres": "ג׳אנר סדרות", - "components.Discover.TvGenreSlider.tvgenres": "ג׳אנרים סדרות", - "components.Discover.createnewslider": "יצירת סליידר", - "components.Discover.customizediscover": "התאמת מה חדש", - "components.Discover.discover": "מה חדש", - "components.Discover.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", - "components.Discover.moviegenres": "סרטים לפי ג׳אנר", - "components.Discover.networks": "רשתות", - "components.Discover.plexwatchlist": "רשימת הצפייה שלך", - "components.Discover.popularmovies": "סרטים פופולריים", - "components.Discover.populartv": "הסדרות הפופולריות", - "components.Discover.recentlyAdded": "התווסף לאחרונה", - "components.Discover.recentrequests": "התבקשו לאחרונה", - "components.Discover.resetfailed": ".משהו השתבש בעת איפוס ההגדרות עבור מה חדש", - "components.Discover.resetsuccess": ".הגדרות מה חדש אופסו בהצלחה", - "components.Discover.resettodefault": "איפוס לברירת מחדל", - "components.Discover.resetwarning": "!איפוס כל הסליידרים לברירת מחדל. הדבר יוביל למחיקת סליידרים מותאמים אישית", - "components.Discover.stopediting": "הפסקת עריכה", - "components.Discover.studios": "לפי אולפנים", - "components.Discover.tmdbmoviegenre": "(TMDB) ג׳אנר סרט", - "components.Discover.tmdbmoviekeyword": "(TMDB) מילות חיפוש לסרט", - "components.Discover.tmdbmoviestreamingservices": "(TMDB) שירות הזרמת סרטים", - "components.Discover.tmdbnetwork": "(TMDB) רשת", - "components.Discover.tmdbsearch": "(TMDB) חיפוש", - "components.Discover.tmdbstudio": "(TMDB) אולפנים", - "components.Discover.tmdbtvgenre": "(TMDB) ג׳אנר של סדרה", - "components.Discover.tmdbtvkeyword": "(TMDB) מילות חיפוש לסדרה", - "components.Discover.tmdbtvstreamingservices": "TMDB שירות הזרדמת מדיה של טלויזיה", - "components.Discover.trending": "הכי נצפה", - "components.Discover.tvgenres": "סדרות לפי ג׳אנר", + "components.Discover.TvGenreList.seriesgenres": "סוגי סדרות", + "components.Discover.TvGenreSlider.tvgenres": "סוגי סדרות", + "components.Discover.recentlyAdded": "נוספו לאחרונה", + "components.Discover.recentrequests": "בקשות אחרונות", + "components.Discover.trending": "חמים", "components.Discover.upcoming": "סרטים שיצאו בקרוב", - "components.Discover.upcomingmovies": "סרטים בקרוב", + "components.Discover.upcomingmovies": "סרטים שיצאו בקרוב", "components.Discover.upcomingtv": "סדרות שיצאו בקרוב", - "components.Discover.updatefailed": ".משהו השתבש בעדכון ההגדרות וההתאמות של מה חדש", - "components.Discover.updatesuccess": ".הגדרות מה חדש נשמרו בהצלחה", - "components.DownloadBlock.estimatedtime": "{time} בערך", - "components.DownloadBlock.formattedTitle": "{title}: עונה {seasonNumber} פרק {episodeNumber}", - "components.IssueDetails.IssueComment.areyousuredelete": "?האם למחוק את התגובה", + "components.DownloadBlock.estimatedtime": "{time} משוער", "components.IssueDetails.IssueComment.delete": "מחיקת תגובה", + "components.IssueDetails.IssueComment.areyousuredelete": "למחוק את התגובה?", "components.IssueDetails.IssueComment.edit": "עריכת תגובה", - "components.IssueDetails.IssueComment.postedby": "{username} ע״י {relativeTime} נוצר", - "components.IssueDetails.IssueComment.postedbyedited": "(נערך) {username} ע״י {relativeTime} נוצר", - "components.IssueDetails.IssueComment.validationComment": "יש לרשום הודעה", - "components.IssueDetails.IssueDescription.deleteissue": "מחיקת תקלה", - "components.IssueDetails.IssueDescription.description": "תיאור", - "components.IssueDetails.IssueDescription.edit": "עריכת תיאור", + "components.IssueDetails.IssueDescription.edit": "ערוך תיאור", "components.IssueDetails.allepisodes": "כל הפרקים", "components.IssueDetails.allseasons": "כל העונות", - "components.IssueDetails.closeissue": "סגירת תקלה", - "components.IssueDetails.closeissueandcomment": "סגירה עם תגובה", - "components.IssueDetails.commentplaceholder": "…הוספת תגובה", - "components.IssueDetails.comments": "תגובות", - "components.IssueDetails.deleteissue": "מחיקת תקלה", - "components.IssueDetails.deleteissueconfirm": "?למחוק את התקלה", - "components.IssueDetails.episode": "{episodeNumber} פרק", - "components.IssueDetails.issuepagetitle": "תקלה", - "components.IssueDetails.issuetype": "סוג", - "components.IssueDetails.lastupdated": "עודכן לאחרונה", - "components.IssueDetails.leavecomment": "תגובה", - "components.IssueDetails.nocomments": "אין תגובות", - "components.IssueDetails.openedby": "{username} ע״י {relativeTime} נפתח {issueId}", - "components.IssueDetails.openin4karr": "{arr} 4K פתיחה ב", - "components.IssueDetails.openinarr": "{arr} פתיחה ב", - "components.IssueDetails.play4konplex": "{mediaServerName} 4K ניגון ב", - "components.IssueDetails.playonplex": "{mediaServerName} ניגון ב", - "components.IssueDetails.problemepisode": "בעיה בפרק", - "components.IssueDetails.problemseason": "בעיה בעונה", - "components.IssueDetails.reopenissue": "פתיחת תקלה מחדש", - "components.IssueDetails.reopenissueandcomment": "פתיחה מחדש עם הערה", - "components.IssueDetails.season": "{seasonNumber} עונה", - "components.IssueDetails.toasteditdescriptionfailed": ".משהו השתבש בעת עדכון תיאור התקלה", - "components.IssueDetails.toasteditdescriptionsuccess": "!תיאור התקלה עודכן בהצלחה", - "components.IssueDetails.toastissuedeleted": "!התקלה נמחקה בהצלחה", - "components.IssueDetails.toastissuedeletefailed": ".משהו השתבש בעת מחיקת התקלה", - "components.IssueDetails.toaststatusupdated": "!סטאטוס התקלה עודכן בהצלחה", - "components.IssueDetails.toaststatusupdatefailed": ".משהו השתבש בעת עדכון סטאטוס התקלה", - "components.IssueDetails.unknownissuetype": "ללא", - "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {פרק} other {פרקים}}", - "components.IssueList.IssueItem.issuestatus": "סטאטוס", + "components.IssueDetails.closeissue": "סגור מקרה", + "components.IssueDetails.closeissueandcomment": "סגור עם תגובה", + "components.IssueDetails.episode": "פרק {episodeNumber}", + "components.IssueDetails.issuepagetitle": "מקרה", + "components.IssueDetails.playonplex": "הפעל ב-Plex", + "components.IssueDetails.play4konplex": "הפעל 4K ב-Plex", + "components.IssueDetails.problemepisode": "פרק מושפע", + "components.IssueDetails.toastissuedeleted": "מקרה נמחק בהצלחה!", "components.IssueList.IssueItem.issuetype": "סוג", - "components.IssueList.IssueItem.opened": "נפתחה", - "components.IssueList.IssueItem.openeduserdate": "ע״י {user} לפני {time}", - "components.IssueList.IssueItem.problemepisode": "משפיע על פרק", - "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", - "components.IssueList.IssueItem.unknownissuetype": "ללא", - "components.IssueList.IssueItem.viewissue": "הצגת התקלה", - "components.IssueList.issues": "תקלות", - "components.IssueList.showallissues": "הצג את כל התקלות", - "components.IssueList.sortAdded": "הכי עדכני", - "components.IssueList.sortModified": "השתנה לאחרונה", - "components.IssueModal.CreateIssueModal.allepisodes": "כל הפרקים", - "components.IssueModal.CreateIssueModal.allseasons": "כל העונות", - "components.IssueModal.CreateIssueModal.episode": "{episodeNumber} פרק", - "components.IssueModal.CreateIssueModal.extras": "אקסטה", - "components.IssueModal.CreateIssueModal.problemepisode": "בעיה בפרק", - "components.IssueModal.CreateIssueModal.problemseason": "בעיה בעונה", - "components.IssueModal.CreateIssueModal.providedetail": ".יש לספק תיאור מפורט אודות התקלה שאירעה", - "components.IssueModal.CreateIssueModal.reportissue": "דיווח על תקלה", - "components.IssueModal.CreateIssueModal.season": "{seasonNumber} עונה", - "components.IssueModal.CreateIssueModal.submitissue": "יצירת תקלה", - "components.IssueModal.CreateIssueModal.toastFailedCreate": ".משהו השתבש בעת יצירת התקלה", - "components.IssueModal.CreateIssueModal.toastSuccessCreate": "!התקלה נוצרה בהצלחה עבור {title}", - "components.IssueModal.CreateIssueModal.toastviewissue": "צפייה בתקלה", - "components.IssueModal.CreateIssueModal.validationMessageRequired": "יש לרשום תיאור", - "components.IssueModal.CreateIssueModal.whatswrong": "?מהי התקלה", - "components.IssueModal.issueAudio": "אודיו", - "components.IssueModal.issueOther": "אחר", + "components.IssueList.IssueItem.opened": "נפתח", + "components.IssueList.IssueItem.openeduserdate": "{date} ע\"י {user}", "components.IssueModal.issueSubtitles": "כתוביות", "components.IssueModal.issueVideo": "וידאו", - "components.LanguageSelector.languageServerDefault": "({language}) ברירת מחדל", + "components.Layout.Sidebar.dashboard": "גילוי חדשים", + "components.Login.signingin": "מבצע לוגאין…", + "components.Login.signinheader": "יש להתחבר בכדי להמשיך", + "components.Login.signinwithoverseerr": "שימוש בחשבון {applicationTitle}", + "components.Login.signinwithplex": "שימוש בחשבון Plex", + "components.ManageSlideOver.downloadstatus": "הורדות", + "components.Discover.DiscoverWatchlist.watchlist": "רשימת צפייה", + "components.Discover.MovieGenreSlider.moviegenres": "סוגי סרטים", + "components.Discover.populartv": "סדרות פופולריות", + "components.IssueDetails.IssueComment.postedby": "פורסם לפני {relativeTime} ע\"י {username}", + "components.IssueDetails.IssueComment.postedbyedited": "פורסם לפני {relativeTime} ע\"י {username} (נערך)", + "components.IssueDetails.IssueDescription.description": "תיאור", + "components.IssueDetails.openedby": "#{issueId} נפתח לפני {relativeTime} ע\"י {username}", + "components.IssueDetails.openin4karr": "נפתח ב4K {arr}", + "components.IssueDetails.openinarr": "פתח ב {arr}", + "components.IssueList.IssueItem.problemepisode": "פרק מושפע", + "components.IssueList.sortAdded": "הכי עדכני", + "components.IssueList.sortModified": "עודכן לאחרונה", + "components.IssueModal.CreateIssueModal.allepisodes": "כל הפרקים", + "components.IssueModal.CreateIssueModal.providedetail": "יש לספק מידע מפורט אודות המקרה שחווית.", + "components.IssueModal.CreateIssueModal.submitissue": "הגש מקרה", "components.LanguageSelector.originalLanguageDefault": "כל השפות", - "components.Layout.LanguagePicker.displaylanguage": "שפת התצוגה", - "components.Layout.SearchInput.searchPlaceholder": "חיפוש סרטים וסדרות", - "components.Layout.Sidebar.browsemovies": "סרטים", - "components.Layout.Sidebar.browsetv": "סדרות", - "components.Layout.Sidebar.dashboard": "מה חדש", - "components.Layout.Sidebar.issues": "תקלות", "components.Layout.Sidebar.requests": "בקשות", "components.Layout.Sidebar.settings": "הגדרות", "components.Layout.Sidebar.users": "משתמשים", - "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "בקשת סרטים", - "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "בקשת סדרות", "components.Layout.UserDropdown.myprofile": "פרופיל", - "components.Layout.UserDropdown.requests": "בקשות", "components.Layout.UserDropdown.settings": "הגדרות", - "components.Layout.UserDropdown.signout": "התנתקות", - "components.Layout.UserWarnings.emailInvalid": ".כתובת דואר אינה חוקית", - "components.Layout.UserWarnings.emailRequired": ".יש לרשום כתובת מייל", - "components.Layout.UserWarnings.passwordRequired": "נדרשת סיסמה.", - "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} behind", - "components.Layout.VersionStatus.outofdate": "נדרש עידכון", - "components.Layout.VersionStatus.streamdevelop": "Jellyseerr פיתוח", - "components.Layout.VersionStatus.streamstable": "Jellyseerr יציב", - "components.Login.adminerror": "נדרש להתחבר עם חשבון מנהל", - "components.Login.credentialerror": ".שם המשתמש או הסיסמה שגויים", - "components.Login.description": "בחיבור ראשוני אל {applicationName} יש להוסיף כתובת מייל תקינה.", - "components.Login.email": "מייל", - "components.Login.emailtooltip": ".{mediaServerName} אין צורך לשייך את הכתובת עם שרת", - "components.Login.enablessl": "SSL שימוש ב", + "components.Layout.VersionStatus.streamdevelop": "פיתוח Jellyseerr", + "components.AirDateBadge.airedrelative": "שודר ב-{relativeTime}", + "components.Discover.NetworkSlider.networks": "רשתות שידור", + "components.Discover.discover": "לגלות", + "components.Discover.plexwatchlist": "רשימת הצפייה שלך", + "components.Discover.popularmovies": "סרטים פופולרים", + "components.IssueDetails.IssueComment.validationComment": "אנא הכנס הודעה", + "components.IssueDetails.IssueDescription.deleteissue": "מחק מקרה", + "components.IssueDetails.issuetype": "סוג", + "components.IssueDetails.lastupdated": "עודכן לאחרונה", + "components.IssueDetails.leavecomment": "תגובה", + "components.IssueDetails.nocomments": "אין תגובות.", + "components.IssueDetails.problemseason": "עונה מושפעת", + "components.IssueDetails.reopenissue": "פתח בעיה מחדש", + "components.IssueDetails.reopenissueandcomment": "פתח מחדש עם תגובה", + "components.IssueDetails.season": "עונה {seasonNumber}", + "components.IssueDetails.toasteditdescriptionfailed": "משהו השתבש בזמן עריכת תיאור המקרה.", + "components.IssueDetails.toasteditdescriptionsuccess": "תיאור המקרה נערך בהצלחה!", + "components.IssueDetails.toastissuedeletefailed": "משהו השתבש בזמן מחיקת המקרה.", + "components.IssueDetails.toaststatusupdated": "סטאטוס המקרה עודכן בהצלחה!", + "components.IssueDetails.toaststatusupdatefailed": "משהו השתבש בזמן עדכון סטאטוס המקרה.", + "components.IssueDetails.unknownissuetype": "לא ידוע", + "components.IssueList.IssueItem.issuestatus": "סטאטוס", + "components.IssueList.IssueItem.unknownissuetype": "לא ידוע", + "components.IssueList.IssueItem.viewissue": "צפה במקרה", + "components.IssueList.issues": "מקרים", + "components.IssueList.showallissues": "הצג את כל המקרים", + "components.IssueModal.CreateIssueModal.allseasons": "כל העונות", + "components.IssueModal.CreateIssueModal.episode": "פרק {episodeNumber}", + "components.IssueModal.CreateIssueModal.extras": "תוספות", + "components.IssueModal.CreateIssueModal.problemepisode": "פרק מושפע", + "components.IssueModal.CreateIssueModal.season": "עונה {seasonNumber}", + "components.IssueModal.CreateIssueModal.problemseason": "עונה מושפעת", + "components.IssueModal.CreateIssueModal.toastFailedCreate": "משהו השתבש בזמן הגשת מקרה.", + "components.IssueModal.CreateIssueModal.reportissue": "דווח על מקרה", + "components.IssueModal.CreateIssueModal.whatswrong": "מה השתבש?", + "components.IssueModal.issueAudio": "אודיו", + "components.IssueModal.issueOther": "אחר", + "components.IssueModal.CreateIssueModal.toastviewissue": "צפה במקרה", + "components.IssueModal.CreateIssueModal.validationMessageRequired": "אנא כתוב תיאור", + "components.Layout.LanguagePicker.displaylanguage": "שפת תצוגה", + "components.Layout.SearchInput.searchPlaceholder": "חיפוש סרטים וסדרות", + "components.LanguageSelector.languageServerDefault": "ברירת מחדל({language})", + "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "בקשות סרטים", + "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "בקשות סדרות", "components.Login.forgotpassword": "שכחת סיסמה?", - "components.Login.hostname": "{mediaServerName} כתובת", - "components.Login.initialsignin": "התחברות", - "components.Login.initialsigningin": "…מתחבר", - "components.Login.invalidurlerror": ".{mediaServerName} אין אפשרות להתחבר אל", - "components.Login.loginerror": "משהו השתבש בעת ההחברות.", - "components.Login.password": "סיסמה", - "components.Login.port": "פורט", - "components.Login.save": "הוספה", - "components.Login.saving": "…מוסיף", - "components.Login.signin": "התחברות", - "components.Login.signingin": "…מתחבר", - "components.Login.signinheader": "יש להתחבר בכדי להמשיך", - "components.Login.signinwithjellyfin": "{mediaServerName} שימוש בחשבון", - "components.Login.signinwithoverseerr": "{applicationTitle} שימוש בחשבון", - "components.Login.signinwithplex": "שימוש בחשבון Plex", - "components.Login.title": "הוסף מייל", - "components.Login.urlBase": "תחילית לכתובת", - "components.Login.username": "משתמש", - "components.Login.validationEmailFormat": "מייל שגוי", - "components.Login.validationEmailRequired": "יש לרשום מייל", - "components.Login.validationHostnameRequired": "יש לרשום שם מארח או כתובת חוקיים", - "components.Login.validationPortRequired": "יש לרשום מספר פורט תקין", - "components.Login.validationUrlBaseLeadingSlash": "יש להתחיל עם סלאש בתחילית לכתובת", - "components.Login.validationUrlBaseTrailingSlash": "אין לסיים את תחילית הכתובת עם סלאש", - "components.Login.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", - "components.Login.validationemailformat": "יש לרשום כתובת מייל", - "components.Login.validationemailrequired": "יש לרשום כתובת מייל תקינה", - "components.Login.validationhostformat": "נדרשת כתובת שרת תקינה", - "components.Login.validationhostrequired": "יש לרשום כתובת שרת", - "components.Login.validationpasswordrequired": "יש לרשום סיסמה", - "components.Login.validationusernamerequired": "יש לרשום שם משתמש", - "components.ManageSlideOver.alltime": "כל הזמנים", - "components.ManageSlideOver.downloadstatus": "הורדות", + "components.Layout.VersionStatus.streamstable": "Jellyseerr יציבה", + "components.Login.email": "כתובת מייל", "components.ManageSlideOver.manageModalAdvanced": "מתקדם", - "components.ManageSlideOver.manageModalClearMedia": "נקה מידע", - "components.ManageSlideOver.manageModalClearMediaWarning": "* {mediaServerName} הדבר יוביל למחיקה בלתי הפיכה של ה{mediaType} כל האינפורמציה תיווצר מחדש בסריקה הבאה {mediaServerName} אם זה קיים ב", + "components.ManageSlideOver.manageModalClearMedia": "ניקוי מידע", + "components.Discover.emptywatchlist": "מדיה נוספה לתוך רשימת צפייה תוצג פה.", + "components.IssueModal.CreateIssueModal.toastSuccessCreate": "דווח מקרה של {title} הוגש בהצלחה!", + "components.Layout.Sidebar.issues": "מקרים", + "components.Layout.UserDropdown.requests": "בקשות", + "components.Layout.UserDropdown.signout": "התנתק", + "components.Layout.VersionStatus.outofdate": "לא מעודכן", + "components.Login.loginerror": "משהו השתבש בלוגאין.", + "components.Login.password": "סיסמה", + "components.Login.signin": "התחברות", + "components.Login.validationpasswordrequired": "יש לספק סיסמה", + "components.Discover.CreateSlider.editSlider": "ערוך סליידר", + "components.Discover.CreateSlider.editfail": "נכשלה עריכת סליידר.", + "components.Discover.CreateSlider.needresults": "אתה צריך לפחות תוצאה אחת.", + "components.Discover.CreateSlider.nooptions": "אין תוצאות.", + "components.Discover.CreateSlider.providetmdbgenreid": "ספק מזהה ז'אנר TMDB", + "components.Discover.CreateSlider.providetmdbkeywordid": "ספק מזהה מילת מפתח TMDB", + "components.Discover.CreateSlider.providetmdbsearch": "ספק שאילתת חיפוש", + "components.Discover.CreateSlider.providetmdbstudio": "ספק מזהה סטודיו TMDB", + "components.Discover.CreateSlider.searchGenres": "חפש ז'אנרים…", + "components.Discover.CreateSlider.searchKeywords": "חפש מילות מפתח…", + "components.Discover.CreateSlider.searchStudios": "חפש אולפנים…", + "components.Discover.CreateSlider.slidernameplaceholder": "שם הסליידר", + "components.Discover.CreateSlider.validationDatarequired": "עליך לספק ערך.", + "components.Discover.CreateSlider.validationTitlerequired": "עליך לספק כותר.", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "סרטים {keywordTitle}", + "components.Discover.DiscoverMovies.discovermovies": "סרטים", + "components.Discover.DiscoverMovies.sortPopularityAsc": "פופולריות בסדר עולה", + "components.Discover.DiscoverMovies.sortPopularityDesc": "פופולריות בסדר יורד", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "תאריך שחרור בסדר עולה", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "תאריך שחרור בסדר יורד", + "components.Discover.DiscoverMovies.sortTitleAsc": "כותר (א-ת, A-Z) עולה", + "components.Discover.DiscoverMovies.sortTitleDesc": "כותר (ת-א, A-Z) יורד", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "דירוג TMDB בסדר עולה", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "דירוג TMDB בסדר יורד", + "components.Discover.DiscoverSliderEdit.deletesuccess": "סליידר נמחק בהצלחה.", + "components.Discover.DiscoverSliderEdit.enable": "שנה נראות", + "components.Discover.DiscoverSliderEdit.remove": "הסר", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {פילטר אחד פעיל} other {# פילטרים פעילים}}", + "components.Discover.DiscoverTv.discovertv": "סדרה", + "components.Discover.CreateSlider.addfail": "נכשלה יצירת סליידר חדש.", + "components.Discover.DiscoverTv.sortPopularityAsc": "פופולריות בסדר עולה", + "components.Discover.DiscoverTv.sortPopularityDesc": "פופולריות בסדר יורד", + "components.Discover.DiscoverTv.sortTitleAsc": "כותר (א-ת, A-Z) עולה", + "components.Discover.DiscoverTv.sortTitleDesc": "כותר (א-ת, A-Z) יורד", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "דירוג TMDB בסדר עולה", + "components.Discover.DiscoverTvKeyword.keywordSeries": "סדרה {keywordTitle}", + "components.Discover.FilterSlideover.activefilters": "{count, plural, one {פילטר אחד פעיל} other {# פילטרים פעילים}}", + "components.Discover.FilterSlideover.clearfilters": "נקה פילטרים פעילים", + "components.Discover.FilterSlideover.filters": "פילטרים", + "components.Discover.FilterSlideover.firstAirDate": "תאריך שידור ראשון", + "components.Discover.FilterSlideover.from": "מאת", + "components.Discover.FilterSlideover.keywords": "מילות מפתח", + "components.Discover.FilterSlideover.originalLanguage": "שפה מקורית", + "components.Discover.FilterSlideover.ratingText": "דירוג בין {minValue} ל {maxValue}", + "components.Discover.FilterSlideover.releaseDate": "תאריך שחרור", + "components.Discover.FilterSlideover.runtime": "זמן ריצה", + "components.Discover.FilterSlideover.streamingservices": "שירותי הזרמה", + "components.Discover.FilterSlideover.studio": "אולפן", + "components.Discover.FilterSlideover.tmdbuserscore": "דירוג משתמש TMDB", + "components.Discover.CreateSlider.addSlider": "הוסף סליידר", + "components.Discover.CreateSlider.addcustomslider": "צור סליידר מותאם", + "components.Discover.CreateSlider.addsuccess": "סליידר חדש נוצר בהצלחה ונשמרו הגדרות התאמה.", + "components.Discover.CreateSlider.providetmdbnetwork": "ספק מזהה רשת TMDB", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "תאריך שידור ראשון בסדר יורד", + "components.Discover.CreateSlider.starttyping": "התחל להזין כדי לחפש.", + "components.Discover.FilterSlideover.runtimeText": "זמן ריצה בין {minValue} ל {maxValue}", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {פילטר פעיל} other {# פילטרים פעילים}}", + "components.Discover.DiscoverSliderEdit.deletefail": "כשל במחיקת סליידר.", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "תאריך שידור ראשון בסדר עולה", + "components.Discover.CreateSlider.editsuccess": "סליידר נערך ונשמר בהצלחה.", + "components.Discover.DiscoverTv.sortTmdbRatingDesc": "דירוג TMDB בסדר יורד", + "components.Discover.FilterSlideover.genres": "ז'אנרים", + "components.Discover.FilterSlideover.tmdbuservotecount": "מספר הצבעות משתמשים בTMDB", + "components.Discover.FilterSlideover.to": "בשביל", + "components.Discover.FilterSlideover.voteCount": "מספר הצבעות בין {minValue} ל-{maxValue}", + "components.Discover.PlexWatchlistSlider.emptywatchlist": "מדיה נוספה לרשימת צפייה ב-Plexותופיע שם.", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "רשימת הצפייה שלך", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "נוספו לאחרונה", + "components.Discover.createnewslider": "יצירת מחוון חדש", + "components.Discover.customizediscover": "התאם אישית את הגילוי", + "components.Discover.networks": "רשתות", + "components.Discover.resetsuccess": "הגדרות הגילוי אופסו בהצלחה.", + "components.Discover.resettodefault": "אופס לברירת מחדל", + "components.Discover.stopediting": "סיים עריכה", + "components.Discover.studios": "אולפנים", + "components.Discover.tmdbmoviegenre": "ז'אנר סרט בTMDB", + "components.Discover.tmdbmoviekeyword": "מילת מפתח של סרט בTMDB", + "components.Discover.tmdbmoviestreamingservices": "רשת צפייה של סרט בTMDB", + "components.Discover.tmdbnetwork": "רשת בTMDB", + "components.Discover.tmdbsearch": "חיפוש בTMDB", + "components.Discover.tmdbstudio": "אולפן בTMDB", + "components.Discover.tmdbtvgenre": "ז'אנר סדרה בTMDB", + "components.Discover.tmdbtvkeyword": "מילת מפתח של סדרה בTMDB", + "components.Discover.tvgenres": "ז'אנרים של סדרות", + "components.Discover.updatefailed": "משהו השתבש במהלך עדכון הגדרות של גילוי.", + "components.Discover.updatesuccess": "הגדרות הגילוי התעדכנו.", + "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", + "components.Layout.Sidebar.browsetv": "סדרות", + "components.Layout.UserWarnings.emailRequired": "דרושה כתובת מייל.", + "components.Layout.UserWarnings.passwordRequired": "דרושה סיסמה.", + "components.Login.credentialerror": "שם המשתמש או הסיסמה שגויים.", + "components.Login.description": "בהתחברות ראשונית ל-{applicationName}, יש להוסיף כתובת מייל.", + "components.Login.host": "קישור {mediaServerName}", + "components.Login.initialsignin": "חיבור", + "components.Login.initialsigningin": "מתחבר…", + "components.Login.save": "הוספה", + "components.Login.saving": "מוסיף…", + "components.Login.title": "הוספת מייל", + "components.Login.username": "שם משתמש", + "components.Login.validationEmailFormat": "כתובת מייל שגוייה", + "components.Login.validationEmailRequired": "יש להוסיף מייל", + "components.Login.validationemailformat": "נדרש מייל תקין", + "components.Login.validationhostrequired": "נדרש קישור של {mediaServerName}", + "components.Login.validationusernamerequired": "נדרש שם משתמש", "components.ManageSlideOver.manageModalIssues": "תקלות פתוחות", "components.ManageSlideOver.manageModalMedia": "מדיה", - "components.ManageSlideOver.manageModalMedia4k": "4K מדיה", + "components.ManageSlideOver.manageModalMedia4k": "מדיה ב-4K", "components.ManageSlideOver.manageModalNoRequests": "אין בקשות.", - "components.ManageSlideOver.manageModalRemoveMediaWarning": "* {arr} הדבר יוביל למחיקה לצמיתות של ה{mediaType} כולל כל הקבצים ב", "components.ManageSlideOver.manageModalRequests": "בקשות", - "components.ManageSlideOver.manageModalTitle": "ניהול ה{mediaType}", - "components.ManageSlideOver.mark4kavailable": "סימון 4K כזמין", - "components.ManageSlideOver.markallseasons4kavailable": "סימון כל העונות זמינות 4K", - "components.ManageSlideOver.markallseasonsavailable": "סימון כל העונות כזמינות", - "components.ManageSlideOver.markavailable": "סימון כזמין", + "components.ManageSlideOver.manageModalTitle": "נהל {mediaType}", + "components.ManageSlideOver.mark4kavailable": "סמן כזמין באיכות 4K", + "components.ManageSlideOver.markallseasonsavailable": "סמן את כל העונות כזמינות", + "components.ManageSlideOver.markavailable": "סמן כזמין", "components.ManageSlideOver.movie": "סרט", - "components.ManageSlideOver.openarr": "{arr} - פתיחה ב", - "components.ManageSlideOver.openarr4k": "פתיחה ב 4K {arr}", - "components.ManageSlideOver.opentautulli": "Tautulli - פתיחה ב", - "components.ManageSlideOver.pastdays": "ימים האחרונים {days, number} ב", - "components.ManageSlideOver.playedby": "נוגן ע״י", - "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {נוגן} other {נוגנו}}", - "components.ManageSlideOver.removearr": "{arr} - הסרה מ", - "components.ManageSlideOver.removearr4k": "4K {arr} - הסרה מ", + "components.ManageSlideOver.openarr": "הפעל ב-{arr}", + "components.ManageSlideOver.openarr4k": "הפעל גרסת 4K ב-{arr}", + "components.ManageSlideOver.opentautulli": "הפעל ב-Tautulli", + "components.ManageSlideOver.pastdays": "{days, number} ימים עברו", + "components.ManageSlideOver.playedby": "משוחק על ידי", + "components.ManageSlideOver.removearr": "הסר מ-{arr}", + "components.ManageSlideOver.removearr4k": "הסר את איכות 4K מ-{arr}", "components.ManageSlideOver.tvshow": "סדרה", - "components.MediaSlider.ShowMoreCard.seemore": "הצג עוד", - "components.MovieDetails.MovieCast.fullcast": "כל הליהוק", - "components.MovieDetails.MovieCrew.fullcrew": "הצוות המלא", + "components.MediaSlider.ShowMoreCard.seemore": "ראה עוד", + "components.MovieDetails.MovieCast.fullcast": "כל השחקנים", + "components.MovieDetails.MovieCrew.fullcrew": "כל הצוות", "components.MovieDetails.budget": "תקציב", "components.MovieDetails.cast": "ליהוק", - "components.MovieDetails.digitalrelease": "שיחרור דיגטילי", - "components.MovieDetails.downloadstatus": "סטאטוס הורדה", - "components.MovieDetails.imdbuserscore": "IMDB ציון צופים", - "components.MovieDetails.managemovie": "נהל סרט", - "components.MovieDetails.mark4kavailable": "4K סימון כזמין ב", - "components.MovieDetails.markavailable": "סימון כזמין", - "components.MovieDetails.openradarr": "Radarr פתח סרט ב", - "components.MovieDetails.openradarr4k": "4K Radarr פתח סרט ב", + "components.MovieDetails.digitalrelease": "מהדורה דיגיטלית", + "components.MovieDetails.downloadstatus": "מצב הורדה", + "components.MovieDetails.imdbuserscore": "דירוג משתמש ב-IMDB", + "components.MovieDetails.mark4kavailable": "סמן כזמין באיכות 4K", + "components.MovieDetails.markavailable": "סמן כזמין", + "components.MovieDetails.openradarr": "הפעל סרט ב-Radarr", + "components.MovieDetails.openradarr4k": "הפעל סרט באיכות 4K ב-Radarr", "components.MovieDetails.originallanguage": "שפת מקור", - "components.MovieDetails.originaltitle": "שם מקורי", - "components.MovieDetails.overview": "תקציר", - "components.MovieDetails.overviewunavailable": "תקציר אינו זמין", - "components.MovieDetails.physicalrelease": "שיחרור מדיה", - "components.MovieDetails.play": "{mediaServerName} ניגון ב", - "components.MovieDetails.play4k": "{mediaServerName} 4K ניגון ב", - "components.MovieDetails.productioncountries": "מפיקה {countryCount, plural, one {מדינה} other {מדינות}}", - "components.MovieDetails.recommendations": "דומים לסרט", - "components.MovieDetails.releasedate": "{releaseCount, plural, one {תאריך שיחרור} other {תאריכי שיחרור}}", - "components.MovieDetails.reportissue": "דיווח על תקלה", - "components.MovieDetails.revenue": "מחזור הכנסות", - "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes דירוג קהל", - "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes טומטומטר של", + "components.MovieDetails.originaltitle": "השם במקור", + "components.MovieDetails.overview": "סקירה", + "components.MovieDetails.physicalrelease": "מהדורה פיזית", + "components.MovieDetails.play": "הפעל ב-{mediaServerName}", + "components.MovieDetails.play4k": "הפעל באיכות 4K ב-{mediaServerName}", + "components.MovieDetails.recommendations": "המלצות", + "components.MovieDetails.revenue": "רווח", + "components.MovieDetails.rtaudiencescore": "דירוג צופים ב-Rotten Tomatoes", + "components.MovieDetails.rtcriticsscore": "דירוג ב-Rotten Tomatoes", "components.MovieDetails.runtime": "{minutes} דקות", - "components.MovieDetails.showless": "הצג פחות", - "components.MovieDetails.showmore": "עוד", - "components.MovieDetails.similar": "סרטים דומים", - "components.MovieDetails.streamingproviders": "מנוגן ב", - "components.MovieDetails.studio": "{studioCount, plural, one {אולפן} other {אולפנים}}", - "components.MovieDetails.theatricalrelease": "שיחרור תיאטרלי", - "components.MovieDetails.tmdbuserscore": "TMDB דירוג", - "components.MovieDetails.viewfullcrew": "הצגת הצוות המלא", - "components.MovieDetails.watchtrailer": "צפייה בטריילר", - "components.NotificationTypeSelector.adminissuecommentDescription": "קבלת התראות כשמשתמשים מוסיפים תגובה על התקלה", - "components.NotificationTypeSelector.adminissuereopenedDescription": ".קבלת התראות כשתקלות נפתחות מחדש ע״י משתמשים", - "components.NotificationTypeSelector.adminissueresolvedDescription": ".קבלת התראות כשמשתמש אחר פותר את התקלה", - "components.NotificationTypeSelector.issuecomment": "תגובה על תקלה", - "components.NotificationTypeSelector.issuecommentDescription": ".קבלת התראות כשיש תגובות חדשות לתקלה", - "components.NotificationTypeSelector.issuecreated": "דיווח על תקלה", - "components.NotificationTypeSelector.issuecreatedDescription": ".קבלת התראות כשמדווחים על תקלות", - "components.NotificationTypeSelector.issuereopened": "תקלה נפתחה מחדש", - "components.NotificationTypeSelector.issuereopenedDescription": ".קבלת התראות כשפותחים תקלות מחדש", - "components.NotificationTypeSelector.issueresolved": "תקלה נפתרה", - "components.NotificationTypeSelector.issueresolvedDescription": ".קבלת התראות כשתקלות נפתרות", + "components.MovieDetails.showless": "הראה פחות", + "components.MovieDetails.showmore": "הראה יותר", + "components.MovieDetails.streamingproviders": "זמין כעת ב", + "components.MovieDetails.studio": "{studioCount, plural, one {Studio} other {Studios}}", + "components.Discover.moviegenres": "ז'אנרים של סרטים", + "components.Discover.resetfailed": "משהו השתבש, מאפס את ההתאמה האישית בגילוי.", + "components.Discover.resetwarning": "מאפס את כל המחוונים, זה ימחק כל מחוון מותאם אישית!", + "components.Discover.tmdbtvstreamingservices": "רשתות צפייה של סדרות בTMDB", + "components.DownloadBlock.formattedTitle": "{title}: עונה {seasonNumber} פרק {episodeNumber}", + "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {Episode} other {Episodes}}", + "components.Layout.Sidebar.browsemovies": "סרטים", + "components.Layout.UserWarnings.emailInvalid": "כתובת מייל אינה תקינה.", + "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} מאחור", + "components.Login.signinwithjellyfin": "שימוש בחשבון {mediaServerName}", + "components.Login.validationhostformat": "נדרש קישור תקין", + "components.ManageSlideOver.manageModalClearMediaWarning": "* כל הנתונים שלך ל-{mediaType}, כולל בקשות, ימחקו. אם זה קיים בספרייה של {mediaServerName}, זה יצור אותה מחדש בסריקה הבאה.", + "components.ManageSlideOver.markallseasons4kavailable": "סמן את כל העונות כזמינות ב-4K", + "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {play} other {plays}}", + "components.MovieDetails.managemovie": "נהל סרט", + "components.MovieDetails.overviewunavailable": "סקירה לא זמינה.", + "components.MovieDetails.productioncountries": "נוצר ב-{countryCount, plural, one {Country} other {Countries}}", + "components.MovieDetails.releasedate": "{releaseCount, plural, one {Release Date} other {Release Dates}}", + "components.MovieDetails.reportissue": "דווח על תקלה", + "components.MovieDetails.similar": "כותרים דומים", + "components.Login.emailtooltip": "הכתובת אינה צריכה להיות משוייכת ל-{mediaServerName}.", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* הפעולה תסיר את {mediaType} מ-{arr} כולל כל הקבצים.", + "components.MovieDetails.theatricalrelease": "שיחרור מוגבל", + "components.MovieDetails.tmdbuserscore": "דיגור TMDB", + "components.MovieDetails.viewfullcrew": "צפייה בכל הצוות", + "components.MovieDetails.watchtrailer": "נגן טריילר", + "components.NotificationTypeSelector.adminissuecommentDescription": "קבלת התראות כאשר משתמשים אחרים מגיבים למקרה.", + "components.NotificationTypeSelector.adminissuereopenedDescription": "קבלת התראות כאשר משתמשים אחרים פותחים את המקרה מחדש.", + "components.NotificationTypeSelector.adminissueresolvedDescription": "קבלת התראות כאשר המקרה נפתר ע״י משתמשים אחרים.", + "components.NotificationTypeSelector.issuecomment": "תגובה למקרה", + "components.NotificationTypeSelector.issuecommentDescription": "קבלת התראות כאשר מתקבלות תגובות חדשות למקרים.", + "components.NotificationTypeSelector.issuecreated": "מקרה חדש נפתח", + "components.NotificationTypeSelector.issuecreatedDescription": "קבלת התראות כאשר מקרה חדש נפתח.", + "components.NotificationTypeSelector.issuereopened": "מקרה נפתח מחדש", + "components.NotificationTypeSelector.issuereopenedDescription": "קבלת התראות כאשר מקרים נפתחים מחדש.", + "components.NotificationTypeSelector.issueresolved": "פנייה נפתרה", + "components.NotificationTypeSelector.issueresolvedDescription": "קבלת התראות כאשר פניות נפתרות.", "components.NotificationTypeSelector.mediaAutoApproved": "בקשה אושרה אוטומטית", - "components.NotificationTypeSelector.mediaAutoApprovedDescription": ".קבלת התראות כשמשתמשים יוצרים בקשת מדיה שמאושרת אוטומטית", + "components.NotificationTypeSelector.mediaAutoApprovedDescription": "קבלת התראות כאשר משתמשים פותחים בקשה חדשה שמאושרת אוטומטית.", "components.NotificationTypeSelector.mediaapproved": "בקשה אושרה", - "components.NotificationTypeSelector.mediaapprovedDescription": ".קבלת התראות כשבקשות מדיה מאושרות ידנית", - "components.NotificationTypeSelector.mediaautorequested": "בקשה נוצרת אוטומטית", - "components.NotificationTypeSelector.mediaautorequestedDescription": ".קבלת התראות כשבקשה נוצרת אוטומטית מרשימת הצפייה שלך", - "components.NotificationTypeSelector.mediaavailable": "ניתן ליצור בקשות", - "components.NotificationTypeSelector.mediaavailableDescription": ".קבלת התראות כשניתן ליצור בקשות", + "components.NotificationTypeSelector.mediaapprovedDescription": "שליחת התראות כאשר בקשות מדיה מאושרות אוטומטית.", + "components.NotificationTypeSelector.mediaautorequested": "בקשה נפתחה אוטומטית", + "components.NotificationTypeSelector.mediaautorequestedDescription": "שליחת התראות כאשר הבקשה נוצרת אוטומטית עבור מדיה ברשימת הצפייה.", + "components.NotificationTypeSelector.mediaavailable": "בקשה זמינה", + "components.NotificationTypeSelector.mediaavailableDescription": "שליחת התראות כאשר ניתן ליצור בקשות מדיה.", "components.NotificationTypeSelector.mediadeclined": "בקשה נדחתה", - "components.NotificationTypeSelector.mediadeclinedDescription": ".קבלת הראות כשבקשות מדיה נדחות", + "components.NotificationTypeSelector.mediadeclinedDescription": "קבלת התראות כאשר בקשות מדיה נדחות.", "components.NotificationTypeSelector.mediafailed": "עיבוד הבקשה נכשל", - "components.NotificationTypeSelector.mediafailedDescription": ".Radarr או Sonarr קבלת התראות כשבקשות המדיה אינן מתווספות אל ", "components.NotificationTypeSelector.mediarequested": "בקשה ממתינה לאישור", - "components.NotificationTypeSelector.mediarequestedDescription": ".קבלת התראות כשמשתמשים יוצרים בקשות מדיה שדורשות אישור", - "components.NotificationTypeSelector.notificationTypes": "סוגי התראות", - "components.NotificationTypeSelector.userissuecommentDescription": ".קבלת התראות כשתקלות שדיווחת מקבלות תגובות חדשות", - "components.NotificationTypeSelector.userissuecreatedDescription": ".קבלת התראות כשמשתמשים מדווחים על תקלות", - "components.NotificationTypeSelector.userissuereopenedDescription": ".קבלת התראות שתקלות שדיווחת נפתחות מחדש", - "components.NotificationTypeSelector.userissueresolvedDescription": ".קבלת התראות שתקלות שדיווחת נפתרות", - "components.NotificationTypeSelector.usermediaAutoApprovedDescription": ".קבלת התראות כשמשתמשים אחרים מבקשים מדיה שאושרה אוטומטית", - "components.NotificationTypeSelector.usermediaapprovedDescription": ".קבלת התראות כשבקשות המדיה שביקשת אושרו", - "components.NotificationTypeSelector.usermediaavailableDescription": ".קבלת התראות כשניתן ליצור בקשות חדשות", - "components.NotificationTypeSelector.usermediadeclinedDescription": ".קבלת התראות כשבקשות המדיה שביקשת נדחו", - "components.NotificationTypeSelector.usermediafailedDescription": ".Radarr או Sonarr קבלת התראות כשבקשות המדיה אינן מתווספות אל ", - "components.NotificationTypeSelector.usermediarequestedDescription": ".קבלת התראות כשמשתמשים אחרים מבקשים מדיה שזקוקה לאישור", - "components.PermissionEdit.admin": "אדמין", - "components.PermissionEdit.adminDescription": ".גישת מנהל מלאה. עוקף את כל ההרשאות האחרות", + "components.NotificationTypeSelector.mediarequestedDescription": "קבלת התראות כאשר משתמשים פותחים בקשות מדיה שדורשות אישור.", + "components.NotificationTypeSelector.userissuecreatedDescription": "קבלת התראות כאשר משתמשים אחרים מדווחים על תקלות", + "components.PermissionEdit.admin": "מנהל", + "components.PermissionEdit.adminDescription": "גישת מנהל מלאה. עוקף את כל ההרשאות שסומנו.", "components.PermissionEdit.advancedrequest": "בקשות מתקדמות", - "components.PermissionEdit.advancedrequestDescription": ".הענקת הרשאות לשינוי מתקדם של אפשרויות מדיה", - "components.PermissionEdit.autoapprove": "אישור בקשות אוטומטי", - "components.PermissionEdit.autoapprove4k": "4K אישור בקשות אוטומטי", - "components.PermissionEdit.autoapprove4kDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של מדיה", - "components.PermissionEdit.autoapprove4kMovies": "4K אישור בקשות אוטומטי לסרטים", - "components.PermissionEdit.autoapprove4kMoviesDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של סרטים", - "components.PermissionEdit.autoapprove4kSeries": "4K אישור בקשות אוטומטי לסדרות", - "components.PermissionEdit.autoapprove4kSeriesDescription": ".4K הענקת הרשאה לאישור בקשות אוטומטי של סדרות", - "components.PermissionEdit.autoapproveDescription": ".הענקת הרשאה לאישור אוטומטי של כל בקשות המדיה באיכות גבוהה", - "components.PermissionEdit.autoapproveMovies": "אישור בקשה אוטומטי של סרטים", - "components.PermissionEdit.autoapproveMoviesDescription": ".הענקת הרשאה לאישור בקשות אוטומטי של סרטים באיכות גבוהה", - "components.PermissionEdit.autoapproveSeries": "אישור בקשה אוטומטי של סדרות", - "components.PermissionEdit.autoapproveSeriesDescription": "אישור בקשות אוטומטי עבור סדרות באיכות גבוהה", - "components.PermissionEdit.autorequest": "בקשות אוטומטיות", - "components.PermissionEdit.autorequestDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות עבור מדיה באיכות גבוהה באמצעות רשימת צפייה של", - "components.PermissionEdit.autorequestMovies": "בקשת סרטים אוטומטית", - "components.PermissionEdit.autorequestMoviesDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות סרטים באיכות גבוהה באמצעות רשימת צפייה של", - "components.PermissionEdit.autorequestSeries": "בקשת סדרות אוטומטית", - "components.PermissionEdit.autorequestSeriesDescription": "Plex הענקת הרשאה ליצירה אוטומטית של בקשות סדרות באיכות גבוהה באמצעות רשימת צפייה של", - "components.PermissionEdit.createissues": "דיווח על תקלות", - "components.PermissionEdit.createissuesDescription": ".הענקת הרשאות לדיווח על תקלות מדיה", - "components.PermissionEdit.manageissues": "ניהול תקלות", - "components.PermissionEdit.manageissuesDescription": ".הענקת הרשאות לניהול תקלות מדיה", - "components.PermissionEdit.managerequests": "ניהול בקשות", - "components.PermissionEdit.managerequestsDescription": ".הענקת הרשאות לניהול בקשות מדיה. כל הבקשות שנוצרות ע|י המשתמש עם הרשאה זו יאושרו אוטומטית", - "components.PermissionEdit.request": "בקשות", - "components.PermissionEdit.request4k": "4K בקשות", - "components.PermissionEdit.request4kDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של", - "components.PermissionEdit.request4kMovies": "4K בקשת סרטים", - "components.PermissionEdit.request4kMoviesDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של סרטים", - "components.PermissionEdit.request4kTv": "4K בקשת סדרות", - "components.PermissionEdit.request4kTvDescription": ".4K הענקת הרשאות ליצירת בקשות מדיה של סדרות", - "components.PermissionEdit.requestDescription": ".הענקת הרשאות ליצירת בקשות מדיה באיכות גבוהה", - "components.PermissionEdit.requestMovies": "בקשת סרטים", - "components.PermissionEdit.requestMoviesDescription": ".הענקת הרשאות ליצירת בקשות סרטים באיכות גבוהה", - "components.PermissionEdit.requestTv": "בקשת סדרות", - "components.PermissionEdit.requestTvDescription": ".הענקת הרשאות ליצירת בקשות סדרות באיכות גבוהה", - "components.PermissionEdit.users": "ניהול משתמשים", - "components.PermissionEdit.usersDescription": ".הענקת הרשאות לניהול משתמשים. משתמשים עם ההרשאה הזו אינם יכולים להעניק הראשת מנהל", - "components.PermissionEdit.viewissues": "צפייה בתקלות", - "components.PermissionEdit.viewissuesDescription": ".הענקת הרשאות לצפייה בתקלות מדיה שדווחו ע״י משתמשים", - "components.PermissionEdit.viewrecent": "צפייה בהתווסף לאחרונה", - "components.PermissionEdit.viewrecentDescription": ".הענקת הרשאות לצפייה ברשימה של המדיה שהתווספה לאחרונה", - "components.PermissionEdit.viewrequests": "הצגת הבקשות", - "components.PermissionEdit.viewrequestsDescription": ".הענקת הרשאות להצגת בקשות מדיה שנוצרו ע״י משתמשים", - "components.PermissionEdit.viewwatchlists": "{mediaServerName} צפייה ברשימות צפייה של", - "components.PermissionEdit.viewwatchlistsDescription": ".{mediaServerName} הענקת הרשאה לצפייה ברשימות צפייה של", - "components.PersonDetails.alsoknownas": "{names} מוכר גם כ", - "components.PersonDetails.appearsin": "הופעות", - "components.PersonDetails.ascharacter": "{character} בתור", - "components.PersonDetails.birthdate": "{birthdate} תאריך לידה", - "components.PersonDetails.crewmember": "צוות", - "components.PersonDetails.lifespan": "{birthdate} – {deathdate}", - "components.PlexLoginButton.signingin": "…מתחבר", - "components.PlexLoginButton.signinwithplex": "החברות", - "components.QuotaSelector.days": "{count, plural, one {יום} other {ימים}}", - "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} per {quotaDays} {days}", - "components.QuotaSelector.movies": "{count, plural, one {סרט} other {סרטים}}", - "components.QuotaSelector.seasons": "{count, plural, one {עונה} other {seasonעונותs}}", - "components.QuotaSelector.tvRequests": "{quotaLimit} {seasons} per {quotaDays} {days}", - "components.QuotaSelector.unlimited": "ללא הגבלה", - "components.RegionSelector.regionDefault": "כל האיזורים", - "components.RegionSelector.regionServerDefault": "({region}) ברירת מחדל", - "components.RequestBlock.approve": "אישור בקשה", - "components.RequestBlock.decline": "דחיית בקשה", - "components.RequestBlock.delete": "מחיקת בקשה", - "components.RequestBlock.edit": "עריכת בקשה", - "components.RequestBlock.languageprofile": "פרופיל שפה", - "components.RequestBlock.lastmodifiedby": "שונה לאחרונה ע״י", - "components.RequestBlock.profilechanged": "פרופיל איכות", - "components.RequestBlock.requestdate": "תאריך הבקשה", - "components.RequestBlock.requestedby": "בקשה ע״י", - "components.RequestBlock.requestoverrides": "עקיפות שיש לבקשה", - "components.RequestBlock.rootfolder": "תיקיית מדיה", - "components.RequestBlock.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", - "components.RequestBlock.server": "שרת יעד", - "components.RequestButton.approve4krequests": "אישור {requestCount, plural, one {4K בקשת} other {{requestCount} 4K בקשות}}", - "components.RequestButton.approverequest": "אישור בקשה", - "components.RequestButton.approverequest4k": "4K אישור בקשת", - "components.RequestButton.approverequests": "אישור {requestCount, plural, one {בקשה} other {{requestCount} בקשות}}", - "components.RequestButton.decline4krequests": "דחיית {requestCount, plural, one {4K בקשת} other {{requestCount} 4K בקשות}}", - "components.RequestButton.declinerequest": "דחיית בקשה", - "components.RequestButton.declinerequest4k": "4K דחיית בקשת", - "components.RequestButton.declinerequests": "דחיית {requestCount, plural, one {בקשה} other {{requestCount} בקשות}}", - "components.RequestButton.requestmore": "הוספה לבקשה", - "components.RequestButton.requestmore4k": "4K הוספה לבקשת", - "components.RequestButton.viewrequest": "הצגת הבקשה", - "components.RequestButton.viewrequest4k": "4K הצגת בקשת", - "components.RequestCard.approverequest": "אישור בקשה", - "components.RequestCard.cancelrequest": "ביטול בקשה", - "components.RequestCard.declinerequest": "דחיית בקשה", - "components.RequestCard.deleterequest": "מחיקת בקשה", - "components.RequestCard.editrequest": "עריכת בקשה", - "components.RequestCard.failedretry": ".משהו השתשבש בבקשה החוזרת", - "components.RequestCard.mediaerror": "חסר {mediaType}", - "components.RequestCard.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", - "components.RequestCard.tmdbid": "TMDB ID", - "components.RequestCard.tvdbid": "TheTVDB ID", - "components.RequestCard.unknowntitle": "כותר חסר", - "components.RequestList.RequestItem.cancelRequest": "ביטול בקשה", - "components.RequestList.RequestItem.deleterequest": "מחיקת בקשה", - "components.RequestList.RequestItem.editrequest": "עריכת בקשה", - "components.RequestList.RequestItem.failedretry": ".משהו השתשבש בבקשה החוזרת", - "components.RequestList.RequestItem.mediaerror": "חסר {mediaType}", - "components.RequestList.RequestItem.modified": "השתנה", - "components.RequestList.RequestItem.modifieduserdate": "ע״י {date} {user} ", - "components.RequestList.RequestItem.requested": "התבקש", - "components.RequestList.RequestItem.requesteddate": "התבקש", - "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {עונה} other {עונות}}", - "components.RequestList.RequestItem.tmdbid": "TMDB ID", - "components.RequestList.RequestItem.tvdbid": "TheTVDB ID", - "components.RequestList.RequestItem.unknowntitle": "כותר חסר", - "components.RequestList.requests": "בקשות", - "components.RequestList.showallrequests": "הצגת כל הבקשות", - "components.RequestList.sortAdded": "הכי עדכני", - "components.RequestList.sortModified": "השתנה לאחרונה", - "components.RequestModal.AdvancedRequester.advancedoptions": "מתקדם", - "components.RequestModal.AdvancedRequester.animenote": ". זוהי סדרת אנימה *", - "components.RequestModal.AdvancedRequester.default": "{name} (Default)", - "components.RequestModal.AdvancedRequester.destinationserver": "שרת יעד", - "components.RequestModal.AdvancedRequester.folder": "{path} ({space})", - "components.RequestModal.AdvancedRequester.languageprofile": "פרופיל שפה", - "components.RequestModal.AdvancedRequester.notagoptions": ".אין תגיות", - "components.RequestModal.AdvancedRequester.qualityprofile": "פרופיל איכות", - "components.RequestModal.AdvancedRequester.requestas": "בקשה בתור", - "components.RequestModal.AdvancedRequester.rootfolder": "ספריית מדיה", - "components.RequestModal.AdvancedRequester.selecttags": "בחירת תגיות", - "components.RequestModal.AdvancedRequester.tags": "תגיות", - "components.RequestModal.QuotaDisplay.allowedRequests": ".ימים {days} כל {limit} {type} מותר לבקש", - "components.RequestModal.QuotaDisplay.allowedRequestsUser": ".ימים {days} כל {limit} {type} למשתמש מותר לבקש", - "components.RequestModal.QuotaDisplay.movie": "סרט", - "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {סרט} other {סרטים}}", - "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "אין בקשות עונות שנותרו", - "components.RequestModal.QuotaDisplay.quotaLink": ".בעמוד הפרופיל ניתן לצפות בתקציר מגבלת בקשות", - "components.RequestModal.QuotaDisplay.quotaLinkUser": "You can view a summary of this user's request limits on their profile page.", - "components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {No} other {#}} {type} {remaining, plural, one {בקשה} other {בקשות}} remaining", - "components.RequestModal.QuotaDisplay.requiredquota": ".בכדי לבצע בקשה עבור סדרה זו {seasons} {seasons, plural, one {בקשת סדרה} other {בקשות סדרה}} צריך להיות ברשותך לפחות", - "components.RequestModal.QuotaDisplay.requiredquotaUser": ".בכדי לבצע בקשה עבור סדרה זו {seasons} {seasons, plural, one {בקשת סדרה} other {בקשות סדרה}} למשתמש זה צריך להיות לפחות", - "components.RequestModal.QuotaDisplay.season": "עונה", - "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {עונה} other {עונות}}", - "components.RequestModal.SearchByNameModal.nomatches": ".אין התאמות עבור סדרה זו", - "components.RequestModal.SearchByNameModal.notvdbiddescription": ".אין אפשרות למצוא התאמה אוטומטית. יש לבחור את הסדרה הנכונה ברשימה מטה", - "components.RequestModal.alreadyrequested": "כבר התבקש", - "components.RequestModal.approve": "אישור הבקשה", - "components.RequestModal.autoapproval": "אישור אוטומטי", - "components.RequestModal.cancel": "ביטול הבקשה", - "components.RequestModal.edit": "עריכת הבקשה", - "components.RequestModal.errorediting": ".משהו השתבש בעת עריכת הבקשה", - "components.RequestModal.extras": "אקסטרה", - "components.RequestModal.numberofepisodes": "פרקים #", - "components.RequestModal.pending4krequest": "בקשת 4K ממתינה", - "components.RequestModal.pendingapproval": ".הבקשה ממתינה לאישור", - "components.RequestModal.pendingrequest": "בקשה ממתינה", - "components.RequestModal.requestApproved": "!אושרה {title} הבקשה עבור", - "components.RequestModal.requestCancel": ".בוטלה {title} הבקשה עבור", - "components.RequestModal.requestSuccess": "!{title} התבקשה בהצלחה", - "components.RequestModal.requestadmin": ".בקשה זו תאושר אוטומטית", - "components.RequestModal.requestcancelled": ".בוטלה {title} הבקשה עבור", - "components.RequestModal.requestcollection4ktitle": "4K בקשת האוסף ב", - "components.RequestModal.requestcollectiontitle": "בקשת האוסף", - "components.RequestModal.requestedited": "!נערכה בהצלחה {title} הבקשה עבור", - "components.RequestModal.requesterror": ".משהו השתבש בהגשת הבקשה", - "components.RequestModal.requestfrom": ".ממתינה לאישור {username} הבקשה של", - "components.RequestModal.requestmovie4ktitle": "4K בקשת סרט", - "components.RequestModal.requestmovies": "בקשת {count} {count, plural, one {סרט} other {סרטים}}", - "components.RequestModal.requestmovies4k": "בקשת {count} {count, plural, one {סרט} other {סרטים}} 4K ב", - "components.RequestModal.requestmovietitle": "בקשת סרט", - "components.RequestModal.requestseasons": "בקשה של {seasonCount} {seasonCount, plural, one {עונה} other {עונות}}", - "components.RequestModal.requestseasons4k": "בקשה של {seasonCount} {seasonCount, plural, one {עונה} other {עונות}} in 4K", - "components.RequestModal.requestseries4ktitle": "4K בקשת סדרה", - "components.RequestModal.requestseriestitle": "בקשת סדרה", - "components.RequestModal.season": "עונה", - "components.RequestModal.seasonnumber": "עונה {number}", - "components.RequestModal.selectmovies": "(ים)בחירת סרט", - "components.RequestModal.selectseason": "(ות)בחירת עונה", - "components.ResetPassword.confirmpassword": "אימות סיסמה", - "components.ResetPassword.email": "כתובת מייל", - "components.ResetPassword.emailresetlink": "שליחת מייל עם קישור לשיחזור", - "components.ResetPassword.gobacklogin": "חזרה לעמוד לוגאין", - "components.ResetPassword.password": "סיסמה", - "components.ResetPassword.passwordreset": "איפוס סיסמה", - "components.ResetPassword.requestresetlinksuccessmessage": ".קישור לאיפוס הסיסמה יישלח לכתובת המייל המסופקת אם היא משוייכת למשתמש", - "components.ResetPassword.resetpassword": "איפוס סיסמה", - "components.ResetPassword.resetpasswordsuccessmessage": "!הסיסמה אופסה בהצלחה", - "components.ResetPassword.validationemailrequired": "יש לרשום כתובת מייל חוקית", - "components.ResetPassword.validationpasswordmatch": "הסיסמאות צריכות להיות זהות", - "components.ResetPassword.validationpasswordminchars": "הסיסמה צריכה להיות באורך 8 תווים", - "components.ResetPassword.validationpasswordrequired": "יש לרשום סיסמה", - "components.Search.search": "חיפוש", - "components.Search.searchresults": "תוצאות חיפוש", - "components.Selector.nooptions": "אין תוצאות", - "components.Selector.searchGenres": "…בחירת ג׳אנרים", - "components.Selector.searchKeywords": "…מילות מפתח לחיפוש", - "components.Selector.searchStudios": "…חיפוש אולפנים", - "components.Selector.showless": "הצג פחות", - "components.Selector.showmore": "עוד", - "components.Selector.starttyping": ".יש להקליד כדי להתחיל חיפוש", - "components.Settings.Notifications.NotificationsGotify.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": ".Gotify אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "!נשמרו בהצלחה Gotify הגדרות התראות", - "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": ".נכשלה Gotify שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "…בתהליך שליחה Gotify הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "!נשלחה בהצלחה Gotify הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsGotify.token": "יישום Token", - "components.Settings.Notifications.NotificationsGotify.url": "כתובת השרת", - "components.Settings.Notifications.NotificationsGotify.validationTokenRequired": "עבור היישום Token יש לרשום", - "components.Settings.Notifications.NotificationsGotify.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "יש לרשום כתובת תקינה", - "components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", - "components.Settings.Notifications.NotificationsLunaSea.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsLunaSea.profileName": "שם הפרופיל", - "components.Settings.Notifications.NotificationsLunaSea.profileNameTip": "default נדרש רק אם משתמשים בפרופיל", - "components.Settings.Notifications.NotificationsLunaSea.settingsFailed": ".LunaSea אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsLunaSea.settingsSaved": "!נשמרו בהצלחה LunaSea ההגדרות עבור", - "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestFailed": ".נכשלה LunaSea שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSending": "…בתהליך שליחה LunaSea הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSuccess": "!נשלחה בהצלחה LunaSea הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsLunaSea.validationTypes": "יש לבחור לפחות סוג התראה אחת", - "components.Settings.Notifications.NotificationsLunaSea.validationWebhookUrl": "יש לרשום כתובת תקינה", - "components.Settings.Notifications.NotificationsLunaSea.webhookUrl": "Webhook כתובת", - "components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Webhook כתובת התראות", - "components.Settings.Notifications.NotificationsPushbullet.accessToken": "Access Token", - "components.Settings.Notifications.NotificationsPushbullet.accessTokenTip": "בהגדרות החשבון Token ניתן ליצור", - "components.Settings.Notifications.NotificationsPushbullet.agentEnabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsPushbullet.channelTag": "תגית ערוץ", - "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsFailed": ".Pushbullet אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsSaved": "!נשמרו בהצלחה Pushbullet הגדרות התראות", - "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": ".נכשלה Pushbullet שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSending": "…בתהליך שליחה Pushbullet הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSuccess": "!נשלחה בהצלחה Pushbullet הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsPushbullet.validationAccessTokenRequired": "access token יש לרשום", - "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.NotificationsPushover.accessToken": "של יישום API Token", - "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Jellyseerr רישום יישום לשימוש עם", - "components.Settings.Notifications.NotificationsPushover.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "ברירת המחדל של המכשיר", - "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": ".Pushover אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "!נשמרו בהצלחה Pushover הגדרות התראות", - "components.Settings.Notifications.NotificationsPushover.sound": "צליל התראה", - "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": ".נכשלה Pushover שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "…בתהליך שליחה Pushover הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSuccess": "!נשלחה בהצלחה Pushover הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsPushover.userToken": "מפתח משתמש או קבוצה", - "components.Settings.Notifications.NotificationsPushover.userTokenTip": "מזהה מפתח או קבוצה באורך 30 תווים", - "components.Settings.Notifications.NotificationsPushover.validationAccessTokenRequired": "access token יש לרשום", - "components.Settings.Notifications.NotificationsPushover.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "יש לרשום מפתח משתמש או קבוצה תקין", - "components.Settings.Notifications.NotificationsSlack.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": ".Slack אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "!נשמרו בהצלחה Slack הגדרות התראות", - "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": ".נכשלה Slack שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsSlack.toastSlackTestSending": "…בתהליך שליחה Slack הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsSlack.toastSlackTestSuccess": "!נשלחה בהצלחה Slack הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsSlack.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.NotificationsSlack.validationWebhookUrl": "יש לרשום כתובת תקינה", - "components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook כתובת", - "components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Webhook יצירת אינטגרציה של", - "components.Settings.Notifications.NotificationsWebPush.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": ".HTTPS צריך להיות מוגדר ב Jellyseerr בכדי לקבל התראות פוש", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": ".נכשלה Web שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "…בתהליך שליחה Web הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "!נשלחה בהצלחה Web הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": ".Web אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "!נשמרו בהצלחה Web הגדרות התראות", - "components.Settings.Notifications.NotificationsWebhook.agentenabled": "איפשור יישום", - "components.Settings.Notifications.NotificationsWebhook.authheader": "Authorization Header", - "components.Settings.Notifications.NotificationsWebhook.customJson": "JSON Payload", - "components.Settings.Notifications.NotificationsWebhook.resetPayload": "איפוס לברירת מחדל", - "components.Settings.Notifications.NotificationsWebhook.resetPayloadSuccess": "!בוצע בהצלחה JSON איפוס", - "components.Settings.Notifications.NotificationsWebhook.templatevariablehelp": "עזרה עם משתני התבנית", - "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": ".נכשלה Webhook שליחת הודעת בדיקה", - "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSending": "…בתהליך שליחה Webhook הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSuccess": "!נשלחה בהצלחה Webhook הודעת בדיקה אל", - "components.Settings.Notifications.NotificationsWebhook.validationJsonPayloadRequired": "תקין JSON יש לרשום", - "components.Settings.Notifications.NotificationsWebhook.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.NotificationsWebhook.validationWebhookUrl": "יש לרשום כתובת תקינה", - "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook כתובת", - "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": ".Webhook אירעה שגיאה בשמירת הגדרות עבור", - "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "!נשמרו בהצלחה Webhook הגדרות התראות", - "components.Settings.Notifications.agentenabled": "איפשור יישום", - "components.Settings.Notifications.allowselfsigned": "איפשור תעודות חתומות עצמית", - "components.Settings.Notifications.authPass": "SMTP סיסמה", - "components.Settings.Notifications.authUser": "SMTP משתמש", - "components.Settings.Notifications.botAPI": "אימות של בוט Token", - "components.Settings.Notifications.botApiTip": "Jellyseerr לשימוש עם יצירת בוט", - "components.Settings.Notifications.botAvatarUrl": "כתובת של תמונת בוט", - "components.Settings.Notifications.botUsername": "משתמש בוט", - "components.Settings.Notifications.botUsernameTip": "מאפשר למשתמשים להתחיל צ׳אט עם הבוט ולהגדיר את ההתראות לעצמם", - "components.Settings.Notifications.chatId": "Chat ID", - "components.Settings.Notifications.chatIdTip": "/my_id ובדיקה של @get_id_bot התחלת צ׳אט עם הבוט הוספת,", - "components.Settings.Notifications.discordsettingsfailed": ".Discord אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.Settings.Notifications.discordsettingssaved": "!נשמרו בהצלחה Discord הגדרות התראות", - "components.Settings.Notifications.emailsender": "כתובת השולח", - "components.Settings.Notifications.emailsettingsfailed": ".אירעה שגיאה בעת שמירת הגדרות התראות למייל", - "components.Settings.Notifications.emailsettingssaved": "!הגדרות התראות למייל נשמרו בהצלחה", - "components.Settings.Notifications.enableMentions": "אפישור איזכורים", - "components.Settings.Notifications.encryption": "סוג הצפנה", - "components.Settings.Notifications.encryptionDefault": "אם זמין STARTTLS השתמש ב", - "components.Settings.Notifications.encryptionImplicitTls": "TLS שימוש ב", - "components.Settings.Notifications.encryptionNone": "ללא", - "components.Settings.Notifications.encryptionOpportunisticTls": "STARTTLS תמיד יש להשתמש ב", - "components.Settings.Notifications.encryptionTip": "ברוב המקרים TLS משתמש בפורט 465 ו STARTLS בפורט 587", - "components.Settings.Notifications.pgpPassword": "PGP סיסמת", - "components.Settings.Notifications.pgpPasswordTip": "OpenPGP הצפן הודעת מייל באמצעות", - "components.Settings.Notifications.pgpPrivateKey": "PGP Private Key", - "components.Settings.Notifications.pgpPrivateKeyTip": "OpenPGP הצפן הודעת מייל באמצעות", - "components.Settings.Notifications.sendSilently": "שליחה שקטה", - "components.Settings.Notifications.sendSilentlyTip": "שליחת התראות ללא צליל", - "components.Settings.Notifications.senderName": "שם השולח", - "components.Settings.Notifications.smtpHost": "SMTP מארח", - "components.Settings.Notifications.smtpPort": "SMTP Port", - "components.Settings.Notifications.telegramsettingsfailed": ".Telegram אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.Settings.Notifications.telegramsettingssaved": "!נשמרו בהצלחה Telegram הגדרות התראות", - "components.Settings.Notifications.toastDiscordTestFailed": ".נכשלה Discord שליחת הודעת בדיקה", - "components.Settings.Notifications.toastDiscordTestSending": "…בתהליך שליחה Discord הודעת בדיקה אל", - "components.Settings.Notifications.toastDiscordTestSuccess": "!נשלחה בהצלחה Discord הודעת בדיקה אל", - "components.Settings.Notifications.toastEmailTestFailed": ".שליחת הודעת מייל לבדיקה נכשלה", - "components.Settings.Notifications.toastEmailTestSending": "…שליחת הודעת מייל לבדיקה", - "components.Settings.Notifications.toastEmailTestSuccess": "!הודעת בדיקת מייל נשלחה בהצלחה", - "components.Settings.Notifications.toastTelegramTestFailed": ".נכשלה Telegram שליחת הודעת בדיקה", - "components.Settings.Notifications.toastTelegramTestSending": "…בתהליך שליחה Telegram הודעת בדיקה אל", - "components.Settings.Notifications.toastTelegramTestSuccess": "!נשלחה בהצלחה Telegram הודעת בדיקה אל", - "components.Settings.Notifications.userEmailRequired": "דרישת מייל משתמש", - "components.Settings.Notifications.validationBotAPIRequired": "authorization token יש לרשום", - "components.Settings.Notifications.validationChatIdRequired": "chat ID יש לרשום", - "components.Settings.Notifications.validationEmail": "יש לרשום כתובת מייל תקינה", - "components.Settings.Notifications.validationPgpPassword": "PGP יש לרשום סיסמת", - "components.Settings.Notifications.validationPgpPrivateKey": "תקין PGP private key יש לרשום", - "components.Settings.Notifications.validationSmtpHostRequired": "או מארח תקינים IP Address יש לרשום", - "components.Settings.Notifications.validationSmtpPortRequired": "יש לרשום מספר פורט תקין", - "components.Settings.Notifications.validationTypes": "יש לבחור לפחות סוג התראה אחד", - "components.Settings.Notifications.validationUrl": "יש לרשום כתובת", - "components.Settings.Notifications.webhookUrl": "Webhook כתובת", - "components.Settings.Notifications.webhookUrlTip": "בשרת webhook אינטגרציית יצירת", - "components.Settings.RadarrModal.add": "הוספת השרת", - "components.Settings.RadarrModal.announced": "הוכרז", - "components.Settings.RadarrModal.apiKey": "API Key", - "components.Settings.RadarrModal.baseUrl": "תחילית לכתובת השרת", - "components.Settings.RadarrModal.create4kradarr": "חדש Radarr 4K הוספת שרת", - "components.Settings.RadarrModal.createradarr": "Radarr הוספת שרת", - "components.Settings.RadarrModal.default4kserver": "4K קבע כשרת ברירת מחדל", - "components.Settings.RadarrModal.defaultserver": "שרת ברירת מחדל", - "components.Settings.RadarrModal.edit4kradarr": "4K Radarr ערוך שרת", - "components.Settings.RadarrModal.editradarr": "Radarr ערוך שרת", - "components.Settings.RadarrModal.enableSearch": "אפשר חיפוש אוטומטי", - "components.Settings.RadarrModal.externalUrl": "כתובת פומבית", - "components.Settings.RadarrModal.hostname": "IP שם מארח או כתובת", - "components.Settings.RadarrModal.inCinemas": "בקולנוע", - "components.Settings.RadarrModal.loadingTags": "טוען תגיות…", - "components.Settings.RadarrModal.loadingprofiles": "…טוען פרופילי איכות", - "components.Settings.RadarrModal.loadingrootfolders": "…טוען תיקיות מדיה", - "components.Settings.RadarrModal.minimumAvailability": "זמינות מינימאלית", - "components.Settings.RadarrModal.notagoptions": ".אין תגיות", - "components.Settings.RadarrModal.port": "פורט", - "components.Settings.RadarrModal.qualityprofile": "פרופיל איכות", - "components.Settings.RadarrModal.released": "שוחרר", - "components.Settings.RadarrModal.rootfolder": "תיקיית מדיה", - "components.Settings.RadarrModal.selectMinimumAvailability": "בחירת זמינות מינימלית", - "components.Settings.RadarrModal.selectQualityProfile": "בחירת פרופיל איכות", - "components.Settings.RadarrModal.selectRootFolder": "בחירת תקיית מדיה", - "components.Settings.RadarrModal.selecttags": "בחירת תגיות", - "components.Settings.RadarrModal.server4k": "4K שרת", - "components.Settings.RadarrModal.servername": "שם השרת", - "components.Settings.RadarrModal.ssl": "SSL שימוש ב", - "components.Settings.RadarrModal.syncEnabled": "אפשר סריקה", - "components.Settings.RadarrModal.tagRequests": "תיוג בקשות", - "components.Settings.RadarrModal.tagRequestsInfo": "הוספת תגית אוטומטית עם מזהה המשתמש ושם התצוגה", - "components.Settings.RadarrModal.tags": "תגיות", - "components.Settings.RadarrModal.testFirstQualityProfiles": "לטעינת פרופיל איכות, יש ללחוץ על בדיקה", - "components.Settings.RadarrModal.testFirstRootFolders": "לטעינת ספריות מדיה, יש ללחוץ על בדיקה", - "components.Settings.RadarrModal.testFirstTags": "לטעינת תגיות, יש ללחוץ על בדיקה", - "components.Settings.RadarrModal.toastRadarrTestFailure": ".Radarr התחברות נכשלה אל", - "components.Settings.RadarrModal.toastRadarrTestSuccess": "!נוצר בהצלחה Radarr החיבור אל", - "components.Settings.RadarrModal.validationApiKeyRequired": "תקין API Key יש לרשום", - "components.Settings.RadarrModal.validationApplicationUrl": "יש לרשום כתובת שרת תקינה", - "components.Settings.RadarrModal.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", - "components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "יש להתחיל עם סלאש", - "components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", - "components.Settings.RadarrModal.validationHostnameRequired": "תקינה IP יש לרשום שם מאחר או כתובת", - "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "יש לבחור זמינות מינימאלית", - "components.Settings.RadarrModal.validationNameRequired": "יש לרשום שם שרת", - "components.Settings.RadarrModal.validationPortRequired": "יש לרשום פורט תקין", - "components.Settings.RadarrModal.validationProfileRequired": "יש לבחור פרופיל איכות", - "components.Settings.RadarrModal.validationRootFolderRequired": "יש לבחור תיקיית מדיה", - "components.Settings.SettingsAbout.Releases.currentversion": "נוכחית", - "components.Settings.SettingsAbout.Releases.latestversion": "עדכנית", - "components.Settings.SettingsAbout.Releases.releasedataMissing": ".מידע על גרסה אינו זמין", - "components.Settings.SettingsAbout.Releases.releases": "גרסאות", - "components.Settings.SettingsAbout.Releases.versionChangelog": "{version} שינויים לגרסה", - "components.Settings.SettingsAbout.Releases.viewchangelog": "צפייה בשינויים", - "components.Settings.SettingsAbout.Releases.viewongithub": "GitHub צפייה ב", - "components.Settings.SettingsAbout.about": "אודות", - "components.Settings.SettingsAbout.appDataPath": "תיקיית מדיה", - "components.Settings.SettingsAbout.betawarning": "!GitHub זוהי גרסת בטה. יכולות עלולות להיות שבורות או להתנהג באופן בלתי צפוי. אנא דווחו על כל תקלה", - "components.Settings.SettingsAbout.documentation": "דוקומנטציה", - "components.Settings.SettingsAbout.gettingsupport": "קבלת תמיכה", - "components.Settings.SettingsAbout.githubdiscussions": "GitHub דיונים ב", - "components.Settings.SettingsAbout.helppaycoffee": "הזמינו אותי לקפה", - "components.Settings.SettingsAbout.outofdate": "ישן", - "components.Settings.SettingsAbout.overseerrinformation": "Jellyseerr אודות", - "components.Settings.SettingsAbout.preferredmethod": "מועדף", - "components.Settings.SettingsAbout.runningDevelop": ".מומלץ להתקין ממנו רק עבור מפתחים או משתמשים שתורמים בפיתוח ו/או מוכנים לנסות את הפיתוחים האחרונים develop הבראנץ הנוכחי הוא", - "components.Settings.SettingsAbout.supportjellyseerr": "Jellyseerr תמיכה ב", - "components.Settings.SettingsAbout.supportoverseerr": "Overseerr תמיכה ב", - "components.Settings.SettingsAbout.timezone": "איזור זמן", - "components.Settings.SettingsAbout.totalmedia": "סה״כ מדיה", - "components.Settings.SettingsAbout.totalrequests": "סה״כ בקשות", - "components.Settings.SettingsAbout.uptodate": "מעודכן", - "components.Settings.SettingsAbout.version": "גרסה", - "components.Settings.SettingsJobsCache.availability-sync": "סינכרון זמינות מדיה", - "components.Settings.SettingsJobsCache.cache": "מטמון", - "components.Settings.SettingsJobsCache.cacheDescription": ".מיותרות API חיצוניות בכדי לייעל ולהימנע מקריאות API ממחזר קריאות Jellyseerr", - "components.Settings.SettingsJobsCache.cacheflushed": ".נוקה {cachename} המטמון", - "components.Settings.SettingsJobsCache.cachehits": "הפעלות", - "components.Settings.SettingsJobsCache.cachekeys": "סה״כ מפתחות", - "components.Settings.SettingsJobsCache.cacheksize": "גודל מפתח", - "components.Settings.SettingsJobsCache.cachemisses": "החמצות", - "components.Settings.SettingsJobsCache.cachename": "שם מטמון", - "components.Settings.SettingsJobsCache.cachevsize": "גודל ערך", - "components.Settings.SettingsJobsCache.canceljob": "ביטול משימה", - "components.Settings.SettingsJobsCache.command": "פעולה", - "components.Settings.SettingsJobsCache.download-sync": "סינכרון הורדות", - "components.Settings.SettingsJobsCache.download-sync-reset": "איפוס סינכון הורדות", - "components.Settings.SettingsJobsCache.editJobSchedule": "עריכת משימה", - "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "תדירות נוכחית", - "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "תדירות חדשה", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "כל {jobScheduleHours, plural, one {שעה} other {{jobScheduleHours} שעות}}", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "כל {jobScheduleMinutes, plural, one {דקה} other {{jobScheduleMinutes} דקות}}", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "כל {jobScheduleSeconds, plural, one {שניה} other {{jobScheduleSeconds} שניות}}", - "components.Settings.SettingsJobsCache.flushcache": "ניקוי מטמון", - "components.Settings.SettingsJobsCache.image-cache-cleanup": "ניקוי מטמון תמונות", - "components.Settings.SettingsJobsCache.imagecache": "מטמון תמונות", - "components.Settings.SettingsJobsCache.imagecacheDescription": "מחדש יתחבר וישמור מטמון של תמונות ממקורות חיצוניים. התמונות נשמרות בתיקיית הקונפיגורציה. ניתן למצוא את הקבצים בנתיב הבא Jellyseerr ,כאשר מופעל בהגדרות {appDataPath}/cache/images", - "components.Settings.SettingsJobsCache.imagecachecount": "תמונות נשמרו במטמון", - "components.Settings.SettingsJobsCache.imagecachesize": "גודל מטמון", - "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Jellyfin סריקה מלאה לספריות", - "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "אחר התווסף לאחרונה Jellyfin סריקת", - "components.Settings.SettingsJobsCache.jobScheduleEditFailed": ".משהו השתבש בשמירת המשימה", - "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "!המשימה עודכנה בהצלחה", - "components.Settings.SettingsJobsCache.jobcancelled": ".בוטלה {jobname}", - "components.Settings.SettingsJobsCache.jobname": "שם משימה", - "components.Settings.SettingsJobsCache.jobs": "משימות", - "components.Settings.SettingsJobsCache.jobsDescription": ".מבצע משימות תחזוקה כמשימות מתוזמנות מראש. משימות אלו ניתנות להפעלה באופן יזום ללא השפעה על לו״ז המשימות Jellyseerr", - "components.Settings.SettingsJobsCache.jobsandcache": "משימות & מטמון", - "components.Settings.SettingsJobsCache.jobstarted": ".התחילה {jobname}", - "components.Settings.SettingsJobsCache.jobtype": "סוג", - "components.Settings.SettingsJobsCache.nextexecution": "ההרצה הבאה", - "components.Settings.SettingsJobsCache.plex-full-scan": "מלאה Plex סריקת ספריית", - "components.Settings.SettingsJobsCache.plex-recently-added-scan": "אחר התווסף לאחרונה Plex סריקת", - "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex סיכנרון רשימת צפייה של", - "components.Settings.SettingsJobsCache.process": "תהליך", - "components.Settings.SettingsJobsCache.radarr-scan": "Radarr סריקת", - "components.Settings.SettingsJobsCache.runnow": "הפעלה", - "components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr סריקת", - "components.Settings.SettingsJobsCache.unknownJob": "משימה לא ידועה", - "components.Settings.SettingsLogs.copiedLogMessage": ".לוע הועתק ללוח", - "components.Settings.SettingsLogs.copyToClipboard": "העתקה ללוח", - "components.Settings.SettingsLogs.extraData": "מידע נוסף", - "components.Settings.SettingsLogs.filterDebug": "Debug", - "components.Settings.SettingsLogs.filterError": "Error", - "components.Settings.SettingsLogs.filterInfo": "Info", - "components.Settings.SettingsLogs.filterWarn": "Warning", - "components.Settings.SettingsLogs.label": "תווית", - "components.Settings.SettingsLogs.level": "דחיפות", - "components.Settings.SettingsLogs.logDetails": "מידע לוג", - "components.Settings.SettingsLogs.logs": "לוגים", - "components.Settings.SettingsLogs.logsDescription": "או בנתיב stdout ניתן לצפות בלוגים הללו באמצעות {appDataPath}/logs/overseerr.log", - "components.Settings.SettingsLogs.message": "הודעה", - "components.Settings.SettingsLogs.pauseLogs": "השהיית הלו״ז", - "components.Settings.SettingsLogs.resumeLogs": "המשך", - "components.Settings.SettingsLogs.showall": "הצגת כל הלוגים", - "components.Settings.SettingsLogs.time": "זמן", - "components.Settings.SettingsLogs.viewdetails": "פרטים", - "components.Settings.SettingsMain.apikey": "API Key", - "components.Settings.SettingsMain.applicationTitle": "כותרת היישום", - "components.Settings.SettingsMain.applicationurl": "תחילית עבור הכתובת", - "components.Settings.SettingsMain.cacheImages": "הפעלת מטמון לתמונות", - "components.Settings.SettingsMain.cacheImagesTip": "שמירה תמונות חיצוניות במטמון (דורש שטח דיסק גדול בהרבה)", - "components.Settings.SettingsMain.csrfProtection": "CSRF הפעלת הגנת", - "components.Settings.SettingsMain.csrfProtectionHoverTip": "!יש לאפשר הגדרה זו רק אם מבינים בנושא", - "components.Settings.SettingsMain.csrfProtectionTip": "(HTTPS דורש) לקריאה בלבד Api קבע את ה", - "components.Settings.SettingsMain.general": "כללי", - "components.Settings.SettingsMain.generalsettings": "הגדרות כלליות", - "components.Settings.SettingsMain.generalsettingsDescription": ".Jellyseerr הגדרות גלובליות וברירות מחדל עבור", - "components.Settings.SettingsMain.hideAvailable": "הסתרת מדיה זמינה", - "components.Settings.SettingsMain.locale": "שפת התצוגה", - "components.Settings.SettingsMain.originallanguage": "שפת מה חדש", - "components.Settings.SettingsMain.originallanguageTip": "סינון תכנים בשפה המועדפת", - "components.Settings.SettingsMain.partialRequestsEnabled": "בקשה חלקית של סדרות", - "components.Settings.SettingsMain.region": "איזור עבור מה חדש", - "components.Settings.SettingsMain.regionTip": "סינון תכנים לפי זמינות איזורית", - "components.Settings.SettingsMain.toastApiKeyFailure": ".חדש API key משהו השתבש ביצירת", - "components.Settings.SettingsMain.toastApiKeySuccess": "!חדש נוצר בהצלחה Api Key", - "components.Settings.SettingsMain.toastSettingsFailure": ".משהו השתבש בעת שמירת ההגדרות", - "components.Settings.SettingsMain.toastSettingsSuccess": "!ההגדרות נשמרו בהצלחה", - "components.Settings.SettingsMain.trustProxy": "הפעלת תמיכב בפרוקסי", - "components.Settings.SettingsMain.trustProxyTip": "מאחורי פרוקסי IP לרשום כתובות של Jellyseerr איפשור ל", - "components.Settings.SettingsMain.validationApplicationTitle": "יש לרשום כותרת יישום", - "components.Settings.SettingsMain.validationApplicationUrl": "יש לרשום כתובת תקינה", - "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", - "components.Settings.SettingsUsers.defaultPermissions": "הרשאות ברירת מחדל", - "components.Settings.SettingsUsers.defaultPermissionsTip": "הרשאות התחלתיות למשתמשים חדשים", - "components.Settings.SettingsUsers.localLogin": "איפשור התחברות מקומית", - "components.Settings.SettingsUsers.localLoginTip": "{mediaServerName} OAuth אפשר למשתמשים לבצע לוגאין באמצעות מייל וסיסמה במקום", - "components.Settings.SettingsUsers.movieRequestLimitLabel": "מגבלת גלוגלית לבקשת סרטים", - "components.Settings.SettingsUsers.newPlexLogin": "חדשים לבצע לוגאין {mediaServerName} לאפשר למשתמשי", - "components.Settings.SettingsUsers.newPlexLoginTip": "לבצע לוגאין ללא ייבוא מראש {mediaServerName} אפשר למשתמשי", - "components.Settings.SettingsUsers.toastSettingsFailure": ".אירעה שגיאה בעת שמירת ההגדרות", - "components.Settings.SettingsUsers.toastSettingsSuccess": "!הגדרות המשתמש נשמרו בהצלחה", - "components.Settings.SettingsUsers.tvRequestLimitLabel": "מגבלת גלוגלית לבקשת סדרות", - "components.Settings.SettingsUsers.userSettings": "הגדרות משתמש", - "components.Settings.SettingsUsers.userSettingsDescription": ".קביעת הגדרות משתמש וערכי ברירת מחדל", - "components.Settings.SettingsUsers.users": "משתמשים", - "components.Settings.SonarrModal.add": "הוספת השרת", - "components.Settings.SonarrModal.animeSeriesType": "סוג סדרת אנימה", - "components.Settings.SonarrModal.animeTags": "תגיות אנימה", - "components.Settings.SonarrModal.animelanguageprofile": "פרופיל שפת אנימה", - "components.Settings.SonarrModal.animequalityprofile": "פרופיל איכות אנימה", - "components.Settings.SonarrModal.animerootfolder": "ספריות מדיה של אנימה", - "components.Settings.SonarrModal.apiKey": "API Key", - "components.Settings.SonarrModal.baseUrl": "תחילית לכתובת השרת", - "components.Settings.SonarrModal.create4ksonarr": "חדש Sonarr 4K הוספת שרת", - "components.Settings.SonarrModal.createsonarr": "Sonarr הוספת שרת", - "components.Settings.SonarrModal.default4kserver": "4K קבע כשרת ברירת מחדל", - "components.Settings.SonarrModal.defaultserver": "שרת ברירת מחדל", - "components.Settings.SonarrModal.edit4ksonarr": "4K Sonarr ערוך שרת", - "components.Settings.SonarrModal.editsonarr": "Sonarr ערוך שרת", - "components.Settings.SonarrModal.enableSearch": "אפשר חיפוש אוטומטי", - "components.Settings.SonarrModal.externalUrl": "כתובת פומבית", - "components.Settings.SonarrModal.hostname": "IP שם מארח או כתובת", - "components.Settings.SonarrModal.languageprofile": "פרופיל שפה", - "components.Settings.SonarrModal.loadingTags": "טוען תגיות…", - "components.Settings.SonarrModal.loadinglanguageprofiles": "…טוען פרופילי שפה", - "components.Settings.SonarrModal.loadingprofiles": "…טוען פרופילי איכות", - "components.Settings.SonarrModal.loadingrootfolders": "…טוען תיקיות מדיה", - "components.Settings.SonarrModal.notagoptions": ".אין תגיות", - "components.Settings.SonarrModal.port": "פורט", - "components.Settings.SonarrModal.qualityprofile": "פרופיל איכות", - "components.Settings.SonarrModal.rootfolder": "תיקיית מדיה", - "components.Settings.SonarrModal.seasonfolders": "תיקייה לכל עונה", - "components.Settings.SonarrModal.selectLanguageProfile": "בחירת פרופיל שפה", - "components.Settings.SonarrModal.selectQualityProfile": "בחירת פרופיל איכות", - "components.Settings.SonarrModal.selectRootFolder": "בחירת תקיית מדיה", - "components.Settings.SonarrModal.selecttags": "בחירת תגיות", - "components.Settings.SonarrModal.seriesType": "סוג סדרה", - "components.Settings.SonarrModal.server4k": "4K שרת", - "components.Settings.SonarrModal.servername": "שם השרת", - "components.Settings.SonarrModal.ssl": "SSL שימוש ב", - "components.Settings.SonarrModal.syncEnabled": "אפשר סריקה", - "components.Settings.SonarrModal.tagRequests": "תיוג בקשות", - "components.Settings.SonarrModal.tagRequestsInfo": "הוספת תגית אוטומטית עם מזהה המשתמש ושם התצוגה", - "components.Settings.SonarrModal.tags": "תגיות", - "components.Settings.SonarrModal.testFirstLanguageProfiles": "לטעינת פרופילי שפה, יש ללחוץ על בדיקה", - "components.Settings.SonarrModal.testFirstQualityProfiles": "לטעינת פרופיל איכות, יש ללחוץ על בדיקה", - "components.Settings.SonarrModal.testFirstRootFolders": "לטעינת ספריות מדיה, יש ללחוץ על בדיקה", - "components.Settings.SonarrModal.testFirstTags": "לטעינת תגיות, יש ללחוץ על בדיקה", - "components.Settings.SonarrModal.toastSonarrTestFailure": ".Sonarr התחברות נכשלה אל", - "components.Settings.SonarrModal.toastSonarrTestSuccess": "!נוצר בהצלחה Sonarr החיבור אל", - "components.Settings.SonarrModal.validationApiKeyRequired": "תקין API Key יש לרשום", - "components.Settings.SonarrModal.validationApplicationUrl": "יש לרשום כתובת שרת תקינה", - "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "אין לרשום סלאש בסוף הכתובת", - "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "יש להתחיל עם סלאש", - "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", - "components.Settings.SonarrModal.validationHostnameRequired": "תקינה IP יש לרשום שם מאחר או כתובת", - "components.Settings.SonarrModal.validationLanguageProfileRequired": "יש לבחור פרופיל שפה", - "components.Settings.SonarrModal.validationNameRequired": "יש לרשום שם שרת", - "components.Settings.SonarrModal.validationPortRequired": "יש לרשום פורט תקין", - "components.Settings.SonarrModal.validationProfileRequired": "יש לבחור פרופיל איכות", - "components.Settings.SonarrModal.validationRootFolderRequired": "יש לבחור תיקיית מדיה", - "components.Settings.activeProfile": "פרופיל נבחר", - "components.Settings.addradarr": "Radarr הוספת שרת", - "components.Settings.address": "כתובת", - "components.Settings.addsonarr": "Sonarr הוספת שרת", - "components.Settings.advancedTooltip": "הגדרה שגויה של אפשרות זו עלולה לשבש את הפונקציונאליות", - "components.Settings.cancelscan": "ביטול סריקה", - "components.Settings.copied": ".ללוח API key העתקת", - "components.Settings.currentlibrary": "{name} :ספריה נוכחית", - "components.Settings.default": "ברירת מחדל", - "components.Settings.default4k": "4K ברירת מחדל", - "components.Settings.deleteServer": "{serverType} מחיקת שרת", - "components.Settings.deleteserverconfirm": "?האם למחוק את השרת", - "components.Settings.email": "מייל", - "components.Settings.enablessl": "SSL שימוש ב", - "components.Settings.experimentalTooltip": "איפשור ההגדרה הזו עלול לגרום להתנהגות בלתי צפוייה של האפליקציה", - "components.Settings.externalUrl": "כתובת", - "components.Settings.hostname": "IP שם מארח או כתובת", - "components.Settings.invalidurlerror": ".{mediaServerName} אין אפשרות להתחבר לשרת", - "components.Settings.is4k": "4K", - "components.Settings.jellyfinForgotPasswordUrl": "כתובת עבור שכחתי סיסמה", - "components.Settings.jellyfinSettings": "{mediaServerName} הגדרות", - "components.Settings.jellyfinSettingsDescription": ".ניתן גם להגדיר כתובת עבור שכחתי סיסמה בכדי לכוון את המשתמשים לכתובת מותאמת אישית .{mediaServerName} ניתן להגדיר את הכתובות הפומביות של והפנימיות של השרת ברוב המקרים, הכתובת הפומבית שונה מהכתובת המקומית.", - "components.Settings.jellyfinSettingsFailure": ".{mediaServerName} אירעה שגיאה בשמירת הגדרות", - "components.Settings.jellyfinSettingsSuccess": "!נשמרו בהצלחה {mediaServerName} ההגדרות", - "components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "אימות מותאם אישית ביחד עם קיבוץ סיפריות אוטומטי אינו נתמך", - "components.Settings.jellyfinSyncFailedGenericError": "משהו השתבש בסינכרון ספריות", - "components.Settings.jellyfinSyncFailedNoLibrariesFound": "אין ספריות זמינות", - "components.Settings.jellyfinlibraries": "{mediaServerName} ספריות", - "components.Settings.jellyfinlibrariesDescription": "ספריות {mediaServerName} סורקות כותרים. אם אין ספריות שנמצאו, יש ללחוץ על הכפתור מטה.", - "components.Settings.jellyfinsettings": "{mediaServerName} הגדרות", - "components.Settings.jellyfinsettingsDescription": ".בכדי לראות איזה תוכן זמין {mediaServerName} סורק את ספריות {mediaServerName}. {mediaServerName} הגדרת ההגדרות של שרת", - "components.Settings.librariesRemaining": "ספריות שנותרו: {count}", - "components.Settings.manualscan": "סריקת ספריה ידנית", - "components.Settings.manualscanDescription": "בד״כ, הסריקה מבוצעת כל 24 שעות. Jellyseerr יבדוק את התסווספו לאחרונה של שרת Plex. אם זוהי הפעם הראשונה בהגדרת שרת Plex, מומלץ לבצע סריקה ידנית מלאה של כלל הספריות לפחות פעם אחת", - "components.Settings.manualscanDescriptionJellyfin": "{mediaServerName} יבדוק את התווספו לאחרונה של שרת בד״כ, הסריקה תבוצע פעם אחת כל 24 שעות. אם זוהי הפעם הראשונה בהגדרת השרת, מומלץ לבצע סריקה ידנית מלאה לפחות פעם אחת Jellyseerr.", - "components.Settings.manualscanJellyfin": "סריקת ספריה ידנית", - "components.Settings.mediaTypeMovie": "סרט", - "components.Settings.mediaTypeSeries": "סדרה", - "components.Settings.menuAbout": "אודות", - "components.Settings.menuGeneralSettings": "כללי", - "components.Settings.menuJellyfinSettings": "{mediaServerName}", - "components.Settings.menuJobs": "משימות & מטמון", - "components.Settings.menuLogs": "לוגים", - "components.Settings.menuNotifications": "התראות", - "components.Settings.menuPlexSettings": "Plex", - "components.Settings.menuServices": "שרתים", - "components.Settings.menuUsers": "משתמשים", - "components.Settings.noDefault4kServer": "כברירת מחדל 4K {serverType} יש להגדיר 4K {serverType} בכדי לאפשר למשתמשים ליצור בקשות חדשות אל", - "components.Settings.noDefaultNon4kServer": "לייעד אותו עבור איכות גבוהה מאוד אין {serverType}, אחד עבור כל האיכויות או שאם הוא משתמש רק להורדת תוכן באיכות גבוהה מאוד {serverType} אם יש ברשותך רק שרת", - "components.Settings.noDefaultServer": ". אחד בכדי שבקשות {mediaType} יטופלו {serverType} יש לסמן לפחות שרת", - "components.Settings.notificationAgentSettingsDescription": ".איפשור והגדרת של יישומי התראות", - "components.Settings.notifications": "התראות", - "components.Settings.notificationsettings": "הגדרות התראות", - "components.Settings.notrunning": "בהשהייה", - "components.Settings.plex": "Plex", - "components.Settings.plexlibraries": "Plex ספריות", - "components.Settings.plexlibrariesDescription": "ספריות Jellyseerr סורקות כותרים. אם אין ספריות שנמצאו, יש ללחוץ על הכפתור מטה.", - "components.Settings.plexsettings": "Plex הגדרות", - "components.Settings.plexsettingsDescription": ".בכדי לראות איזה תוכן זמין Jellyseerr סורק את ספריות Plex. Plex הגדרת ההגדרות של שרת", - "components.Settings.port": "פורט", - "components.Settings.radarrsettings": "Radarr הגדרות", - "components.Settings.restartrequiredTooltip": "בכדי שההגדרות ייכנסו לתוקף Jellyseerr יש לאתחל את", - "components.Settings.save": "שמירת שינויים", - "components.Settings.saving": "…שמירה", - "components.Settings.scan": "סריקת ספריות", - "components.Settings.scanning": "…סינכרון", - "components.Settings.serverLocal": "מקומי", - "components.Settings.serverRemote": "מרוחק", - "components.Settings.serverSecure": "מאובטח", - "components.Settings.serverpreset": "שרת", - "components.Settings.serverpresetLoad": "יש ללחוץ על הכפתור בכדי לטעון שרתים זמינים", - "components.Settings.serverpresetManualMessage": "הגדרה ידנית", - "components.Settings.serverpresetRefreshing": "…איחזור שרתים", - "components.Settings.serviceSettingsDescription": "רק שניים יוכלו להיות מוגדרים כברירת מחדל (אחד רגיל ואחד איכות גבוהה). מנהלים יוכלו לבחור את השרת שבו תעובד הבקשה לפני שהיא תאושר.{serverType} ניתן להגדיר מספר שרתי", - "components.Settings.services": "שירותים", - "components.Settings.settingUpPlexDescription": ".יש ללחוץ על הכפתור לצד הרשימה כדי לטעון שרתים זמינים plex.tv ניתן לרשום את הפרטים באופן ידני או לבחור שרת מתוך ,Plex בכדי להגדיר את", - "components.Settings.sonarrsettings": "Sonarr הגדרות", - "components.Settings.ssl": "SSL", - "components.Settings.startscan": "התחל סריקה", - "components.Settings.syncJellyfin": "סריקת ספריות", - "components.Settings.syncing": "מסנכרן", - "components.Settings.tautulliApiKey": "API Key", - "components.Settings.tautulliSettings": "Tautulli הגדרות", - "components.Settings.tautulliSettingsDescription": ".Tautulli הגדרת ההגדרות עבור שרת. Jellyseerr מאחזר את היסטוריית הצפייה של Plex מ Tautulli", - "components.Settings.timeout": "שגיאה", - "components.Settings.toastPlexConnecting": "…Plex מנסה להתחבר אל", - "components.Settings.toastPlexConnectingFailure": ".Plex כשל בחיבור אל", - "components.Settings.toastPlexConnectingSuccess": "!בוצע בהצלחה Plex החיבור אל", - "components.Settings.toastPlexRefresh": "…Plex איחזור רשימת שרתי", - "components.Settings.toastPlexRefreshFailure": ".נכשל Plex איחזור שרתי", - "components.Settings.toastPlexRefreshSuccess": "!בוצע בהצלחה Plex איחזור שרתי", - "components.Settings.toastTautulliSettingsFailure": ".Tautulli אירעה שגיאה בעת שמירת הגדרות", - "components.Settings.toastTautulliSettingsSuccess": "!נשמרו בהצלחה Tautulli הגדרות", - "components.Settings.urlBase": "תחילית לכתובת", - "components.Settings.validationApiKey": "API key יש לרשום", - "components.Settings.validationHostnameRequired": "יש לרשום שם מארח או כתובת חוקיים", - "components.Settings.validationPortRequired": "יש לרשום מספר פורט תקין", - "components.Settings.validationUrl": "יש לרשום כתובת", - "components.Settings.validationUrlBaseLeadingSlash": "יש להתחיל עם סלאש בתחילית לכתובת", - "components.Settings.validationUrlBaseTrailingSlash": "אין לסיים את תחילית הכתובת עם סלאש", - "components.Settings.validationUrlTrailingSlash": "אין לסיים את הכתובת עם סלאש", - "components.Settings.webAppUrl": "Web App כתובת", - "components.Settings.webAppUrlTip": "\"hosted\" web app במקום אל web app ניתן להפנות משתמשים אל", - "components.Settings.webhook": "Webhook", - "components.Settings.webpush": "Web Push", - "components.Setup.configuremediaserver": "הגדרות שרת המדיה", - "components.Setup.configureservices": "הגדרת שרתים", - "components.Setup.continue": "הבא", - "components.Setup.finish": "סיום הגדרה ראשונית", - "components.Setup.finishing": "…מסיים הגדרה", - "components.Setup.scanbackground": ".הסריקה תרוץ ברקע בכדי לאפשר את המשך ההגדרה הראשונית", - "components.Setup.setup": "Setup", - "components.Setup.signin": "התחברות", - "components.Setup.signinMessage": "יש לבצע לוגאין בכדי להתחיל", - "components.Setup.signinWithJellyfin": "{mediaServerName} שימוש בחשבון", - "components.Setup.signinWithPlex": "Plex שימוש בחשבון", - "components.Setup.tip": "טיפ", - "components.Setup.welcome": "Jellyseerr ברוך הבא אל", - "components.StatusBadge.managemedia": "ניהול ה{mediaType}", - "components.StatusBadge.openinarr": "{arr} פתיחה ב", - "components.StatusBadge.playonplex": "{mediaServerName} ניגון ב", - "components.StatusBadge.seasonepisodenumber": "{episodeNumber}פ{seasonNumber}ע", - "components.StatusBadge.status": "{status}", - "components.StatusBadge.status4k": "4K {status}", - "components.StatusChecker.appUpdated": "{applicationTitle} עודכן", - "components.StatusChecker.appUpdatedDescription": ".יש ללחוץ על השרת מטה בכדי לטעון מחדש את האפליקציה", - "components.StatusChecker.reloadApp": "{applicationTitle} טעינה חוזרת של", - "components.StatusChecker.restartRequired": "יש לאתחל את השרת", - "components.StatusChecker.restartRequiredDescription": ".יש לאתחל את השרת הכדי להחיל את ההגדרות שעודכנו", - "components.TitleCard.addToWatchList": "הוספה לרשימת צפייה", - "components.TitleCard.cleardata": "ניקוי מידע", - "components.TitleCard.mediaerror": "חסר {mediaType}", - "components.TitleCard.tmdbid": "TMDB ID", - "components.TitleCard.tvdbid": "TheTVDB ID", - "components.TitleCard.watchlistCancel": ".{title} בוטלה רשימת הצפייה עבור", - "components.TitleCard.watchlistDeleted": "!{title} הוסר מרשימת הצפייה בהצלחה", - "components.TitleCard.watchlistError": ".משהו השתבש, יש לנסות שוב", - "components.TitleCard.watchlistSuccess": "!{title} התווסף לרשימת הצפייה בהצלחה", - "components.TvDetails.Season.noepisodes": ".אין רשימת פרקים זמינה", - "components.TvDetails.Season.somethingwentwrong": ".אירעה שגיאה האיחזור נתוני עונה", - "components.TvDetails.TvCast.fullseriescast": "הצגת הליהוק המלא", - "components.TvDetails.TvCrew.fullseriescrew": "הצגת הצוות המלא", - "components.TvDetails.anime": "אנימה", - "components.TvDetails.cast": "ליהוק", - "components.TvDetails.episodeCount": "{episodeCount, plural, one {# פרק} other {פרקים #}, }", - "components.TvDetails.episodeRuntime": "זמן פרק", - "components.TvDetails.episodeRuntimeMinutes": "{runtime} דקות", - "components.TvDetails.firstAirDate": "שודר לראשונה", - "components.TvDetails.manageseries": "ניהול סדרה", - "components.TvDetails.network": "{networkCount, plural, one {רשת} other {רשתות}}", - "components.TvDetails.nextAirDate": "תאריך השידור הבא", - "components.TvDetails.originallanguage": "שפת מקור", - "components.TvDetails.originaltitle": "שם מקורי", - "components.TvDetails.overview": "תקציר", - "components.TvDetails.overviewunavailable": "תקציר אינו זמין", - "components.TvDetails.play": "{mediaServerName} ניגון ב", - "components.TvDetails.play4k": "{mediaServerName} 4K ניגון", - "components.TvDetails.productioncountries": "{countryCount, plural, one {מדינה} other {מדינות}} מפיקה", - "components.TvDetails.recommendations": "דומים לסדרה", - "components.TvDetails.reportissue": "דיווח על תקלה", - "components.TvDetails.rtaudiencescore": "Rotten Tomatoes דירוג קהל", - "components.TvDetails.rtcriticsscore": "Rotten Tomatoes טומטומטר של", - "components.TvDetails.seasonnumber": "עונה {seasonNumber}", - "components.TvDetails.seasons": "{seasonCount, plural, one {# עונה} other {# עונות}}", - "components.TvDetails.seasonstitle": "עונות", - "components.TvDetails.showtype": "סוג סדרה", - "components.TvDetails.similar": "סדרות דומות", - "components.TvDetails.status4k": "4K {status}", - "components.TvDetails.streamingproviders": "משודר ברשת", - "components.TvDetails.tmdbuserscore": "TMDB דירוג משתמשי", - "components.TvDetails.viewfullcrew": "הצגת הצוות המלא", - "components.TvDetails.watchtrailer": "צפייה בטריילר", - "components.UserList.accounttype": "סוג", - "components.UserList.admin": "מנהל", - "components.UserList.autogeneratepassword": "יצירת סיסמה באופן אוטומטי", - "components.UserList.autogeneratepasswordTip": "שליחת הסיסמה שנוצרה במייל למשתמש", - "components.UserList.bulkedit": "עריכת הכל", - "components.UserList.create": "יצירה", - "components.UserList.created": "תאריך הצטרפות", - "components.UserList.createlocaluser": "יצירת משתמש מקומי", - "components.UserList.creating": "…יצירה", - "components.UserList.deleteconfirm": ".למחוק את המשתמש? כל הבקשות והמידע גם יימחק", - "components.UserList.deleteuser": "מחיקת משתמש", - "components.UserList.displayName": "שם תצוגה", - "components.UserList.edituser": "עריכת הרשאות משתמש", - "components.UserList.email": "כתובת מייל", - "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} יובא בהצלחה!", - "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {משתמש} other {משתמשים}} יובא בהצלחה!", - "components.UserList.importfromJellyfin": "{mediaServerName} ייבוא משתמשי", - "components.UserList.importfromJellyfinerror": ".{mediaServerName} משהו השתבש בעת ייבוא משתמשי", - "components.UserList.importfrommediaserver": "{mediaServerName} ייבוא משתמשי", - "components.UserList.importfromplex": "Plex ייבוא משתמשי", - "components.UserList.importfromplexerror": ".Plex משהו השתבש בעת ייבוא משתמשי", - "components.UserList.localLoginDisabled": ".כרגע מבוטלת איפשור לוגאין מקומי הגדרת", - "components.UserList.localuser": "משתמש מקומי", - "components.UserList.mediaServerUser": "{mediaServerName} משתמש", - "components.UserList.newJellyfinsigninenabled": "כרגע מאופשרת חדש {mediaServerName} איפשור לוגאין ההגדרה של. {mediaServerName} אין צורך לייבא משתמשים בעלי גישה לספרייה של", - "components.UserList.newplexsigninenabled": "כרגע מאופשרת חדש {mediaServerName} איפשור לוגאין ההגדרה של. Plex אין צורך לייבא משתמשים בעלי גישה לספרייה של", - "components.UserList.noJellyfinuserstoimport": ".לייבוא {mediaServerName} אין משתמשי", - "components.UserList.nouserstoimport": ".שניתן לייבא Plex אין משתמשי", - "components.UserList.owner": "בעלים", - "components.UserList.password": "סיסמה", - "components.UserList.passwordinfodescription": ".יש להגדיר כתובת יישום ולאפשר הודעות מייל בכדי שיהיה ניתן ליצור סיסמה באופן אוטומטי", - "components.UserList.plexuser": "Plex משתמש", - "components.UserList.role": "תפקיד", - "components.UserList.sortCreated": "תאריך הצטרפות", - "components.UserList.sortDisplayName": "שם תצוגה", - "components.UserList.sortRequests": "מספר בקשות", - "components.UserList.totalrequests": "בקשות", - "components.UserList.user": "משתמש", - "components.UserList.usercreatedfailed": ".אירעה שגיאה ביצירת המשתמש", - "components.UserList.usercreatedfailedexisting": ".כתובת המייל שנרשמה כבר שייכת למשתמש אחר", - "components.UserList.usercreatedsuccess": "!המשתמש נוצר בהצלחה", - "components.UserList.userdeleted": "!המשתמש נמחק בהצלחה", - "components.UserList.userdeleteerror": ".אירעה שגיאה בעת מחיקת המשתמש", - "components.UserList.userfail": ".אירעה שגיאה בעת שמירת הרשאות המשתמש", - "components.UserList.userlist": "רשימת משתמשים", - "components.UserList.users": "משתמשים", - "components.UserList.userssaved": "!הרשאות המשתמש נשמרו בהצלחה", - "components.UserList.validationEmail": "יש לרשום כתובת מייל תקינה", - "components.UserList.validationpasswordminchars": "הסיסמה צריכה להיות באורך 8 תווים", - "components.UserProfile.ProfileHeader.joindate": "חבר מאז {joindate}", - "components.UserProfile.ProfileHeader.profile": "צפייה בפרופיל", - "components.UserProfile.ProfileHeader.settings": "עריכת הגדרות", - "components.UserProfile.ProfileHeader.userid": "{userid} :מזהה משתמש", - "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "סוג החשבון", - "components.UserProfile.UserSettings.UserGeneralSettings.admin": "מנהל", - "components.UserProfile.UserSettings.UserGeneralSettings.applanguage": "שפת התצוגה", - "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "מזהה משתמש מה חדש", - "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "מזהה ספרות הינו מזהה הספרות המשוייך לחשבון Discord", - "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "שם תצוגה", - "components.UserProfile.UserSettings.UserGeneralSettings.email": "מייל", - "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "עקיפת מגבלה", - "components.UserProfile.UserSettings.UserGeneralSettings.general": "כללי", - "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "הגדרות כללות", - "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "({language}) ברירת מחדל", - "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "משתמש מקומי", - "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} משתמש", - "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "הגבלת בקשות סרטים", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Discover שפת", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "סינון תוכן לפי שפת המקור", - "components.UserProfile.UserSettings.UserGeneralSettings.owner": "בעלים", - "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex משתמש", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "בקשת סרטים אוטומטית", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Plex רשימת צפייה של בקשת סרטים אוטומטית עבור", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "בקשת סדרות אוטומטית", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Plex רשימת צפייה של בקשת סדרות אוטומטית עבור", - "components.UserProfile.UserSettings.UserGeneralSettings.region": "איזור מה חדש", - "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "סינון תוכן לפי זמינות איזורית", - "components.UserProfile.UserSettings.UserGeneralSettings.role": "תפקיד", - "components.UserProfile.UserSettings.UserGeneralSettings.save": "שמירת שיניים", - "components.UserProfile.UserSettings.UserGeneralSettings.saving": "…בשמירה", - "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "הגבלת בקשת סדרות", - "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": ".אירעה שגיאה בעת שמירת ההגדרות", - "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "!ההגדרות נשמרו בהצלחה", - "components.UserProfile.UserSettings.UserGeneralSettings.user": "משתמש", - "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "תקין Discord user ID יש לרשום", - "components.UserProfile.UserSettings.UserGeneralSettings.validationemailformat": "יש לרשום מייל תקין", - "components.UserProfile.UserSettings.UserGeneralSettings.validationemailrequired": "יש לרשום מייל", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "ברירת המחדל של המכשיר", - "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID", - "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "שמקושר לחשבון שלך multi-digit ID number ה", - "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": ".Discord אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "!נשמרו בהצלחה Discord הגדרות התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.email": "מייל", - "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": ".אירעה שגיאה בעת שמירת הגדרות התראות למייל", - "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "!הגדרות התראות למייל נשמרו בהצלחה", - "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "הגדרות התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "PGP Public Key", - "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "OpenPGP הצפנת הודעות מייל באמצעות", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessToken": "Access Token", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "בהגדרות חשבון Token ניתן ליצור", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": ".Pushbullet אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "!נשמרו בהצלחה Pushbullet הגדרות התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Application API Token", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "{applicationTitle} לשימוש עם רישום אפליקציה", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "User או Group Key", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "מזהה משתמש או קבוצה יש לרשום מפתח 30 תווים של", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": ".Pushover אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "!נשמרו בהצלחה Pushover הגדרות התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "שליחה שקטה", - "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "שליחת התראות ללא צליל", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "צליל התראה", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat ID", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat, add @get_id_bot, and issue the /my_id command", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": ".אירעה שגיאה בשמירת הגדרות התראות לטלגרם", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "!נשמרו בהצלחה Telegram הגדרות התראות", - "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "תקין user ID יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "תקין PGP public key יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "תקין access token יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "תקין application token יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "תקין group key יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "תקין chat ID יש לרשום", - "components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": ".Web אירעה שגיאה בעת שמירת הגדרות התראות עבור", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "!נשמרו בהצלחה Web הגדרות התראות", - "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "אימות סיסמה", - "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "סיסמה נוכחית", - "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "סיסמה חדשה", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": ".אין סיסמה מוגדרת למשתמש. בכדי שיתאפשר לוגאין עם \"משתמש מקומי\" באמצעות מייל, יש להגדיר סיסמה", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": ".אין סיסמה מוגדרת בחשבון. בכדי שיתאפשר לוגאין עם \"משתמש מקומי\" באמצעות מייל, יש להגדיר סיסמה", - "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": ".אין הרשאות מספקות בכדי לשנות את הסיסמה עבור המשתמש הזה", - "components.UserProfile.UserSettings.UserPasswordChange.password": "סיסמה", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": ".משהו השתבש בשמירת הסיסמה", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "?משהו השתבש בשמירת הסיסמה. האם הסיסמה הנוכחית נכונה", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "!הסיסמה נשמרה בהצלחה", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "יש לרשום סיסמה זהה", - "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "הסיסמאות צריכות להיות זהות", - "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "יש לרשום את הסיסמה הנוכחית", - "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "יש לרשום סיסמה חדשה", - "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "הסיסמה צריכה להיות באורך 8 תווים לפחות", - "components.UserProfile.UserSettings.UserPermissions.permissions": "הרשאות", - "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": ".משהו השתבש בשמירת ההגדרות", - "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "!הרשאות נשמרו בהצלחה", - "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": ".אין אפשרות לשנות את ההגדרות לעצמך", - "components.UserProfile.UserSettings.menuChangePass": "סיסמה", - "components.UserProfile.UserSettings.menuGeneralSettings": "כללי", - "components.UserProfile.UserSettings.menuNotifications": "התראות", - "components.UserProfile.UserSettings.menuPermissions": "הרשאות", - "components.UserProfile.UserSettings.unauthorizedDescription": ".אין הרשאות מספקות הכדי לשנות את הגדרות המשתמש הזה", - "components.UserProfile.emptywatchlist": ".תוצג כאן Plex רשימת צפייה של המדיה שמתווספת אל", - "components.UserProfile.limit": "{limit} מתוך {remaining}", - "components.UserProfile.localWatchlist": "{username} רשימת הצפייה של", - "components.UserProfile.movierequests": "בקשות סרטים", - "components.UserProfile.pastdays": "{type} (ימים האחרונים {days} ב)", - "components.UserProfile.plexwatchlist": "Plex רשימת צפייה של", - "components.UserProfile.recentlywatched": "נצפו לאחרונה", - "components.UserProfile.recentrequests": "התבקשו לאחרונה", - "components.UserProfile.requestsperdays": "{limit} נותרו", - "components.UserProfile.seriesrequest": "בקשת סדרות", - "components.UserProfile.totalrequests": "סה״כ בקשות", - "components.UserProfile.unlimited": "חופשי", - "i18n.advanced": "מתקדם", - "i18n.all": "הכל", - "i18n.approve": "אישור", - "i18n.approved": "אושר", - "i18n.areyousure": "?בטוח", - "i18n.available": "זמינה", - "i18n.back": "חזרה", - "i18n.cancel": "ביטול", - "i18n.canceling": "…מבטל", - "i18n.close": "סגירה", - "i18n.collection": "אוסף", - "i18n.decline": "סרב", - "i18n.declined": "סורב", - "i18n.delete": "מחיקה", - "i18n.deleting": "…מוחק", - "i18n.delimitedlist": "{a}, {b}", - "i18n.edit": "עריכה", - "i18n.experimental": "נסיוני", - "i18n.failed": "נכשל", - "i18n.import": "ייבוא", - "i18n.importing": "…מייבא", - "i18n.loading": "…טוען", - "i18n.movie": "סרט", - "i18n.movies": "סרטים", - "i18n.next": "הבא", - "i18n.noresults": "אין תוצאות", - "i18n.notrequested": "אין בקשה", - "i18n.open": "פתוח", - "i18n.partiallyavailable": "זמין חלקית", - "i18n.pending": "ממתין", - "i18n.previous": "הקודם", - "i18n.processing": "מעבד", - "i18n.request": "בקשה", - "i18n.request4k": "4K בקשה של", - "i18n.requested": "בקשה ממתינה", - "i18n.requesting": "…מבקש", - "i18n.resolved": "נפתר", - "i18n.restartRequired": "נדרש איתחול", - "i18n.resultsperpage": "מציג {pageSize} תוצאות בעמוד", - "i18n.retry": "ניסיון חוזר", - "i18n.retrying": "…ניסיון חוזר", - "i18n.save": "שמירת שינויים", - "i18n.saving": "…שמירה", - "i18n.settings": "הגדרות", - "i18n.showingresults": "תוצאות {total} עד {to} מתוך {from} מציג", - "i18n.status": "סטאטוס", - "i18n.test": "בדיקה", - "i18n.testing": "…בדיקה", - "i18n.tvshow": "סדרה", - "i18n.tvshows": "סדרות", - "i18n.unavailable": "אין זמינות", - "i18n.usersettings": "הגדרות משתמש", - "i18n.view": "צפייה", - "pages.errormessagewithcode": "{statusCode} - {error}", - "pages.internalservererror": "שגיאת שרת", - "pages.oops": "אופס", - "pages.pagenotfound": "הדף חסר", - "pages.returnHome": "חזרה למסך הבית", - "pages.serviceunavailable": "השירות אינו זמין", - "pages.somethingwentwrong": "משהו השתבש" + "components.PermissionEdit.advancedrequestDescription": "הרשאות לשינוי אפשרויות בקשות מדיה.", + "components.PermissionEdit.autoapprove": "אישור אוטומטי", + "components.PermissionEdit.autoapprove4k": "אישור אוטומטי של 4K", + "components.PermissionEdit.autoapprove4kMovies": "אישור אוטומטי סרטי 4K", + "components.PermissionEdit.autoapprove4kMoviesDescription": "אישור אוטומטי של בקשות לסרטים 4K", + "components.PermissionEdit.autoapprove4kSeries": "אישור אוטומטי של סדרות 4K", + "components.PermissionEdit.autoapproveDescription": "אישור אוטומטי של בקשות לסדרות ברזולוציית נמוכה מ-4K", + "components.PermissionEdit.autoapproveMovies": "אישור סרטים אוטומטי", + "components.PermissionEdit.autoapproveMoviesDescription": "אישור בקשות סרטים אוטומטי (ללא 4K).", + "components.PermissionEdit.autoapproveSeries": "אישור סדרות אוטומטי", + "components.PermissionEdit.autorequest": "בקשה אוטומטית", + "components.NotificationTypeSelector.mediafailedDescription": "קבלת התראות כאשר יש כשל בהוספת בקשות מדיה אל Radarr או Sonarr", + "components.NotificationTypeSelector.notificationTypes": "סוגי התראות", + "components.NotificationTypeSelector.userissuecommentDescription": "קבלת התראות כאשר תקלות שדיווחת מקבלות תגובות חדשות.", + "components.PermissionEdit.autoapprove4kDescription": "אישור אוטומטי של בקשות מדיה 4K", + "components.PermissionEdit.autoapprove4kSeriesDescription": "אישור אוטומטי של בקשות לסדרות ברזולוציית 4K", + "components.PermissionEdit.autoapproveSeriesDescription": "אישור בקשות סדרות אוטומטי (ללא 4K)." } diff --git a/src/i18n/locale/nl.json b/src/i18n/locale/nl.json index 9592c3a9..39a62cc2 100644 --- a/src/i18n/locale/nl.json +++ b/src/i18n/locale/nl.json @@ -3,12 +3,12 @@ "components.Discover.discovertv": "Populaire series", "components.Discover.popularmovies": "Populaire films", "components.Discover.populartv": "Populaire series", - "components.Discover.recentlyAdded": "Recent toegevoegd", + "components.Discover.recentlyAdded": "Onlangs toegevoegd", "components.Discover.recentrequests": "Recente verzoeken", "components.Discover.trending": "Trending", "components.Discover.upcoming": "Verwachte films", "components.Discover.upcomingmovies": "Verwachte films", - "components.Layout.SearchInput.searchPlaceholder": "Zoek films en series", + "components.Layout.SearchInput.searchPlaceholder": "Films en series zoeken", "components.Layout.Sidebar.dashboard": "Ontdekken", "components.Layout.Sidebar.requests": "Verzoeken", "components.Layout.Sidebar.settings": "Instellingen", @@ -16,7 +16,7 @@ "components.Layout.UserDropdown.signout": "Uitloggen", "components.MovieDetails.budget": "Budget", "components.MovieDetails.cast": "Cast", - "components.MovieDetails.originallanguage": "Originele taal", + "components.MovieDetails.originallanguage": "Oorspronkelijke taal", "components.MovieDetails.overview": "Overzicht", "components.MovieDetails.overviewunavailable": "Overzicht niet beschikbaar.", "components.MovieDetails.recommendations": "Aanbevelingen", @@ -114,7 +114,7 @@ "components.Settings.hostname": "Hostnaam of IP-adres", "components.Settings.librariesRemaining": "Resterende bibliotheken: {count}", "components.Settings.manualscan": "Handmatige bibliotheekscan", - "components.Settings.manualscanDescription": "Normaal wordt dit eens elke 24 uur uitgevoerd. Jellyseerr controleert de recent toegevoegde items van je Plex-server agressiever. Als je Plex voor de eerste keer configureert, is een eenmalige handmatige volledige bibliotheekscan aanbevolen!", + "components.Settings.manualscanDescription": "Normaliter wordt dit eenmaal per 24 uur uitgevoerd. Jellyseerr zal de lijst met onlangs toegevoegde media op je Plex-server vaker controleren. Als dit de eerste keer is dat je Jellyseerr instelt, wordt aanbevolen eenmalig een handmatige, volledige bibliotheekscan uit te voeren!", "components.Settings.menuAbout": "Over", "components.Settings.menuGeneralSettings": "Algemeen", "components.Settings.menuJobs": "Taken en cache", @@ -127,7 +127,7 @@ "components.Settings.plexlibraries": "Plex-bibliotheken", "components.Settings.plexlibrariesDescription": "De bibliotheken die Jellyseerr scant voor titels. Stel je Plex-verbinding in en sla ze op. Klik daarna op de onderstaande knop als er geen bibliotheken staan.", "components.Settings.plexsettings": "Plex-instellingen", - "components.Settings.plexsettingsDescription": "Configureer de instellingen voor je Plex-server. Jellyseerr scant je Plex-bibliotheken om te zien welke content beschikbaar is.", + "components.Settings.plexsettingsDescription": "Configureer de instellingen voor je Plex-server. Jellyseerr scant je Plex-bibliotheken om te zien welke inhoud beschikbaar is.", "components.Settings.port": "Poort", "components.Settings.radarrsettings": "Radarr-instellingen", "components.Settings.sonarrsettings": "Sonarr-instellingen", @@ -137,12 +137,12 @@ "components.Setup.configureservices": "Diensten configureren", "components.Setup.continue": "Doorgaan", "components.Setup.finish": "Installatie voltooien", - "components.Setup.finishing": "Bezig met voltooien…", + "components.Setup.finishing": "Voltooien…", "components.Setup.loginwithplex": "Inloggen met Plex", - "components.Setup.signinMessage": "Ga aan de slag door in te loggen met je Plex-account", + "components.Setup.signinMessage": "Ga aan de slag door je aan te melden", "components.Setup.welcome": "Welkom bij Jellyseerr", "components.TvDetails.cast": "Cast", - "components.TvDetails.originallanguage": "Originele taal", + "components.TvDetails.originallanguage": "Oorspronkelijke taal", "components.TvDetails.overview": "Overzicht", "components.TvDetails.overviewunavailable": "Overzicht niet beschikbaar.", "components.TvDetails.recommendations": "Aanbevelingen", @@ -164,7 +164,7 @@ "i18n.movies": "Films", "i18n.partiallyavailable": "Deels beschikbaar", "i18n.pending": "In behandeling", - "i18n.processing": "Bezig met verwerken", + "i18n.processing": "Verwerken", "i18n.tvshows": "Series", "i18n.unavailable": "Niet beschikbaar", "pages.oops": "Oeps", @@ -187,14 +187,14 @@ "components.Setup.tip": "Tip", "components.Settings.SonarrModal.testFirstRootFolders": "Test verbinding om hoofdmappen te laden", "components.Settings.SonarrModal.testFirstQualityProfiles": "Test verbinding om kwaliteitsprofielen te laden", - "components.Settings.SonarrModal.loadingrootfolders": "Bezig met laden van hoofdmappen…", - "components.Settings.SonarrModal.loadingprofiles": "Bezig met laden van kwaliteitsprofielen…", + "components.Settings.SonarrModal.loadingrootfolders": "Hoofdmappen laden…", + "components.Settings.SonarrModal.loadingprofiles": "Kwaliteitsprofielen laden…", "components.Settings.SettingsAbout.gettingsupport": "Ondersteuning krijgen", "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Je moet een minimale beschikbaarheid selecteren", "components.Settings.RadarrModal.testFirstRootFolders": "Test verbinding om hoofdmappen te laden", "components.Settings.RadarrModal.testFirstQualityProfiles": "Test verbinding om kwaliteitsprofielen te laden", - "components.Settings.RadarrModal.loadingrootfolders": "Bezig met laden van hoofdmappen…", - "components.Settings.RadarrModal.loadingprofiles": "Bezig met laden van kwaliteitsprofielen…", + "components.Settings.RadarrModal.loadingrootfolders": "Hoofdmappen laden…", + "components.Settings.RadarrModal.loadingprofiles": "Kwaliteitsprofielen laden…", "components.Settings.SettingsAbout.Releases.releasedataMissing": "Versiegegevens zijn momenteel niet beschikbaar.", "components.Settings.SettingsAbout.Releases.latestversion": "Nieuwste", "components.Settings.SettingsAbout.Releases.currentversion": "Huidig", @@ -208,7 +208,7 @@ "i18n.retry": "Opnieuw proberen", "i18n.requested": "Aangevraagd", "i18n.failed": "Mislukt", - "i18n.deleting": "Bezig met verwijderen…", + "i18n.deleting": "Verwijderen…", "i18n.close": "Sluiten", "components.UserList.userdeleteerror": "Er ging iets mis bij het verwijderen van de gebruiker.", "components.UserList.userdeleted": "Gebruiker succesvol verwijderd!", @@ -223,7 +223,7 @@ "components.TvDetails.network": "{networkCount, plural, one {Netwerk} other {Netwerken}}", "components.TvDetails.firstAirDate": "Datum eerste uitzending", "components.TvDetails.anime": "Anime", - "components.StatusChacker.reloadOverseerr": "Herladen", + "components.StatusChacker.reloadJellyseerr": "Herladen", "components.StatusChacker.newversionavailable": "Toepassingsupdate", "components.StatusChacker.newversionDescription": "Jellyseerr is geüpdatet! Klik op de onderstaande knop om de pagina opnieuw te laden.", "components.Settings.toastSettingsSuccess": "Instellingen succesvol opgeslagen!", @@ -303,10 +303,10 @@ "components.UserList.create": "Aanmaken", "components.UserList.createlocaluser": "Lokale gebruiker aanmaken", "components.UserList.usercreatedfailed": "Er ging iets mis bij het aanmaken van de gebruiker.", - "components.UserList.creating": "Bezig met aanmaken…", + "components.UserList.creating": "Aanmaken…", "components.UserList.validationpasswordminchars": "Wachtwoord is te kort; moet minimaal 8 tekens bevatten", "components.UserList.usercreatedsuccess": "Gebruiker succesvol aangemaakt!", - "components.UserList.passwordinfodescription": "Configureer een applicatie-URL en schakel e-mailmeldingen in om automatische wachtwoordgeneratie mogelijk te maken.", + "components.UserList.passwordinfodescription": "Stel een applicatie-URL in en schakel e-mailmeldingen in om automatische wachtwoordgeneratie mogelijk te maken.", "components.UserList.password": "Wachtwoord", "components.UserList.localuser": "Lokale gebruiker", "components.UserList.email": "E-mailadres", @@ -340,23 +340,23 @@ "components.RequestModal.SearchByNameModal.notvdbiddescription": "We kunnen deze serie niet automatisch matchen. Selecteer hieronder de juiste match.", "components.Login.signinwithplex": "Plex-account gebruiken", "components.Login.signinheader": "Log in om verder te gaan", - "components.Login.signingin": "Bezig met inloggen…", + "components.Login.signingin": "Aanmelden…", "components.Login.signin": "Inloggen", "components.Settings.notificationAgentSettingsDescription": "Meldingsagenten configureren en inschakelen.", "components.PlexLoginButton.signinwithplex": "Inloggen", - "components.PlexLoginButton.signingin": "Bezig met inloggen…", + "components.PlexLoginButton.signingin": "Aanmelden…", "components.PermissionEdit.advancedrequest": "Geavanceerde aanvragen", "components.PermissionEdit.admin": "Beheerder", "components.UserList.userssaved": "Gebruikersrechten succesvol opgeslagen!", "components.Settings.toastPlexRefreshSuccess": "Serverlijst van Plex succesvol opgehaald!", "components.Settings.toastPlexRefresh": "Bezig met serverlijst ophalen van Plex…", - "components.Settings.toastPlexConnecting": "Bezig met verbinden met Plex-server…", + "components.Settings.toastPlexConnecting": "Verbinden met Plex…", "components.UserList.bulkedit": "Meerdere bewerken", "components.Settings.toastPlexRefreshFailure": "Kan serverlijst van Plex niet ophalen.", "components.Settings.toastPlexConnectingSuccess": "Succesvol verbonden met Plex-server!", "components.Settings.toastPlexConnectingFailure": "Kan geen verbinding maken met Plex.", "components.Settings.settingUpPlexDescription": "Om Plex in te stellen, kan je de gegevens handmatig invoeren of een server selecteren die is opgehaald van plex.tv. Druk op de knop rechts van de vervolgkeuzelijst om de lijst van beschikbare servers op te halen.", - "components.Settings.serverpresetRefreshing": "Bezig met servers ophalen…", + "components.Settings.serverpresetRefreshing": "Servers ophalen…", "components.Settings.serverpresetManualMessage": "Handmatige configuratie", "components.Settings.serverpresetLoad": "Klik op de knop om de beschikbare servers te laden", "components.Settings.serverpreset": "Server", @@ -431,7 +431,7 @@ "components.RequestModal.AdvancedRequester.requestas": "Aanvragen als", "components.Discover.discover": "Ontdekken", "components.Settings.validationApplicationTitle": "Je moet een toepassingstitel opgeven", - "components.AppDataWarning.dockerVolumeMissingDescription": "De volumekoppeling {appDataPath} was niet correct geconfigureerd. Alle gegevens zullen worden gewist wanneer de container wordt gestopt of opnieuw wordt gestart.", + "components.AppDataWarning.dockerVolumeMissingDescription": "De volumekoppeling {appDataPath} is niet correct geconfigureerd. Alle gegevens zullen worden gewist wanneer de container wordt gestopt of opnieuw wordt gestart.", "components.Settings.validationApplicationUrlTrailingSlash": "URL mag niet eindigen op een schuine streep", "components.Settings.validationApplicationUrl": "Je moet een geldige URL opgeven", "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "URL mag niet eindigen op een schuine streep", @@ -520,7 +520,7 @@ "components.Layout.UserDropdown.myprofile": "Profiel", "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Je moet een geldige gebruikers-ID opgeven", "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Het meercijferige ID-nummer van je gebruikersaccount", - "components.CollectionDetails.requestcollection4k": "Collectie in 4K aanvragen", + "components.CollectionDetails.requestcollection4k": "Collectie aanvragen in 4K", "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Inhoud filteren op regionale beschikbaarheid", "components.UserProfile.UserSettings.UserGeneralSettings.region": "Regio van Ontdekken", "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Inhoud filteren op oorspronkelijke taal", @@ -546,7 +546,7 @@ "components.Settings.SettingsJobsCache.download-sync-reset": "Reset download sync", "components.Settings.SettingsJobsCache.download-sync": "Synchronisatie downloads", "components.TvDetails.seasons": "{seasonCount, plural, one {# seizoen} other {# seizoenen}}", - "i18n.loading": "Bezig met laden…", + "i18n.loading": "Laden…", "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "Je moet een geldige chat-ID opgeven", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Een chat starten, @get_id_bot toevoegen en de opdracht /my_id geven", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chat-ID", @@ -558,14 +558,14 @@ "components.Discover.DiscoverNetwork.networkSeries": "Series van {network}", "components.Discover.DiscoverMovieGenre.genreMovies": "{genre} films", "components.Setup.scanbackground": "Het scannen wordt op de achtergrond uitgevoerd. Je kunt in de tussentijd doorgaan met het installatieproces.", - "components.Settings.scanning": "Bezig met synchroniseren…", + "components.Settings.scanning": "Synchroniseren…", "components.Settings.scan": "Bibliotheken synchroniseren", "components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr-scan", "components.Settings.SettingsJobsCache.radarr-scan": "Radarr-scan", "components.Settings.SettingsJobsCache.plex-recently-added-scan": "Plex recent toegevoegde scan", "components.Settings.SettingsJobsCache.plex-full-scan": "Plex volledige bibliotheekscan", "components.Settings.SettingsJobsCache.jellyfin-full-scan": "volledige bibliotheekscan Jellyfin", - "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Jellyfin recent toegevoegde scan", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Scan van 'onlangs toegevoegd' in Jellyfin", "components.Settings.Notifications.validationUrl": "Je moet een geldige URL opgeven", "components.Settings.Notifications.botAvatarUrl": "URL bot-avatar", "components.RequestList.RequestItem.requested": "Aangevraagd", @@ -670,27 +670,27 @@ "components.QuotaSelector.unlimited": "Onbeperkt", "i18n.view": "Bekijken", "i18n.tvshow": "Serie", - "i18n.testing": "Bezig met testen…", + "i18n.testing": "Testen…", "i18n.test": "Test", "i18n.status": "Status", "i18n.showingresults": "{from} tot {to} van de {total} resultaten worden weergegeven", - "i18n.saving": "Bezig met opslaan…", + "i18n.saving": "Opslaan…", "i18n.save": "Wijzigingen opslaan", "i18n.resultsperpage": "{pageSize} resultaten per pagina weergeven", - "i18n.requesting": "Bezig met aanvragen…", + "i18n.requesting": "Aanvragen…", "i18n.request4k": "Aanvragen in 4K", "i18n.previous": "Vorige", "i18n.notrequested": "Niet aangevraagd", "i18n.noresults": "Geen resultaten.", "i18n.next": "Volgende", "i18n.movie": "Film", - "i18n.canceling": "Bezig met annuleren…", + "i18n.canceling": "Annuleren…", "i18n.back": "Terug", "i18n.areyousure": "Weet je het zeker?", "i18n.all": "Alle", "components.RequestModal.QuotaDisplay.requiredquotaUser": "Deze gebruiker heeft nog minstens {seasons} {seasons, plural, one {seizoensverzoek} other {seizoensverzoeken}} nodig om deze serie aan te vragen.", - "components.TvDetails.originaltitle": "Originele titel", - "components.MovieDetails.originaltitle": "Originele titel", + "components.TvDetails.originaltitle": "Oorspronkelijke titel", + "components.MovieDetails.originaltitle": "Oorspronkelijke titel", "components.LanguageSelector.originalLanguageDefault": "Alle talen", "components.LanguageSelector.languageServerDefault": "Standaard ({language})", "components.Settings.SonarrModal.testFirstTags": "Test de verbinding om labels te laden", @@ -733,9 +733,9 @@ "components.RequestModal.pendingapproval": "Je verzoek is in afwachting van goedkeuring.", "components.RequestList.RequestItem.cancelRequest": "Verzoek annuleren", "components.NotificationTypeSelector.notificationTypes": "Meldingtypes", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Er is voor jouw account momenteel geen wachtwoord ingesteld. Configureer hieronder een wachtwoord om in te kunnen loggen als een \"lokale gebruiker\" met uw e-mailadres.", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Er is voor jouw account momenteel geen wachtwoord ingesteld. Configureer hieronder een wachtwoord om in te kunnen loggen als een \"lokale gebruiker\" met je e-mailadres.", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Deze gebruikersaccount heeft momenteel geen wachtwoord ingesteld. Configureer hieronder een wachtwoord zodat deze account in staat is om zich aan te melden als een \"lokale gebruiker\".", - "components.Settings.serviceSettingsDescription": "Configureer je {serverType} server(s) hieronder. Je kunt meerdere {serverType} servers verbinden, maar slechts twee ervan kunnen als standaard worden gemarkeerd (één niet-4K en één 4K). Beheerders kunnen vóór de goedkeuring de server die gebruikt wordt om nieuwe aanvragen te verwerken aanpassen.", + "components.Settings.serviceSettingsDescription": "Stel je {serverType}-server(s) hieronder in. Je kunt meerdere {serverType}-servers verbinden, maar slechts twee ervan kunnen als standaard worden gemarkeerd (één niet-4K en één 4K). Beheerders kunnen vóór goedkeuring de server aanpassen die voor nieuwe aanvragen gebruikt wordt.", "components.Settings.noDefaultServer": "Ten minste één {serverType} server moet als standaard worden gemarkeerd om {mediaType}verzoeken te kunnen verwerken.", "components.Settings.noDefaultNon4kServer": "Als je slechts één enkele {serverType} server hebt voor zowel niet-4K als 4K-inhoud (of als je alleen 4K-inhoud downloadt), dan moet je {serverType} server NIET aangeduid worden als een 4K-server.", "components.Settings.mediaTypeSeries": "serie", @@ -747,7 +747,7 @@ "components.Layout.VersionStatus.outofdate": "Verouderd", "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} achter", "components.UserList.autogeneratepasswordTip": "Een door de server gegenereerd wachtwoord naar de gebruiker e-mailen", - "i18n.retrying": "Bezig met opnieuw proberen…", + "i18n.retrying": "Opnieuw proberen…", "components.Settings.serverSecure": "veilig", "components.UserList.usercreatedfailedexisting": "Het opgegeven e-mailadres wordt al gebruikt door een andere gebruiker.", "components.RequestModal.edit": "Verzoek bewerken", @@ -847,7 +847,7 @@ "components.NotificationTypeSelector.usermediadeclinedDescription": "Een melding ontvangen wanneer je mediaverzoeken worden geweigerd.", "components.NotificationTypeSelector.usermediaavailableDescription": "Een melding ontvangen wanneer je mediaverzoeken beschikbaar zijn.", "components.NotificationTypeSelector.usermediaAutoApprovedDescription": "Een melding ontvangen wanneer andere gebruikers nieuwe mediaverzoeken indienen die automatisch worden goedgekeurd.", - "components.Settings.SettingsAbout.betawarning": "Dit is BETA software. Functies kunnen kapot en/of instabiel zijn. Meld eventuele problemen op GitHub!", + "components.Settings.SettingsAbout.betawarning": "Dit is BETA-software. Functies kunnen kapot en/of instabiel zijn. Meld eventuele problemen op GitHub!", "components.Layout.LanguagePicker.displaylanguage": "Weergavetaal", "components.MovieDetails.showmore": "Meer tonen", "components.MovieDetails.showless": "Minder tonen", @@ -971,7 +971,7 @@ "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Je moet een geldige gebruikers- of groepssleutel opgeven", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Instellingen voor Pushbullet-meldingen succesvol opgeslagen!", "components.IssueDetails.playonplex": "Afspelen op {mediaServerName}", - "components.IssueDetails.play4konplex": "Afspelen in 4K op {mediaServerName}", + "components.IssueDetails.play4konplex": "Afspelen op {mediaServerName} in 4K", "components.IssueDetails.openin4karr": "Openen in 4K {arr}", "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {aflevering} other {afleveringen}}", "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {seizoen} other {seizoenen}}", @@ -982,7 +982,7 @@ "components.NotificationTypeSelector.adminissuereopenedDescription": "Ontvang een melding wanneer problemen door andere gebruikers opnieuw worden ingediend.", "components.NotificationTypeSelector.issuereopenedDescription": "Stuur meldingen wanneer problemen opnieuw worden ingediend.", "components.NotificationTypeSelector.userissuereopenedDescription": "Ontvang een bericht wanneer problemen die jij hebt gemeld, opnieuw worden ingediend.", - "components.RequestModal.requestseasons4k": "{seasonCount} {seasonCount, plural, one {seizoen} other {seizoenen}} in 4K aanvragen", + "components.RequestModal.requestseasons4k": "{seasonCount} {seasonCount, plural, one {seizoen} other {seizoenen}} aanvragen in 4K", "components.RequestModal.requestmovies": "{count} {count, plural, one {film} other {films}} aanvragen", "components.RequestModal.selectmovies": "Film(s) selecteren", "components.MovieDetails.productioncountries": "Productie{countryCount, plural, one {land} other {landen}}", @@ -998,7 +998,7 @@ "components.Settings.Notifications.NotificationsGotify.agentenabled": "Agent inschakelen", "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "Instellingen voor meldingen Gotify succesvol opgeslagen!", "components.Settings.Notifications.NotificationsGotify.token": "Toepassingstoken", - "i18n.importing": "Bezig met importeren…", + "i18n.importing": "Importeren…", "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": "Instellingen voor meldingen Gotify niet opgeslagen.", "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": "Testmelding Gotify niet verzonden.", "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "Testmelding Gotify verzonden!", @@ -1012,7 +1012,7 @@ "components.UserList.newplexsigninenabled": "De instelling Nieuwe Plex-aanmelding inschakelen is momenteel ingeschakeld. Plex-gebruikers met bibliotheektoegang hoeven niet te worden geïmporteerd om in te loggen.", "components.ManageSlideOver.manageModalAdvanced": "Geavanceerd", "components.ManageSlideOver.alltime": "Altijd", - "components.ManageSlideOver.markallseasons4kavailable": "Alle seizoenen als beschikbaar in 4K markeren", + "components.ManageSlideOver.markallseasons4kavailable": "Alle seizoenen markeren als beschikbaar in 4K", "components.ManageSlideOver.opentautulli": "In Tautulli openen", "components.ManageSlideOver.pastdays": "Afgelopen {days, number} dagen", "components.ManageSlideOver.playedby": "Afgespeeld door", @@ -1046,8 +1046,8 @@ "components.UserProfile.emptywatchlist": "Media die zijn toegevoegd aan je Plex Kijklijst verschijnen hier.", "components.MovieDetails.digitalrelease": "Digitale release", "i18n.restartRequired": "Opnieuw opstarten vereist", - "components.PermissionEdit.viewrecentDescription": "Toestemming geven om de lijst met recent toegevoegde media te bekijken.", - "components.PermissionEdit.viewrecent": "Recent toegevoegd bekijken", + "components.PermissionEdit.viewrecentDescription": "Toestemming geven om de lijst met onlangs toegevoegde media weer te geven.", + "components.PermissionEdit.viewrecent": "Onlangs toegevoegd weergeven", "components.Settings.deleteServer": "{serverType}-server verwijderen", "components.StatusChecker.appUpdated": "{applicationTitle} bijgewerkt", "components.RequestList.RequestItem.tmdbid": "TMDB ID", @@ -1072,8 +1072,8 @@ "components.TvDetails.seasonnumber": "Seizoen {seasonNumber}", "components.TvDetails.Season.somethingwentwrong": "Er ging iets mis bij het ophalen van de seizoensgegevens.", "components.TvDetails.seasonstitle": "Seizoenen", - "components.Discover.DiscoverWatchlist.discoverwatchlist": "Je Plex-kijklijst", - "components.Discover.plexwatchlist": "Je Plex Kijklijst", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "Jouw kijklijst", + "components.Discover.plexwatchlist": "Jouw kijklijst", "components.MovieDetails.physicalrelease": "Fysieke release", "components.PermissionEdit.autorequest": "Automatisch aanvragen", "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex Kijklijst synchroniseren", @@ -1099,7 +1099,7 @@ "components.TvDetails.manageseries": "Serie beheren", "components.MovieDetails.managemovie": "Film beheren", "components.MovieDetails.reportissue": "Probleem melden", - "components.PermissionEdit.autorequestMoviesDescription": "Toestemming geven om niet-4K films in je Plex Kijklijst automatisch aan te vragen.", + "components.PermissionEdit.autorequestMoviesDescription": "Toestemming geven om niet-4K films in je Plex-kijklijst automatisch aan te vragen.", "components.PermissionEdit.autorequestSeries": "Series automatisch aanvragen", "components.PermissionEdit.autorequestMovies": "Films automatisch aanvragen", "components.Settings.experimentalTooltip": "Deze instelling inschakelen, kan leiden tot onverwacht gedrag van de toepassing", @@ -1152,8 +1152,8 @@ "components.Discover.DiscoverSliderEdit.remove": "Verwijderen", "components.Discover.resetfailed": "Er is iets fout gegaan bij het resetten van de instellingen van Ontdekken.", "components.Discover.PlexWatchlistSlider.emptywatchlist": "Media die zijn toegevoegd aan je Plex Kijklijst verschijnen hier.", - "components.Discover.PlexWatchlistSlider.plexwatchlist": "Je Plex Kijklijst", - "components.Discover.RecentlyAddedSlider.recentlyAdded": "Recent toegevoegd", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "Jouw kijklijst", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "Onlangs toegevoegd", "components.Discover.networks": "Netwerken", "components.Discover.CreateSlider.searchStudios": "Studio's zoeken…", "components.Discover.CreateSlider.starttyping": "Begin met typen om te zoeken.", @@ -1184,8 +1184,8 @@ "components.Discover.DiscoverMovies.sortPopularityAsc": "Populariteit oplopend", "components.Discover.DiscoverMovies.sortPopularityDesc": "Populariteit aflopend", "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Releasedatum oplopend", - "components.Discover.DiscoverMovies.sortTitleAsc": "Titel (A-Z) oplopend", - "components.Discover.DiscoverMovies.sortTitleDesc": "Titel (Z-A) aflopend", + "components.Discover.DiscoverMovies.sortTitleAsc": "Titel oplopend (A-Z)", + "components.Discover.DiscoverMovies.sortTitleDesc": "Titel aflopend (Z-A)", "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMDB-beoordeling oplopend", "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMDB-beoordeling aflopend", "components.Discover.DiscoverSliderEdit.deletefail": "Slider verwijderen mislukt.", @@ -1202,7 +1202,7 @@ "components.Discover.FilterSlideover.from": "Van", "components.Discover.FilterSlideover.genres": "Genres", "components.Discover.FilterSlideover.keywords": "Trefwoorden", - "components.Discover.FilterSlideover.originalLanguage": "Originele taal", + "components.Discover.FilterSlideover.originalLanguage": "Oorspronkelijke taal", "components.Discover.FilterSlideover.ratingText": "Beoordelingen tussen {minValue} en {maxValue}", "components.Discover.FilterSlideover.releaseDate": "Releasedatum", "components.Discover.FilterSlideover.runtime": "Duur", @@ -1262,18 +1262,87 @@ "components.Settings.SettingsJobsCache.availability-sync": "Synchronisatie van mediabeschikbaarheid", "components.Discover.tmdbmoviestreamingservices": "Streamingdiensten voor films TMDB", "components.Discover.tmdbtvstreamingservices": "Streamingdiensten voor series TMDB", - "components.Discover.FilterSlideover.tmdbuservotecount": "Aantal stemmen TMDB-gebruikers", - "components.Discover.FilterSlideover.voteCount": "Aantal stemmen tussen {minValue} en {maxValue}", - "components.Settings.RadarrModal.tagRequests": "Tagverzoeken", - "components.Settings.RadarrModal.tagRequestsInfo": "Voeg automatisch een extra tag toe met de gebruikers-ID en weergavenaam van de aanvrager", - "components.MovieDetails.imdbuserscore": "Gebruikersscore IMDB", - "components.Settings.SonarrModal.tagRequests": "Tagverzoeken", - "components.Settings.SonarrModal.tagRequestsInfo": "Voeg automatisch een extra tag toe met de gebruikers-ID en weergavenaam van de aanvrager", - "i18n.collection": "Collectie", - "components.Settings.Notifications.NotificationsPushover.sound": "Meldingsgeluid", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Apparaatstandaard", + "components.Login.validationhostrequired": "{mediaServerName}-URL vereist", + "components.Layout.UserWarnings.emailInvalid": "E-mailadres is ongeldig.", + "components.Login.description": "Aangezien dit de eerste keer is dat je je aanmeldt bij {applicationName}, dien je een geldig e-mailadres op te geven.", + "components.Login.saving": "Toevoegen…", + "components.ManageSlideOver.removearr": "Verwijderen van {arr}", + "components.Settings.RadarrModal.tagRequests": "Aanvragen taggen", + "components.MovieDetails.openradarr4k": "Film openen in 4K-Radarr", + "components.Settings.RadarrModal.tagRequestsInfo": "Automatisch een extra label toevoegen met de gebruikers-id en weergavenaam van de aanvrager", "components.Settings.SonarrModal.animeSeriesType": "Serietype anime", + "components.Settings.SonarrModal.tagRequestsInfo": "Automatisch een extra label toevoegen met de gebruikers-id en weergavenaam van de aanvrager", + "components.Settings.internalUrl": "Interne URL", + "components.Settings.jellyfinsettings": "{mediaServerName}-instellingen", + "components.Settings.jellyfinlibrariesDescription": "De {mediaServerName}-bibliotheken die op titels worden gescand. Klik op onderstaande knop als er geen bibliotheken in de lijst staan.", + "components.Settings.manualscanDescriptionJellyfin": "Normaliter wordt dit eenmaal per 24 uur uitgevoerd. Jellyseerr zal de lijst met onlangs toegevoegde media op je {mediaServerName}-server vaker controleren. Als dit de eerste keer is dat je Jellyseerr instelt, wordt aanbevolen eenmalig een handmatige, volledige bibliotheekscan uit te voeren!", + "components.Settings.save": "Wijzigingen opslaan", + "components.Settings.syncJellyfin": "Bibliotheken synchoniseren", + "components.TvDetails.play": "Afspelen op {mediaServerName}", + "components.Discover.FilterSlideover.tmdbuservotecount": "Aantal gebruikersstemmen TMDB", + "components.Login.save": "Toevoegen", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Hiermee wordt deze {mediaType} onomkeerbaar verwijderd van {arr}, inclusief alle bestanden.", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Apparaatstandaard", + "components.Settings.Notifications.userEmailRequired": "Gebruikerse-mail vereisen", + "components.Settings.SettingsAbout.supportjellyseerr": "Jellyseerr ondersteunen", "components.Settings.SonarrModal.seriesType": "Serietype", + "components.Settings.jellyfinSettings": "{mediaServerName}-instellingen", + "components.Setup.configuremediaserver": "Mediaserver instellen", + "components.TvDetails.play4k": "Afspelen op {mediaServerName} in 4K", + "components.UserList.mediaServerUser": "{mediaServerName}-gebruiker", + "components.UserList.noJellyfinuserstoimport": "Er zijn geen {mediaServerName}-gebruikers om te importeren.", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-mail", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Apparaatstandaard", "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Meldingsgeluid", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Apparaatstandaard" + "components.Login.signinwithjellyfin": "{mediaServerName}-account gebruiken", + "components.Discover.FilterSlideover.voteCount": "Aantal stemmen tussen {minValue} en {maxValue}", + "components.Layout.UserWarnings.emailRequired": "Een e-mailadres is vereist.", + "components.Layout.UserWarnings.passwordRequired": "Een wachtwoord is vereist.", + "components.Login.credentialerror": "Gebruikersnaam of wachtwoord is onjuist.", + "components.Login.emailtooltip": "Het adres hoeft niet gelieerd te zijn aan je {mediaServerName}-instantie.", + "components.Login.host": "{mediaServerName}-URL", + "components.Login.initialsignin": "Verbinden", + "components.Login.initialsigningin": "Verbinden…", + "components.Login.title": "E-mail toevoegen", + "components.Login.username": "Gebruikersnaam", + "components.Login.validationEmailRequired": "Je moet een e-mailadres opgeven", + "components.Login.validationEmailFormat": "Ongeldig e-mailadres", + "components.Login.validationemailformat": "Geldig e-mailadres vereist", + "components.Login.validationhostformat": "Geldige URL vereist", + "components.Login.validationusernamerequired": "Gebruikersnaam vereist", + "components.ManageSlideOver.removearr4k": "Verwijderen van 4K-{arr}", + "components.MovieDetails.downloadstatus": "Downloadstatus", + "components.MovieDetails.imdbuserscore": "Gebruikersbeoordeling IMDB", + "components.MovieDetails.openradarr": "Film openen in Radarr", + "components.MovieDetails.play": "Afspelen op {mediaServerName}", + "components.MovieDetails.play4k": "Afspelen op {mediaServerName} in 4K", + "components.Settings.Notifications.NotificationsPushover.sound": "Meldingsgeluid", + "components.Settings.SonarrModal.tagRequests": "Aanvragen taggen", + "components.Settings.jellyfinSettingsFailure": "Er is iets misgegaan bij het opslaan van de {mediaServerName}-instellingen.", + "components.Settings.jellyfinSettingsSuccess": "{mediaServerName}-instellingen opgeslagen!", + "components.Settings.jellyfinlibraries": "{mediaServerName}-bibliotheken", + "components.Settings.manualscanJellyfin": "Handmatige bibliotheekscan", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.saving": "Opslaan…", + "components.Settings.syncing": "Synchroniseren", + "components.Settings.timeout": "Time-out", + "components.Setup.signin": "Aanmelden", + "components.Setup.signinWithJellyfin": "{mediaServerName}-account gebruiken", + "components.TitleCard.addToWatchList": "Toevoegen aan kijklijst", + "components.TitleCard.watchlistError": "Er is iets misgegaan. Probeer het opnieuw.", + "components.UserList.importfromJellyfin": "{mediaServerName}-gebruikers importeren", + "components.UserList.importfromJellyfinerror": "Er is iets misgegaan bij het importeren van {mediaServerName}-gebruikers.", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName}-gebruiker", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Wijzigingen opslaan", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Opslaan…", + "i18n.collection": "Collectie", + "components.UserProfile.localWatchlist": "Kijklijst van {username}", + "components.Setup.signinWithPlex": "Plex-account gebruiken", + "components.Settings.jellyfinSettingsDescription": "Optioneel, configureer de interne en externe eindpunten voor uw {mediaServerName} server. In de meeste gevallen verschilt de externe URL met de interne URL. Een aangepaste wachtwoord reset URL kan ook gebruikt worden voor de {mediaServerName} login, voor het geval dat u doorverwezen wilt worden naar een andere wachtwoord reset pagina.", + "components.Settings.jellyfinsettingsDescription": "Configureer de instellingen voor uw {mediaServerName} server. {mediaServerName} scanned uw {mediaServerName} bibliotheken om te zien welke content beschikbaar is.", + "components.TitleCard.watchlistDeleted": "{title} Is succesvol verwijderd van de kijklijst!", + "components.TitleCard.watchlistSuccess": "{title} succesvol toegevoegd aan de kijklijst!", + "components.TitleCard.watchlistCancel": "kijklijst voor {title} is geannuleerd.", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} succesvol geimporteerd!", + "components.UserList.newJellyfinsigninenabled": "De Gebruik Nieuwe {mediaServerName} Login instelling staat momenteel aan. {mediaServerName} gebruikers met toegang tot de bibliotheek, hoeven niet geïmporteerd te worden om in te kunnen loggen." } diff --git a/src/i18n/locale/pl.json b/src/i18n/locale/pl.json index 22aa6229..dba8656f 100644 --- a/src/i18n/locale/pl.json +++ b/src/i18n/locale/pl.json @@ -1061,196 +1061,212 @@ "components.MovieDetails.rtaudiencescore": "Ocena Rotten Tomatoes", "components.MovieDetails.rtcriticsscore": "Tomatometer Rotten Tomatoes", "components.MovieDetails.tmdbuserscore": "Ocena użytkowników TMDB", - "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Bieżąca częstotliwość", - "components.TvDetails.seasonnumber": "Sezon {seasonNumber}", - "components.TvDetails.seasonstitle": "Sezony", - "components.Settings.SettingsJobsCache.imagecache": "Pamięć podręczna obrazów", - "components.PermissionEdit.viewrecent": "Wyświetl ostatnio dodane", - "components.PermissionEdit.viewrecentDescription": "Przyznaj uprawnienia do przeglądania listy ostatnio dodanych multimediów.", - "components.TitleCard.cleardata": "Wyczyść dane", - "components.RequestList.RequestItem.tmdbid": "Identyfikator TMDB", - "components.RequestList.RequestItem.tvdbid": "Identyfikator TVDB", - "components.TitleCard.tmdbid": "Identyfikator TMDB", - "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Synchronizacja listy obserwowanych Plex", - "components.TitleCard.mediaerror": "Nie znaleziono {mediaType}", - "components.TitleCard.tvdbid": "Identyfikator TVDB", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Automatyczne zamawianie filmów z listy obserwowanych Plex", - "components.PermissionEdit.autorequestSeriesDescription": "Udziel zgody na automatyczne przesyłanie próśb dotyczących multimediów innych niż 4K za pośrednictwem listy obserwowanych Plex.", - "components.PermissionEdit.viewwatchlists": "Wyświetlanie list obserwacyjnych Plex", - "components.PermissionEdit.viewwatchlistsDescription": "Przyznaj uprawnienia do przeglądania list obserwowanych Plex innych użytkowników.", - "components.RequestCard.tmdbid": "Identyfikator TMDB", - "components.RequestCard.tvdbid": "Identyfikator TVDB", - "components.Settings.SettingsLogs.viewdetails": "Zobacz szczegóły", - "components.Settings.restartrequiredTooltip": "Overseerr musi zostać ponownie uruchomiony, aby zmiany tego ustawienia zaczęły obowiązywać", - "components.TvDetails.reportissue": "Zgłoś problem", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Automatyczna prośba o serial", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Automatyczne zamawianie filmów z listy obserwowanych Plex", - "components.UserProfile.plexwatchlist": "Lista obserwowanych Plex", - "components.RequestCard.cancelrequest": "Anuluj prośbę", - "components.RequestCard.declinerequest": "Odrzuć prośbę", - "components.RequestCard.approverequest": "Zatwierdź prośbę", - "components.DownloadBlock.formattedTitle": "{title}: Sezon {seasonNumber} Odcinek {episodeNumber}", - "components.RequestBlock.approve": "Zatwierdź prośbę", - "components.RequestBlock.decline": "Odrzuć prośbę", - "components.RequestBlock.delete": "Usuń prośbę", - "components.RequestBlock.edit": "Edytuj prośbę", - "components.RequestBlock.lastmodifiedby": "Ostatnio zmodyfikowane przez", - "components.RequestBlock.requestdate": "Data złożenia prośby", - "components.RequestBlock.requestedby": "Prośba zgłoszona przez", - "components.RequestCard.editrequest": "Edytuj prośbę", - "components.RequestModal.requestcollection4ktitle": "Poproś o kolekcję w 4K", - "components.RequestModal.requestcollectiontitle": "Poproś o kolekcję", - "components.RequestModal.requestmovie4ktitle": "Poproś o film w 4K", - "components.RequestModal.requestmovietitle": "Poproś o film", - "components.RequestModal.requestseries4ktitle": "Poproś o serial w 4K", - "components.RequestModal.requestseriestitle": "Poproś o serial", - "components.Settings.SettingsJobsCache.image-cache-cleanup": "Czyszczenie pamięci podręcznej obrazów", - "components.Settings.SettingsJobsCache.imagecacheDescription": "Po włączeniu w ustawieniach Overseerr będzie pośredniczyć i buforować obrazy ze wstępnie skonfigurowanych źródeł zewnętrznych. Obrazy z pamięci podręcznej są zapisywane w folderze konfiguracji. Możesz znaleźć pliki w {appDataPath}/cache/images.", - "components.Settings.SettingsJobsCache.imagecachecount": "Obrazy zapisane w pamięci podręcznej", - "components.Settings.SettingsJobsCache.imagecachesize": "Całkowity rozmiar pamięci podręcznej", - "components.Settings.advancedTooltip": "Nieprawidłowe skonfigurowanie tego ustawienia może spowodować nieprawidłowe działanie", - "components.Settings.experimentalTooltip": "Włączenie tego ustawienia może spowodować nieoczekiwane zachowanie aplikacji", - "components.TvDetails.rtaudiencescore": "Ocena publiczności Rotten Tomatoes", - "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometr", - "components.TvDetails.status4k": "4K {status}", - "components.TvDetails.tmdbuserscore": "Ocena użytkowników TMDB", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Filmy z próśb automatycznych", - "components.UserProfile.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do listy obserwowanych Plex.", - "components.RequestCard.unknowntitle": "Nieznany tytuł", - "components.RequestList.RequestItem.unknowntitle": "Nieznany tytuł", - "components.StatusBadge.playonplex": "Odtwórz na Plex", - "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}", - "components.TvDetails.Season.noepisodes": "Lista odcinków jest niedostępna.", - "components.TvDetails.Season.somethingwentwrong": "Coś poszło nie tak podczas pobierania danych o sezonie.", - "components.TvDetails.manageseries": "Zarządzaj serialem", - "components.Discover.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do listy obserwowanych Plex.", - "components.RequestModal.SearchByNameModal.nomatches": "Nie udało nam się znaleźć dopasowania do tej serii.", - "components.StatusBadge.managemedia": "Zarządzaj {mediaType}", - "components.StatusBadge.openinarr": "Otwórz w {arr}", - "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Odcinek} other {# Odcinki}}", - "components.Discover.CreateSlider.addSlider": "Dodaj Suwak", - "components.Discover.CreateSlider.addcustomslider": "Utwórz niestandardowy Suwak", - "components.Discover.CreateSlider.addfail": "Nie udało się utworzyć nowego Suwaka.", - "components.Discover.CreateSlider.addsuccess": "Utworzono nowy Suwaki zapisano ustawienia dostosowywania odnajdywania.", - "components.Discover.CreateSlider.editSlider": "Edytuj Suwak", - "components.Discover.CreateSlider.editfail": "Nie udało się edytować Suwaka.", - "components.Discover.CreateSlider.editsuccess": "Edytowano Suwak i zapisano ustawienia odkrywania.", - "components.Discover.CreateSlider.needresults": "Musisz mieć co najmniej 1 wynik.", + "components.Discover.CreateSlider.addSlider": "Dodaj suwak", + "components.Discover.CreateSlider.addcustomslider": "Utwórz niestandardowy suwak", + "components.Discover.CreateSlider.addfail": "Nie udało się utworzyć nowego suwaka.", + "components.Discover.CreateSlider.addsuccess": "Stworzony nowy suwak i zapisano dostosowywania odkrywania.", + "components.Discover.CreateSlider.editSlider": "Edytuj suwak", + "components.Discover.CreateSlider.editfail": "Nie udało się edytować suwaka.", + "components.Discover.CreateSlider.needresults": "Musisz mieć przynajmniej 1 wynik.", "components.Discover.CreateSlider.nooptions": "Brak wyników.", - "components.Discover.CreateSlider.providetmdbgenreid": "Podaj identyfikator gatunku TMDB", - "components.Discover.CreateSlider.providetmdbkeywordid": "Podaj identyfikator słowa kluczowego TMDB", - "components.Discover.CreateSlider.providetmdbnetwork": "Podaj identyfikator platformy TMDB", + "components.Discover.CreateSlider.providetmdbgenreid": "Podaj ID gatunku TMDB", + "components.Discover.CreateSlider.providetmdbnetwork": "Podaj ID stacji TMDB", "components.Discover.CreateSlider.providetmdbsearch": "Podaj zapytanie wyszukiwania", - "components.Discover.CreateSlider.providetmdbstudio": "Podaj identyfikator studia TMDB", + "components.Discover.CreateSlider.providetmdbstudio": "Podaj ID studia TMDB", "components.Discover.CreateSlider.searchGenres": "Szukaj gatunków…", - "components.Discover.CreateSlider.searchKeywords": "Szukaj słów kluczowych…", - "components.Discover.CreateSlider.slidernameplaceholder": "Nazwa slidera", - "components.Discover.CreateSlider.starttyping": "Zacznij pisać, aby wyszukać.", - "components.Discover.CreateSlider.validationDatarequired": "Musisz uzyskać wartość danych.", - "components.Discover.CreateSlider.validationTitlerequired": "Musisz podać tytuł.", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filmy", - "components.Discover.DiscoverSliderEdit.deletefail": "Nie udało się usunąć slidera.", - "components.Discover.DiscoverSliderEdit.remove": "Usuń", - "components.Discover.DiscoverTvKeyword.keywordSeries": "Serial {keywordTitle}", - "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}", + "components.Discover.CreateSlider.searchStudios": "Wyszukaj studia…", + "components.Discover.CreateSlider.slidernameplaceholder": "Nazwa suwaka", + "components.Discover.CreateSlider.starttyping": "Zacznij pisać aby wyszukać.", + "components.Discover.CreateSlider.validationDatarequired": "Należy wprowadzić wartość danych.", + "components.Discover.CreateSlider.validationTitlerequired": "Należy podać tytuł.", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "Filmy: {keywordTitle}", "components.Discover.DiscoverMovies.discovermovies": "Filmy", "components.Discover.DiscoverMovies.sortPopularityAsc": "Popularność rosnąco", "components.Discover.DiscoverMovies.sortPopularityDesc": "Popularność malejąco", - "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Data wydania rosnąco", "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Data wydania malejąco", "components.Discover.DiscoverMovies.sortTitleAsc": "Tytuł (A-Z) rosnąco", "components.Discover.DiscoverMovies.sortTitleDesc": "Tytuł (Z-A) malejąco", "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Ocena TMDB rosnąco", "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Ocena TMDB malejąco", - "components.Discover.DiscoverSliderEdit.deletesuccess": "Pomyślnie usunięto slider.", + "components.Discover.DiscoverSliderEdit.deletefail": "Nie udało się usunąć suwaka.", + "components.Discover.DiscoverSliderEdit.deletesuccess": "Pomyślnie usunięto suwak.", "components.Discover.DiscoverSliderEdit.enable": "Przełącz widoczność", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}", - "components.Discover.DiscoverTv.discovertv": "Seriale", - "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Data pierwszej emisji rosnąco", - "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Pierwsza data emisji malejąco", + "components.Discover.DiscoverSliderEdit.remove": "Usuń", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Data premiery rosnąco", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Data premiery malejąco", "components.Discover.DiscoverTv.sortPopularityAsc": "Popularność rosnąco", "components.Discover.DiscoverTv.sortPopularityDesc": "Popularność malejąco", "components.Discover.DiscoverTv.sortTitleAsc": "Tytuł (A-Z) rosnąco", "components.Discover.DiscoverTv.sortTitleDesc": "Tytuł (Z-A) malejąco", - "components.Discover.DiscoverTv.sortTmdbRatingAsc": "Ocena TMDB rosnąco", "components.Discover.DiscoverTv.sortTmdbRatingDesc": "Ocena TMDB malejąco", - "components.Discover.CreateSlider.searchStudios": "Szukaj studiów…", - "components.Settings.SettingsMain.general": "Ogólne", - "components.Settings.SettingsMain.generalsettings": "Ustawienia ogólne", - "components.Settings.SettingsMain.originallanguageTip": "Filtruj zawartość według języka oryginału", - "components.Discover.PlexWatchlistSlider.emptywatchlist": "W tym miejscu pojawią się multimedia dodane do listy obserwowanych Plex.", - "components.Discover.networks": "Platformy", - "components.Discover.moviegenres": "Gatunki filmowe", - "components.Discover.tmdbnetwork": "Platforma TMDB", - "components.Discover.tmdbstudio": "Studio TMDB", - "components.Discover.PlexWatchlistSlider.plexwatchlist": "Twoja lista obserwowanych Plex", - "components.Discover.createnewslider": "Utwórz nowy suwak", - "components.Discover.customizediscover": "Dostosowywanie funkcji Odkryj", - "components.Discover.resetfailed": "Wystąpił problem podczas resetowania ustawień odnajdywania.", - "components.Discover.resetsuccess": "Pomyślnie zresetowano ustawienia odnajdywania.", - "components.Discover.resetwarning": "Przywróć wszystkie Suwaki do ustawień domyślnych. Spowoduje to również usunięcie wszystkich niestandardowych Suwaków!", - "components.Discover.resettodefault": "Przywróć ustawienia domyślne", - "components.Discover.studios": "Studia", - "components.Discover.tmdbmoviegenre": "Gatunek filmu TMDB", - "components.Discover.tvgenres": "Gatunki serialu", - "components.Settings.SettingsMain.csrfProtectionTip": "Ustaw zewnętrzny dostęp api na tylko do odczytu (wymaga HTTPS)", - "components.Settings.SettingsMain.csrfProtectionHoverTip": "NIE włączaj tego ustawienia, chyba że rozumiesz, co robisz!", - "components.Settings.SettingsMain.generalsettingsDescription": "Skonfiguruj globalne i domyślne ustawienia dla Overseerr.", - "components.Settings.SettingsMain.hideAvailable": "Ukryj dostępne multimedia", - "components.Settings.SettingsMain.trustProxyTip": "Pozwól Overseerr poprawnie rejestrować adresy IP klientów za serwerem proxy", - "components.Settings.SettingsMain.toastSettingsSuccess": "Ustawienia zostały zapisane pomyślnie!", - "components.Settings.SettingsMain.trustProxy": "Włącz obsługę proxy", - "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "Adres URL nie może kończyć się ukośnikiem", - "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# aktywny filtr} other {# aktywnych filtrów}}", "components.Discover.FilterSlideover.clearfilters": "Wyczyść aktywne filtry", "components.Discover.FilterSlideover.filters": "Filtry", - "components.Discover.FilterSlideover.firstAirDate": "Pierwsza data emisji", + "components.Discover.FilterSlideover.firstAirDate": "Data pierwszego wyemitowania", "components.Discover.FilterSlideover.from": "Od", "components.Discover.FilterSlideover.genres": "Gatunki", "components.Discover.FilterSlideover.keywords": "Słowa kluczowe", - "components.Discover.FilterSlideover.originalLanguage": "Język oryginalny", - "components.Discover.FilterSlideover.ratingText": "Oceny pomiędzy {minValue} a {maxValue}", + "components.Discover.FilterSlideover.ratingText": "Oceny między {minValue} a {maxValue}", + "components.Discover.CreateSlider.providetmdbkeywordid": "Podaj ID słowa kluczowego TMDB", + "components.Discover.CreateSlider.searchKeywords": "Wyszukaj słowa kluczowe…", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Data wydania rosnąco", + "components.Discover.DiscoverTv.discovertv": "Seriale", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "Ocena TMDB rosnąco", + "components.Discover.DiscoverTvKeyword.keywordSeries": "Seriale: {keywordTitle}", + "components.Discover.FilterSlideover.originalLanguage": "Oryginalny język", "components.Discover.FilterSlideover.releaseDate": "Data wydania", - "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} czas odtwarzania w minutach", - "components.Discover.FilterSlideover.runtime": "Czas odtwarzania", - "components.Discover.FilterSlideover.tmdbuserscore": "Ocena użytkowników TMDB", + "components.Discover.FilterSlideover.runtime": "Długość", + "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} minut trwania", + "components.Discover.FilterSlideover.streamingservices": "Serwisy streamingowe", + "components.Discover.FilterSlideover.studio": "Studio", + "components.Discover.FilterSlideover.tmdbuservotecount": "Liczba głosów użytkowników TMDB", "components.Discover.FilterSlideover.to": "Do", - "components.Discover.RecentlyAddedSlider.recentlyAdded": "Ostatnio dodane", - "components.Discover.stopediting": "Zatrzymaj edycję", - "components.Discover.tmdbmoviekeyword": "Słowo kluczowe filmu TMDB", - "components.Discover.tmdbsearch": "Wyszukiwanie TMDB", - "components.Layout.Sidebar.browsemovies": "Filmy", - "components.Layout.Sidebar.browsetv": "Seriale", - "components.Selector.searchGenres": "Wybierz gatunki…", - "components.Selector.searchKeywords": "Szukaj słów kluczowych…", - "components.Selector.showmore": "Pokaż więcej", - "components.Selector.starttyping": "Zacznij pisać, aby wyszukać.", - "components.Settings.SettingsMain.applicationTitle": "Tytuł aplikacji", - "components.Settings.SettingsMain.applicationurl": "Adres URL aplikacji", - "components.Settings.SettingsMain.cacheImages": "Włącz buforowanie obrazów", - "components.Settings.SettingsMain.csrfProtection": "Włącz ochronę CSRF", - "components.Settings.SettingsMain.locale": "Język wyświetlania", - "components.Settings.SettingsMain.originallanguage": "Odkryj język", - "components.Settings.SettingsMain.partialRequestsEnabled": "Zezwalaj na prośby o część serialu", - "components.Settings.SettingsMain.region": "Odkryj region", - "components.Settings.SettingsMain.regionTip": "Filtruj zawartość według dostępności regionalnej", - "components.Settings.SettingsMain.toastApiKeyFailure": "Coś poszło nie tak podczas generowania nowego klucza API.", - "components.Settings.SettingsMain.toastApiKeySuccess": "Nowy klucz API został pomyślnie wygenerowany!", - "components.Settings.SettingsMain.validationApplicationTitle": "Należy podać tytuł aplikacji", - "components.Settings.SettingsMain.validationApplicationUrl": "Musisz podać prawidłowy adres URL", - "components.Discover.FilterSlideover.streamingservices": "Usługi streamingowe", - "components.Discover.FilterSlideover.studio": "Studia", - "components.Discover.tmdbtvgenre": "Gatunek serialu TMDB", + "components.Discover.FilterSlideover.voteCount": "Liczba głosów między {minValue} a {maxValue}", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "Twoja lista obserwowanych Plex", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "Niedawno dodane", + "components.Discover.createnewslider": "Dodaj nowy suwak", + "components.Discover.customizediscover": "Dostosuj Odkryj", + "components.Discover.PlexWatchlistSlider.emptywatchlist": "Media dodane do twojej listy obserwowanych Plex zostaną wyświetlone tutaj.", + "components.Discover.emptywatchlist": "Media dodane do twojej listy obserwowanych Plex zostaną wyświetlone tutaj.", + "components.Discover.tmdbstudio": "Studio TMDB", "components.Discover.tmdbtvkeyword": "Słowo kluczowe serialu TMDB", - "components.Discover.updatesuccess": "Zaktualizowano ustawienia odnajdywania.", - "components.Selector.nooptions": "Brak wyników.", + "components.Discover.tmdbtvgenre": "Gatunek serialu TMDB", + "components.Discover.tmdbsearch": "Wyszukiwanie TMDB", + "components.Discover.FilterSlideover.tmdbuserscore": "Ocena użytkownika TMDB", + "components.Discover.moviegenres": "Gatunki filmu", + "components.Discover.networks": "Kanały", + "components.Discover.CreateSlider.editsuccess": "Edytowano suwak i zapisano ustawienia personalizacji.", + "components.Discover.stopediting": "Zakończ edytowanie", + "components.Discover.studios": "Studia", + "components.DownloadBlock.formattedTitle": "{title}: Sezon {seasonNumber} Odcinek {episodeNumber}", + "components.Discover.tvgenres": "Gatunki seriali", + "components.Discover.updatefailed": "Coś poszło nie tak podczas aktualizacji ustawień personalizacji wykrywania.", + "components.Discover.updatesuccess": "Zaktualizowano ustawienia personalizacji wykrywania.", + "components.Discover.resettodefault": "Przywróć domyślne", + "components.Discover.resetfailed": "Coś poszło nie tak podczas resetowania ustawień personalizacji wykrywania.", + "components.Discover.resetsuccess": "Pomyślnie zresetowano ustawienia wykrywania.", + "components.Discover.tmdbmoviegenre": "Gatunek filmu TMDB", + "components.Layout.Sidebar.browsetv": "Seriale", + "components.Layout.UserWarnings.emailInvalid": "Adres e-mail jest nieprawidłowy.", + "components.Layout.UserWarnings.passwordRequired": "Wymagane jest podanie hasła.", + "components.Login.credentialerror": "Nazwa użytkownika lub hasło są nieprawidłowe.", + "components.Login.emailtooltip": "Adres nie musi być powiązany z instancją {mediaServerName}.", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Połącz", + "components.Login.initialsigningin": "Łączenie…", + "components.Login.save": "Reklama", + "components.Login.saving": "Dodaję…", + "components.Login.signinwithjellyfin": "Użyj swojego konta {mediaServerName}", + "components.Login.title": "Dodaj e-mail", + "components.Login.username": "Nazwa użytkownika", + "components.Login.validationEmailRequired": "Musisz podać adres e-mail", + "components.Login.validationemailformat": "Wymagany poprawny adres e-mail", + "components.Login.validationhostformat": "Wymagany poprawny adres URL", + "components.Login.validationhostrequired": "{mediaServerName} URL wymagany", + "components.Login.validationusernamerequired": "Wymagana nazwa użytkownika", + "components.ManageSlideOver.removearr4k": "Usuń z 4k {arr}", + "components.MovieDetails.downloadstatus": "Status pobierania", + "components.MovieDetails.openradarr4k": "Otwórz film 4k w Radarr", + "components.MovieDetails.play": "Odtwórz na {mediaServerName}", + "components.MovieDetails.play4k": "Odtwórz w 4K na {mediaServerName}", + "components.PermissionEdit.viewrecent": "Wyświetl ostatnio dodane", + "components.PermissionEdit.viewrecentDescription": "Zezwolenie na wyświetlanie listy ostatnio dodanych multimediów.", + "components.RequestBlock.approve": "Zatwierdź żądanie", + "components.RequestBlock.decline": "Odrzuć żądanie", + "components.RequestBlock.delete": "Usuń żądanie", + "components.RequestBlock.lastmodifiedby": "Ostatnio modyfikowany przez", + "components.RequestBlock.requestdate": "Data żądania", + "components.RequestBlock.requestedby": "Żądany przez", + "components.RequestCard.cancelrequest": "Anuluj prośbę", + "components.RequestCard.declinerequest": "Odrzuć prośbę", + "components.RequestCard.editrequest": "Edytuj prośbę", + "components.RequestCard.unknowntitle": "Nieznany tytuł", + "components.RequestModal.requestcollectiontitle": "Poproś o kolekcję", + "components.RequestModal.requestmovie4ktitle": "Poproś o film w 4K", + "components.RequestModal.requestmovietitle": "Poproś o film", + "components.RequestModal.requestseries4ktitle": "Poproś o serial w 4K", + "components.RequestModal.requestseriestitle": "Poproś o serial", + "components.Selector.searchGenres": "Wybierz gatunki…", + "components.Selector.searchKeywords": "Wyszukaj słowa kluczowe…", "components.Selector.searchStudios": "Szukaj studiów…", "components.Selector.showless": "Pokaż mniej", - "components.Discover.updatefailed": "Wystąpił problem podczas aktualizowania ustawień odnajdywania.", + "components.Selector.starttyping": "Rozpocznij wpisywanie, aby wyszukać.", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Ostatnio dodane skanowanie Jellyfin", "components.Settings.SettingsMain.apikey": "Klucz API", - "components.Settings.SettingsMain.cacheImagesTip": "Pamięć podręczna dla obrazów pochodzących z zewnętrznych źródeł (wymaga znacznej ilości miejsca na dysku)", + "components.Settings.SettingsMain.applicationTitle": "Nazwa aplikacji", + "components.Settings.SettingsMain.applicationurl": "URL aplikacji", + "components.Settings.SettingsMain.csrfProtection": "Włącz ochronę CSRF", + "components.Settings.SettingsMain.csrfProtectionHoverTip": "NIE włączaj tego ustawienia, jeśli nie rozumiesz, co robisz!", + "components.Settings.SettingsMain.csrfProtectionTip": "Ustawienie dostępu do zewnętrznego API tylko do odczytu (wymaga HTTPS)", + "components.Settings.SettingsMain.general": "Ogólne", + "components.Settings.SettingsMain.generalsettings": "Ustawienia ogólne", + "components.Settings.SettingsMain.generalsettingsDescription": "Konfiguracja globalnych i domyślnych ustawień Overseerr.", + "components.Settings.SettingsMain.hideAvailable": "Ukryj dostępne media", + "components.Settings.SettingsMain.locale": "Wyświetlany język", + "components.Settings.SettingsMain.partialRequestsEnabled": "Zezwalaj na częściowe żądania seriali", + "components.Settings.SettingsMain.region": "Region odkrywania", + "components.Settings.SettingsMain.regionTip": "Filtrowanie zawartości według dostępności regionalnej", "components.Settings.SettingsMain.toastSettingsFailure": "Coś poszło nie tak podczas zapisywania ustawień.", - "i18n.collection": "Kolekcja", - "components.MovieDetails.imdbuserscore": "Ocena użytkowników IMDB", - "components.Settings.SonarrModal.seriesType": "Typ serialu" + "components.Settings.SettingsMain.trustProxyTip": "Umożliwienie Overseerr poprawnego rejestrowania adresów IP klientów za serwerem proxy", + "components.Settings.jellyfinSettings": "{mediaServerName} Ustawienia", + "components.Settings.jellyfinSettingsFailure": "Coś poszło nie tak podczas zapisywania ustawień {mediaServerName}.", + "components.Settings.jellyfinSettingsSuccess": "Ustawienia {mediaServerName} zostały pomyślnie zapisane!", + "components.Settings.jellyfinlibrariesDescription": "Biblioteki {mediaServerName} skanują w poszukiwaniu tytułów. Kliknij poniższy przycisk, jeśli na liście nie ma żadnych bibliotek.", + "components.Settings.jellyfinsettings": "{mediaServerName} ustawienia", + "components.Settings.jellyfinsettingsDescription": "Konfiguracja ustawień serwera {mediaServerName}. {mediaServerName} skanuje biblioteki {mediaServerName}, aby sprawdzić, jaka zawartość jest dostępna.", + "components.Settings.manualscanJellyfin": "Ręczne skanowanie biblioteki", + "components.Settings.saving": "Zapisywanie…", + "components.Settings.syncing": "Synchronizowanie", + "components.Setup.signinWithPlex": "Użyj konta Plex", + "components.StatusBadge.openinarr": "Otwórz w {arr}", + "components.StatusBadge.playonplex": "Odtwórz na {mediaServerName}", + "components.TitleCard.watchlistError": "Coś poszło nie tak, spróbuj ponownie.", + "components.TvDetails.Season.somethingwentwrong": "Coś poszło nie tak podczas pobierania danych sezonu.", + "components.TvDetails.seasonnumber": "Sezon {seasonNumber}", + "components.TvDetails.seasonstitle": "Sezony", + "components.TvDetails.status4k": "4K {status}", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Automatyczne żądania filmów", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Zapisz zmiany", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Zapisywanie…", + "components.Layout.Sidebar.browsemovies": "Filmy", + "components.Login.validationEmailFormat": "Nieprawidłowy adres e-mail", + "components.RequestModal.SearchByNameModal.nomatches": "Nie udało nam się znaleźć odpowiednika dla tego serialu.", + "components.Selector.showmore": "Pokaż więcej", + "components.Settings.SettingsMain.cacheImagesTip": "Pamięć podręczna obrazów pochodzących z zewnątrz (wymaga znacznej ilości miejsca na dysku)", + "components.Settings.SettingsMain.originallanguageTip": "Filtrowanie zawartości według oryginalnego języka", + "components.Settings.SettingsMain.toastSettingsSuccess": "Ustawienia zapisane pomyślnie!", + "components.Settings.experimentalTooltip": "Włączenie tego ustawienia może spowodować nieoczekiwane zachowanie aplikacji", + "components.UserList.mediaServerUser": "{mediaServerName} Użytkownik", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "Email", + "components.Discover.resetwarning": "Przywróć domyślne ustawienia wszystkich suwaków. Spowoduje to również usunięcie wszystkich niestandardowych suwaków!", + "components.Layout.UserWarnings.emailRequired": "Wymagany jest adres e-mail.", + "components.MovieDetails.openradarr": "Otwórz film w Radarr", + "components.RequestModal.requestcollection4ktitle": "Poproś o kolekcję w 4K", + "components.Setup.signinWithJellyfin": "Użyj konta {mediaServerName}", + "components.Login.description": "Ponieważ logujesz się do {applicationName} po raz pierwszy, musisz dodać prawidłowy adres e-mail.", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Spowoduje to nieodwracalne usunięcie tego {mediaType} z {arr}, w tym wszystkich plików.", + "components.ManageSlideOver.removearr": "Usuń z {arr}", + "components.PermissionEdit.autorequestSeriesDescription": "Zezwolenie na automatyczne przesyłanie żądań dotyczących seriali innych niż 4K za pośrednictwem listy obserwowanych Plex.", + "components.PermissionEdit.viewwatchlists": "Wyświetl {mediaServerName} watchlistę", + "components.RequestCard.approverequest": "Zaakceptuj prośbę", + "components.Settings.RadarrModal.tagRequestsInfo": "Automatycznie dodawaj dodatkowy znacznik z identyfikatorem użytkownika i wyświetlaną nazwą użytkownika", + "components.Settings.SettingsAbout.supportjellyseerr": "Wspomóż Jellyseerr", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Pełne skanowanie bibliotek Jellyfin", + "components.Settings.SettingsLogs.viewdetails": "Zobacz szczegóły", + "components.Settings.jellyfinSettingsDescription": "Opcjonalnie skonfiguruj wewnętrzne i zewnętrzne punkty końcowe dla serwera {mediaServerName}. W większości przypadków zewnętrzny adres URL różni się od wewnętrznego adresu URL. Niestandardowy adres URL resetowania hasła można również ustawić dla logowania {mediaServerName}, na wypadek gdybyś chciał przekierować na inną stronę resetowania hasła.", + "components.Settings.save": "Zapisz zmiany", + "components.Settings.syncJellyfin": "Synchronizuj biblioteki", + "components.TvDetails.Season.noepisodes": "Lista odcinków jest niedostępna.", + "components.Settings.Notifications.NotificationsPushover.sound": "Dźwięk powiadomień", + "components.Settings.RadarrModal.tagRequests": "Żądania tagów", + "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Aktualna częstotliwość", + "components.Settings.SettingsMain.validationApplicationTitle": "Musisz podać tytuł aplikacji", + "components.Settings.SonarrModal.seriesType": "Typ seriali", + "components.Settings.advancedTooltip": "Nieprawidłowe skonfigurowanie tego ustawienia może spowodować nieprawidłowe działanie", + "components.TvDetails.reportissue": "Zgłoś problem", + "components.RequestBlock.edit": "Edytuj żądanie", + "components.RequestList.RequestItem.unknowntitle": "Nieznany tytuł", + "components.Selector.nooptions": "Brak wyników.", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Domyślne urządzenie", + "components.Settings.Notifications.userEmailRequired": "Wymagaj adresu e-mail użytkownika", + "components.Settings.manualscanDescriptionJellyfin": "Zwykle będzie to uruchamiane tylko raz na 24 godziny. Jellyseerr będzie bardziej agresywnie sprawdzać ostatnio dodane biblioteki serwera {mediaServerName}. Jeśli po raz pierwszy konfigurujesz Jellyseerr, zalecane jest jednorazowe pełne ręczne skanowanie biblioteki!" } diff --git a/src/i18n/locale/pt_BR.json b/src/i18n/locale/pt_BR.json index bce31a5b..ca8f07a3 100644 --- a/src/i18n/locale/pt_BR.json +++ b/src/i18n/locale/pt_BR.json @@ -1257,9 +1257,35 @@ "components.Discover.FilterSlideover.tmdbuservotecount": "Qtd de Votos de Usuários TMDB", "components.Discover.FilterSlideover.voteCount": "Qtd the votos entre {minValue} e {maxValue}", "components.Settings.RadarrModal.tagRequestsInfo": "Adicione automaticamente uma tag extra com o ID de usuário e o nome de exibição do solicitante", - "i18n.collection": "Coleção", - "components.MovieDetails.imdbuserscore": "Pontuação de usuário IMDB", - "components.Settings.SonarrModal.tagRequestsInfo": "Adiciona automaticamente uma tag adicional com o ID de usuário e nome de exibição de quem pediu", - "components.Settings.SonarrModal.tagRequests": "Marcar Pedidos", - "components.Settings.RadarrModal.tagRequests": "Marcar Pedidos" + "components.Layout.UserWarnings.emailRequired": "Um endereço de e-mail é necessário.", + "components.Login.credentialerror": "O nome de usuário ou senha está incorreto.", + "components.Login.description": "Já que é sua primeira vez entrando em {applicationName}, você precisa adicionar um e-mail válido.", + "components.Login.host": "URL de {mediaServerName}", + "components.Login.initialsignin": "Conectar", + "components.Login.initialsigningin": "Conectando…", + "components.Login.save": "Adicionar", + "components.Login.saving": "Adicionando…", + "components.Login.signinwithjellyfin": "Use sua conta de {mediaServerName}", + "components.Login.title": "Adicionar E-Mail", + "components.Login.username": "Nome de usuário", + "components.Login.validationEmailFormat": "E-mail inválido", + "components.Login.validationEmailRequired": "Você precisa providenciar um e-mail", + "components.Login.validationemailformat": "E-mail válido necessário", + "components.Login.validationhostformat": "URL válido necessário", + "components.Login.validationusernamerequired": "Nome de usuário necessário", + "components.ManageSlideOver.removearr": "Remover de {arr}", + "components.ManageSlideOver.removearr4k": "Remover de {arr} 4K", + "components.MovieDetails.downloadstatus": "Status de download", + "components.MovieDetails.imdbuserscore": "Avaliação de usuário no IMDB", + "components.MovieDetails.openradarr": "Abrir filme no Radarr", + "components.Settings.Notifications.NotificationsPushover.sound": "Som de notificação", + "components.Login.emailtooltip": "Endereço não precisa ser associado à sua instância de {mediaServerName}.", + "components.Layout.UserWarnings.emailInvalid": "Endereço de e-mail inválido.", + "components.Layout.UserWarnings.passwordRequired": "Uma senha é necessária.", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Padrão do dispositivo", + "components.Login.validationhostrequired": "URL de {mediaServerName} necessário", + "components.MovieDetails.openradarr4k": "Abrir filme em Radarr 4K", + "components.MovieDetails.play": "Reproduzir em {mediaServerName}", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Isto irá remover este {mediaType} de {arr}, incluindo todos os arquivos.", + "components.MovieDetails.play4k": "Reproduzir em 4K em {mediaServerName}" } diff --git a/src/i18n/locale/ro.json b/src/i18n/locale/ro.json index 5b399a79..be5997a6 100644 --- a/src/i18n/locale/ro.json +++ b/src/i18n/locale/ro.json @@ -370,7 +370,7 @@ "components.PlexLoginButton.signinwithplex": "Conectat", "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} per {quotaDays} {days}", "components.PersonDetails.lifespan": "{birthdate} – {deathdate}", - "components.RequestBlock.seasons": "{seasonCount, plural, one {Sezon} other {Sezoane}}", + "components.RequestBlock.seasons": "{seasonCount, plural, un {Season} alte {Seasons}}", "components.PermissionEdit.requestMoviesDescription": "Acordați permisiunea de a trimite solicitări pentru filme non-4K.", "components.PermissionEdit.viewissuesDescription": "Acordați permisiunea de a vizualiza problemele media raportate de alți utilizatori.", "components.PermissionEdit.viewwatchlistsDescription": "Acordați permisiunea de a vizualiza listele de urmărire Plex ale altor utilizatori.", @@ -403,62 +403,40 @@ "components.RequestBlock.approve": "Aprobă Solicitarea", "components.RequestBlock.decline": "Respinge Solicitarea", "components.RequestBlock.requestedby": "Solicitat de", - "components.RequestButton.approve4krequests": "Aprobă {requestCount, plural, one {Cerere 4K} other {{requestCount} Cereri 4K}}", + "components.RequestButton.approve4krequests": "Aprobă {requestCount, plural, o {4K Request} alte {{requestCount} 4K Requests}}", "components.RequestBlock.lastmodifiedby": "Ultima Dată Modificat de", "components.RequestBlock.profilechanged": "Profil Calitate", "components.RequestBlock.requestdate": "Dată Solicitare", - "components.RequestButton.approverequests": "Aprobă {requestCount, plural, one {Cerere} other {{requestCount} Cereri}}", - "components.RequestButton.requestmore4k": "Cere mai mult în 4K", - "components.RequestButton.approverequest4k": "Aproba Cereri 4K", - "components.RequestCard.tmdbid": "ID TMDB", - "components.RequestCard.failedretry": "A apărut o eroare la reîncercarea solicitării.", - "components.RequestButton.declinerequest": "Respinge Cerere", - "components.RequestCard.seasons": "{seasonCount, plural, one {Sezon} other {Sezoane}}", - "components.RequestCard.declinerequest": "Respinge Cererea", - "components.RequestButton.viewrequest": "Vezi Cerere", - "components.RequestButton.declinerequests": "Respinge {requestCount, plural, one {Cererea} other {{requestCount} Cererile}}", - "components.RequestCard.mediaerror": "{mediaType} Nu a fost găsit", - "components.RequestCard.editrequest": "Editează Cererea", - "components.RequestButton.viewrequest4k": "Vezi Cerere 4K", - "components.RequestButton.decline4krequests": "Respinge {requestCount, plural, one {Cererea 4K} other {{requestCount} Cererile 4K}}", - "components.RequestButton.declinerequest4k": "Respinge Cerere 4K", - "components.RequestCard.approverequest": "Aprobă Cererea", - "components.RequestButton.approverequest": "Cereri Aprobate", - "components.RequestCard.deleterequest": "Șterge Cererea", - "components.RequestCard.unknowntitle": "Titlu necunoscut", - "components.RequestList.RequestItem.cancelRequest": "Anulează Cerere", - "components.RequestCard.tvdbid": "ID TheTVDB", - "components.RequestButton.requestmore": "Cere mai mult", - "components.RequestList.RequestItem.deleterequest": "Șterge Cerere", - "components.RequestCard.cancelrequest": "Anulează Cererea", - "components.RequestModal.AdvancedRequester.folder": "{path} ({space})", - "components.RequestList.RequestItem.modified": "Modificat", - "components.RequestList.RequestItem.editrequest": "Editează Cererea", - "components.RequestModal.AdvancedRequester.qualityprofile": "Profil de Calitate", - "components.RequestList.requests": "Cereri", - "components.RequestModal.AdvancedRequester.advancedoptions": "Avansat", - "components.RequestModal.AdvancedRequester.notagoptions": "Fără etichete.", - "components.RequestList.RequestItem.modifieduserdate": "{date} de {user}", - "components.RequestModal.AdvancedRequester.requestas": "Cere ca", - "components.RequestList.showallrequests": "Afișează toate cererile", - "components.RequestList.RequestItem.tmdbid": "ID-ul TMDB", - "components.RequestList.RequestItem.requesteddate": "Solicitat", - "components.RequestModal.QuotaDisplay.movie": "film", - "components.RequestList.RequestItem.failedretry": "Ceva a mers greșit în timpul reîncercării cererii.", - "components.RequestList.RequestItem.unknowntitle": "Titlu Necunoscut", - "components.RequestModal.AdvancedRequester.destinationserver": "Server Destinație", - "components.RequestModal.AdvancedRequester.rootfolder": "Folder Rădăcină", - "components.RequestList.sortAdded": "Cele Mai Recente", - "components.RequestModal.AdvancedRequester.tags": "Etichete", - "components.RequestList.RequestItem.mediaerror": "{mediaType} nu a fost găsit", - "components.RequestList.sortModified": "Ultima Modificată", - "components.RequestList.RequestItem.tvdbid": "ID-ul TheTVDB", - "components.RequestModal.AdvancedRequester.selecttags": "Selectați Etichetele", - "components.RequestList.RequestItem.requested": "Solicitat", - "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Nu sunt suficiente cereri de sezon rămase", - "components.RequestModal.AdvancedRequester.default": "{name} (Implicit)", - "components.RequestModal.AdvancedRequester.languageprofile": "Profil de Limbă", - "components.RequestModal.QuotaDisplay.allowedRequestsUser": "Acest utilizator are voie sa ceara {limit} {type} la fiecare {days} zile.", - "components.RequestList.RequestItem.seasons": "", - "components.RequestModal.QuotaDisplay.allowedRequests": "Aveți voie să cereți {limit} de {type} la fiecare {days} zile." + "components.Layout.UserWarnings.emailRequired": "Este necesara o adresa de email.", + "components.Layout.UserWarnings.passwordRequired": "Este necesara o parola.", + "components.Login.credentialerror": "Numele de utilizator sau parola sunt incorecte.", + "components.Login.emailtooltip": "Nu este necesar ca adresa ta sa fie asociata cu instanta ta {mediaServerName} .", + "components.Login.save": "Adauga", + "components.Login.signinwithjellyfin": "Foloseste-ti contul de {mediaServerName}", + "components.Login.title": "Adauga email", + "components.Login.username": "Nume utilizator", + "components.Login.validationEmailFormat": "Adresa email invalida", + "components.Login.validationemailformat": "Necesar email valid", + "components.Login.validationhostformat": "Necesar URL valid", + "components.Login.validationhostrequired": "{mediaServerName} URL necesar", + "components.Login.validationusernamerequired": "Nume utilizator necesar", + "components.MovieDetails.downloadstatus": "Status descarcare", + "components.MovieDetails.openradarr": "Deschide film in Radarr", + "components.MovieDetails.openradarr4k": "Deschide film in 4K Radarr", + "components.MovieDetails.play": "Ruleaza pe {mediaServerName}", + "components.MovieDetails.play4k": "Ruleaza 4k pe {mediaServerName}", + "components.RequestButton.declinerequest": "Refuza cerere", + "components.RequestButton.declinerequest4k": "Refuza cerere 4k", + "components.Layout.UserWarnings.emailInvalid": "Adresa de email este invalida.", + "components.Login.description": "Deoarece este prima ta authentificare in {applicationName}, este necesara sa introduci o adresa de email valida.", + "components.Login.initialsigningin": "Se conecteaza…", + "components.Login.saving": "Se adauga…", + "components.RequestButton.approverequest": "Aproba cerere", + "components.RequestButton.approverequest4k": "Aproba cerere 4k", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Conecteaza-te", + "components.ManageSlideOver.removearr4k": "Elimina din 4K {arr}", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Acesta va elimina ireversibil {mediaType} din {arr}, incluzand toate fisierele asociate.", + "components.Login.validationEmailRequired": "Trebuie sa introduci o adresa email", + "components.ManageSlideOver.removearr": "Elimina din {arr}" } diff --git a/src/i18n/locale/ru.json b/src/i18n/locale/ru.json index e4cb536d..21ee87dc 100644 --- a/src/i18n/locale/ru.json +++ b/src/i18n/locale/ru.json @@ -33,7 +33,7 @@ "components.RequestModal.cancel": "Отменить запрос", "components.RequestModal.extras": "Дополнительно", "components.RequestModal.numberofepisodes": "# эпизодов", - "components.RequestModal.pendingrequest": "", + "components.RequestModal.pendingrequest": "Ожидающий запрос", "components.RequestModal.requestCancel": "Запрос на {title} отменён.", "components.RequestModal.requestSuccess": "{title} успешно запрошен!", "components.RequestModal.requestadmin": "Этот запрос будет одобрен автоматически.", @@ -170,7 +170,7 @@ "pages.oops": "Упс", "pages.returnHome": "Вернуться домой", "components.CollectionDetails.overview": "Обзор", - "components.CollectionDetails.numberofmovies": "{count} {count, plural, one {фильм} few {фильма} other {фильмов}}", + "components.CollectionDetails.numberofmovies": "{count} фильмов", "components.CollectionDetails.requestcollection": "Запросить Коллекцию", "components.Login.email": "Адрес электронной почты", "components.UserList.users": "Пользователи", @@ -427,9 +427,9 @@ "components.Settings.RadarrModal.testFirstRootFolders": "Протестировать подключение для загрузки корневых каталогов", "components.Settings.RadarrModal.loadingrootfolders": "Загрузка корневых каталогов…", "components.RequestModal.AdvancedRequester.destinationserver": "Сервер-получатель", - "components.RequestList.RequestItem.mediaerror": "Название, связанное с этим запросом, больше недоступно.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} не найдено", "components.RequestList.RequestItem.failedretry": "Что-то пошло не так при попытке повторить запрос.", - "components.RequestCard.mediaerror": "Название, связанное с этим запросом, больше недоступно.", + "components.RequestCard.mediaerror": "{mediaType} не найдено", "components.RequestCard.failedretry": "Что-то пошло не так при попытке повторить запрос.", "components.RequestButton.viewrequest4k": "Посмотреть 4К запрос", "components.RequestButton.requestmore4k": "Запросить больше в 4К", @@ -570,7 +570,7 @@ "components.TvDetails.seasons": "{seasonCount, plural, one {# сезон} other {# сезонов}}", "components.RequestModal.QuotaDisplay.requiredquotaUser": "Этому пользователю необходимо иметь по крайней мере {seasons} {seasons, plural, one {запрос на сезоны} other {запроса(ов) на сезоны}} для того, чтобы отправить запрос на этот сериал.", "components.RequestModal.QuotaDisplay.requiredquota": "Вам необходимо иметь по крайней мере {seasons} {seasons, plural, one {запрос на сезоны} other {запроса(ов) на сезоны}} для того, чтобы отправить запрос на этот сериал.", - "components.RequestModal.pending4krequest": "", + "components.RequestModal.pending4krequest": "Ожидающий 4K запрос", "components.RequestModal.autoapproval": "Автоматическое одобрение", "i18n.usersettings": "Настройки пользователя", "i18n.showingresults": "Показываются результаты с {from} по {to} из {total}", @@ -790,7 +790,7 @@ "components.UserList.importfromplexerror": "Что-то пошло не так при импорте пользователей из Plex.", "components.UserList.importfrommediaserver": "Импортировать пользователей из {mediaServerName}", "components.UserList.importfromplex": "Импортировать пользователей из Plex", - "components.UserList.importedfromplex": "{userCount, plural, one {# новый пользователь} other {# новых пользователя(ей)}} успешно импортированы из Plex!", + "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {# новый пользователь} other {# новых пользователя(ей)}} успешно импортированы из Plex!", "components.UserList.edituser": "Изменить разрешения пользователя", "components.UserList.displayName": "Отображаемое имя", "components.UserList.deleteconfirm": "Вы уверены, что хотите удалить этого пользователя? Все данные о его запросах будут удалены без возможности восстановления.", @@ -1018,7 +1018,7 @@ "components.Discover.CreateSlider.addcustomslider": "Создать слайдер", "components.Discover.CreateSlider.nooptions": "Нет результатов.", "components.Discover.CreateSlider.providetmdbgenreid": "Введите TMDB ID жанра", - "components.Discover.CreateSlider.needresults": "Должен быть хотя бы 1 результат.", + "components.Discover.CreateSlider.needresults": "Должен быть хотя-бы 1 результат.", "components.Discover.CreateSlider.providetmdbkeywordid": "Введите TMDB ID ключевого слова", "components.Discover.CreateSlider.providetmdbnetwork": "Введите TMDB ID сети", "components.Discover.CreateSlider.providetmdbsearch": "Введите поисковой запрос", @@ -1027,7 +1027,7 @@ "components.Discover.CreateSlider.searchKeywords": "Поиск ключевых слов…", "components.Discover.CreateSlider.searchStudios": "Поиск студий…", "components.Discover.CreateSlider.slidernameplaceholder": "Название слайдера", - "components.Discover.CreateSlider.starttyping": "Начните писать для поиска.", + "components.Discover.CreateSlider.starttyping": "Начините писать для поиска.", "components.Discover.CreateSlider.validationDatarequired": "Вы должны ввести дату.", "components.Discover.CreateSlider.validationTitlerequired": "Вы должны ввести заголовок.", "components.Discover.DiscoverMovieKeyword.keywordMovies": "Фильмы по ключевому слову \"{keywordTitle}\"", @@ -1059,7 +1059,7 @@ "components.Discover.DiscoverSliderEdit.deletesuccess": "Слайдер успешно удален.", "components.Discover.DiscoverSliderEdit.enable": "Изменить видимость", "components.Discover.DiscoverSliderEdit.remove": "Удалить", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активен фильтр} other {# Активные фильтры }}", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активный фильтр} other {# Активные фильтры}}", "components.Discover.DiscoverTv.discovertv": "Сериалы", "components.Discover.DiscoverTv.sortPopularityAsc": "Популярность по возрастанию", "components.Discover.DiscoverTv.sortPopularityDesc": "Популярность по убыванию", @@ -1117,7 +1117,7 @@ "components.MovieDetails.digitalrelease": "Цифровой релиз", "components.MovieDetails.physicalrelease": "Физический релиз", "components.Settings.SettingsMain.toastSettingsFailure": "Что-то пошло не так при сохранении настроек.", - "components.Settings.SettingsMain.trustProxyTip": "Разрешить Overserr правильно регистрировать IP-адреса клиентов за прокси-сервером", + "components.Settings.SettingsMain.trustProxyTip": "Разрешить Jellyseerr правильно регистрировать IP-адреса клиентов за прокси-сервером", "components.Settings.experimentalTooltip": "Включение этого параметра может привести к неожиданному поведению приложения", "components.Settings.advancedTooltip": "Неправильная настройка этого параметра может привести к нарушению функциональности", "components.Settings.externalUrl": "Внешний URL-адрес", @@ -1131,7 +1131,7 @@ "components.Settings.validationApiKey": "Вы должны предоставить ключ API", "components.TitleCard.mediaerror": "{mediaType} не найдено", "components.TitleCard.tmdbid": "TMDB ID", - "components.Settings.restartrequiredTooltip": "Чтобы изменения этого параметра вступили в силу, необходимо перезапустить Overserr", + "components.Settings.restartrequiredTooltip": "Чтобы изменения этого параметра вступили в силу, необходимо перезапустить Jellyseerr", "components.ManageSlideOver.alltime": "Все время", "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {просмотр} other {просмотров}}", "components.Settings.Notifications.NotificationsGotify.agentenabled": "Включить агент", @@ -1214,13 +1214,13 @@ "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "Отправка тестового уведомления Gotify…", "components.Settings.SettingsMain.cacheImages": "Включить кэширование изображений", "components.Settings.SettingsMain.generalsettings": "Общие настройки", - "components.Settings.SettingsMain.generalsettingsDescription": "Настройте глобальные параметры и параметры по умолчанию для Overserr.", + "components.Settings.SettingsMain.generalsettingsDescription": "Настройте глобальные параметры и параметры по умолчанию для Jellyseerr.", "components.Settings.SettingsMain.hideAvailable": "Скрыть доступные медиа", "components.Settings.SettingsMain.regionTip": "Фильтровать контент по региональной доступности", "components.Settings.SettingsMain.toastApiKeyFailure": "Что-то пошло не так при создании нового ключа API.", "components.Settings.SettingsMain.locale": "Язык приложения", "components.Settings.SettingsMain.originallanguage": "Регион поиска", - "components.Settings.tautulliSettingsDescription": "При желании настройте параметры для вашего сервера Tautulli. Overserr извлекает данные истории просмотров Plex из Tautulli.", + "components.Settings.tautulliSettingsDescription": "При желании настройте параметры для вашего сервера Tautulli. Jellyseerr извлекает данные истории просмотров Plex из Tautulli.", "components.Settings.toastTautulliSettingsFailure": "Что-то пошло не так при сохранении настроек Tautulli.", "components.Settings.toastTautulliSettingsSuccess": "Настройки Tautulli успешно сохранены!", "components.Settings.validationUrlBaseTrailingSlash": "База URL не должна заканчиваться косой чертой", @@ -1241,7 +1241,7 @@ "components.Settings.Notifications.NotificationsGotify.validationTypes": "Вы должны выбрать как минимум один тип уведомления", "components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "Вы должны указать действующий URL", "components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "URL не должен заканчиваться слешем", - "components.Settings.SettingsJobsCache.imagecacheDescription": "Если включено, Overserr будет проксировать и кэшировать изображения из предварительно настроенных внешних источников. Кэшированные изображения сохраняются в папку конфигурации. Вы можете найти файлы в {appDataPath}/cache/images.", + "components.Settings.SettingsJobsCache.imagecacheDescription": "Если включено, Jellyseerr будет проксировать и кэшировать изображения из предварительно настроенных внешних источников. Кэшированные изображения сохраняются в папку конфигурации. Вы можете найти файлы в {appDataPath}/cache/images.", "components.Settings.tautulliSettings": "Настройки Tautulli", "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}", "components.Discover.customizediscover": "Настроить Обнаружение", @@ -1257,21 +1257,92 @@ "components.Selector.showmore": "Показать больше", "components.Settings.SettingsJobsCache.imagecachesize": "Размер кэша", "components.Settings.validationUrlBaseLeadingSlash": "Базовый URL должен начинаться с косой черты", - "components.Discover.FilterSlideover.tmdbuservotecount": "Количество голосов пользователей TMDB", - "components.Discover.FilterSlideover.voteCount": "Количество голосов между {minValue} и {maxValue}", - "components.Discover.tmdbmoviestreamingservices": "Стриминговые сервисы фильмов TMDB", - "components.Discover.tmdbtvstreamingservices": "Стриминговые сервисы сериалов TMDB", - "components.Settings.RadarrModal.tagRequestsInfo": "Автоматически добавлять дополнительный тег с ID и именем запросившего пользователя", - "components.Settings.RadarrModal.tagRequests": "Теги запросов", + "components.Layout.UserWarnings.emailRequired": "Требуется указать email адрес.", + "components.Layout.UserWarnings.passwordRequired": "Требуется указать пароль.", + "components.Login.emailtooltip": "Адрес не обязательно должен быть связан с вашим {mediaServerName} сервером.", + "components.Login.initialsignin": "Подключиться", + "components.Login.initialsigningin": "Подключение…", + "components.Login.save": "Добавить", + "components.Login.saving": "Добавление…", + "components.Login.signinwithjellyfin": "Используйте свой {mediaServerName} аккаунт", + "components.Login.host": "{mediaServerName} URL", + "components.Login.username": "Имя пользователя", + "components.Login.validationEmailFormat": "Неверный email", + "components.Login.validationemailformat": "Необходим корректный email", + "components.Login.validationhostformat": "Необходим корректный URL", + "components.Login.validationhostrequired": "Необходим {mediaServerName} URL", + "components.Login.validationusernamerequired": "Необходимо имя пользователя", + "components.ManageSlideOver.removearr": "Удалить из {arr}", + "components.ManageSlideOver.removearr4k": "Удалить из 4К {arr}", "components.MovieDetails.imdbuserscore": "Оценка пользователей IMDB", - "components.Settings.SettingsJobsCache.availability-sync": "Синхронизация доступности медиа", - "components.Settings.SonarrModal.tagRequests": "Теги запросов", - "components.Settings.SonarrModal.tagRequestsInfo": "Автоматически добавлять тег с ID и именем запросившего пользователя", - "i18n.collection": "Коллекция", - "components.Settings.Notifications.NotificationsPushover.sound": "Звук уведомления", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Устройство по умолчанию", - "components.Settings.SonarrModal.animeSeriesType": "Тип аниме-сериала", + "components.MovieDetails.openradarr": "Открыть фильм в Radarr", + "components.MovieDetails.play": "Запустить на {mediaServerName}", + "components.MovieDetails.play4k": "Запустить 4К на {mediaServerName}", + "components.Settings.RadarrModal.tagRequests": "Тег запросов", + "components.Layout.UserWarnings.emailInvalid": "Неверный email адрес.", "components.Settings.SonarrModal.seriesType": "Тип сериала", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук уведомления", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Устройство по умолчанию" + "components.Discover.FilterSlideover.voteCount": "Количество голосов от {minValue} до {maxValue}", + "components.Login.validationEmailRequired": "Вы должны указать адрес электронной почты", + "components.Settings.Notifications.userEmailRequired": "Требуется email пользователя", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Сканировать недавно добавленное в Jellyfin", + "components.Discover.tmdbmoviestreamingservices": "Сервисы потоковой передачи фильмов TMDB", + "components.Discover.tmdbtvstreamingservices": "Сервисы потоковой передачи сериалов TMDB", + "components.Login.description": "Поскольку вы впервые входите в систему {ApplicationName}, вам необходимо добавить адрес электронной почты.", + "components.Settings.Notifications.NotificationsPushover.sound": "Звук уведомлений", + "components.Settings.RadarrModal.tagRequestsInfo": "Автодобавление тега с именем и ID пользователя, отправившего запрос", + "components.Settings.SonarrModal.animeSeriesType": "Тип аниме", + "components.Discover.FilterSlideover.tmdbuservotecount": "Количество голосов от пользователей TMDB", + "components.Login.credentialerror": "Введено неверное имя пользователя или пароль.", + "components.Login.title": "Добавить email", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Это приведет к необратимому удалению {MediaType} из {arr}, включая все файлы.", + "components.Settings.SettingsAbout.supportjellyseerr": "Поддержать Jellyseerr", + "components.Settings.SonarrModal.tagRequests": "Тег запросов", + "components.MovieDetails.downloadstatus": "Статус загрузки", + "components.MovieDetails.openradarr4k": "Открыть фильм в 4К Radarr", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Устройство по умолчанию", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Сканировать всю библиотеку Jellyfin", + "components.Settings.SettingsJobsCache.availability-sync": "Синхронизировать доступность медиа", + "components.Settings.jellyfinSettingsFailure": "Что-то пошло не так во время сохранения настроек {mediaServerName}.", + "components.Settings.jellyfinSettingsSuccess": "Настройки {mediaServerName} успешно сохранены!", + "components.Settings.jellyfinlibraries": "Библиотеки {mediaServerName}", + "components.Settings.jellyfinlibrariesDescription": "Библиотеки {mediaServerName} проверяются на наличие заголовков. Нажмите кнопку ниже, если в списке не хватает библиотек.", + "components.Settings.jellyfinsettings": "Настройки {mediaServerName}", + "components.Settings.internalUrl": "Внутренний URL-адрес", + "components.Settings.SonarrModal.tagRequestsInfo": "Автодобавление тега с именем и ID пользователя, отправившего запрос", + "components.Settings.jellyfinSettings": "Настройки {mediaServerName}", + "components.Settings.jellyfinSettingsDescription": "Необязательно настраивать внутреннюю и внешнюю конечные точки для вашего сервера {mediaServerName}. В большинстве случаев внешний URL-адрес отличается от внутреннего. Пользовательский URL-адрес для сброса пароля также может быть задан для входа в систему {mediaServerName}, на случай, если вы хотите перенаправить на другую страницу для сброса пароля.", + "components.Settings.jellyfinsettingsDescription": "Настройте свой {mediaServerName} сервер. {mediaServerName} отсканирует ваши библиотеки, чтобы увидеть, какие библиотеки доступны.", + "components.Settings.manualscanJellyfin": "Сканировать библиотеки вручную", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.syncJellyfin": "Синхронизировать библиотеки", + "components.Settings.syncing": "Синхронизация", + "components.Settings.timeout": "Время ожидания", + "components.Setup.signin": "Войти", + "components.Setup.signinWithPlex": "Используйте свой Plex аккаунт", + "components.TitleCard.watchlistDeleted": "{title} успешно удален из списка наблюдения!", + "components.TitleCard.watchlistError": "Что-то пошло не так, попробуйте еще раз.", + "components.TvDetails.play": "Запустить в {mediaServerName}", + "components.TvDetails.play4k": "Запустить 4К в {mediaServerName}", + "components.UserList.importedfromJellyfin": "{userCount} {userCount, plural, one {# новый пользователь} other {# новых пользователя(ей)}} успешно импортированы из {mediaServerName}!", + "components.UserList.importfromJellyfin": "Добавить пользователей из {mediaServerName}", + "components.UserList.noJellyfinuserstoimport": "Нет пользователей {mediaServerName} для импорта.", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "Пользователь {mediaServerName}", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Сохранение…", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук уведомлений", + "i18n.collection": "Коллекция", + "components.Setup.configuremediaserver": "Настройте медиасервер", + "components.UserList.importfromJellyfinerror": "Что-то пошло не так при импорте пользователей из {mediaServerName}.", + "components.UserList.newJellyfinsigninenabled": "Параметр Включить новый вход в {mediaServerName} в настоящее время включен. Пользователей {mediaServerName} с доступом к библиотеке не нужно импортировать для входа.", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "Электронная почта", + "components.UserProfile.localWatchlist": "Список наблюдения {username}", + "components.Settings.manualscanDescriptionJellyfin": "Обычно это выполняется только раз в 24 часа. Jellyseerr будет более настойчиво проверять недавно добавленный сервер {mediaServerName}. Если вы впервые настраиваете Jellyseerr, то рекомендуем однократное полное сканирование библиотеки!", + "components.TitleCard.watchlistCancel": "наблюдение за {title} отменено.", + "components.Settings.saving": "Сохранение…", + "components.TitleCard.addToWatchList": "Добавить в список наблюдения", + "components.TitleCard.watchlistSuccess": "{title} успешно добавлен в список наблюдения!", + "components.Settings.save": "Сохранить изменения", + "components.Setup.signinWithJellyfin": "Используйте свой {mediaServerName} аккаунт", + "components.UserList.mediaServerUser": "Пользователь {mediaServerName}", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Сохранить изменения", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Устройство по умолчанию" } diff --git a/src/i18n/locale/sl.json b/src/i18n/locale/sl.json new file mode 100644 index 00000000..f4da326f --- /dev/null +++ b/src/i18n/locale/sl.json @@ -0,0 +1,59 @@ +{ + "components.Discover.CreateSlider.editsuccess": "Urejen drsnik in shranjene nastavitve prilagajanja odkrivanja.", + "components.CollectionDetails.numberofmovies": "{count} film/ov", + "components.Discover.CreateSlider.slidernameplaceholder": "Ime drsnika", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Premiera ↓", + "components.AppDataWarning.dockerVolumeMissingDescription": "Pripenjanje nosilca {appDataPath} ni bilo pravilno konfigurirano. Vsi podatki bodo izbrisani, ko se vsebnik zaustavi ali znova zažene.", + "components.Discover.DiscoverMovies.sortPopularityDesc": "Priljubljenost ↑", + "components.AirDateBadge.airsrelative": "Predvajanje {relativeTime}", + "components.CollectionDetails.overview": "Pregled", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Active Filter} drugo {# Active Filters}}", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Ocena TMDB ↓", + "components.AirDateBadge.airedrelative": "Predvajano {relativeTime}", + "components.Discover.CreateSlider.searchStudios": "Iskanje studiev …", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Datum izdaje ↑", + "components.Discover.CreateSlider.providetmdbnetwork": "Navedite ID omrežja TMDB", + "components.Discover.CreateSlider.addfail": "Novega drsnika ni bilo mogoče ustvariti.", + "components.CollectionDetails.requestcollection": "Zahtevaj zbirko", + "components.Discover.DiscoverMovieGenre.genreMovies": "Filmi: {genre}", + "components.Discover.DiscoverMovieLanguage.languageMovies": "Filmi: {language}", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Active Filter} other {# Active Filters}}", + "components.Discover.DiscoverMovies.sortPopularityAsc": "Priljubljenost ↓", + "components.Discover.CreateSlider.needresults": "Imeti morate vsaj 1 rezultat.", + "components.Discover.CreateSlider.addcustomslider": "Ustvari drsnik po meri", + "components.Discover.DiscoverTv.sortPopularityAsc": "Priljubljenost ↓", + "components.Discover.CreateSlider.editSlider": "Uredi drsnik", + "components.Discover.DiscoverTv.sortTitleAsc": "Naslov (a-ž) ↓", + "components.Discover.CreateSlider.validationDatarequired": "Navesti morate vrednost podatkov.", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Premiera ↑", + "components.Discover.DiscoverTv.discovertv": "Serije", + "components.Discover.DiscoverSliderEdit.deletefail": "Drsnika ni bilo mogoče izbrisati.", + "components.Discover.CreateSlider.providetmdbstudio": "Navedite ID studia v TMDB", + "components.Discover.DiscoverMovies.sortTitleDesc": "Naslov (a-ž) ↑", + "components.Discover.DiscoverStudio.studioMovies": "{studio} filmi", + "components.Discover.DiscoverTv.sortPopularityDesc": "Priljubljenost ↑", + "components.Discover.CreateSlider.searchGenres": "Išči žanre …", + "components.Discover.CreateSlider.editfail": "Drsnika ni bilo mogoče urediti.", + "components.Discover.CreateSlider.starttyping": "Tipkajte za iskanje.", + "components.Discover.DiscoverSliderEdit.enable": "Preklopi vidnost", + "components.Discover.CreateSlider.addSlider": "Dodaj drsnik", + "components.CollectionDetails.requestcollection4k": "Zahtevaj zbirko 4K", + "components.Discover.CreateSlider.providetmdbsearch": "Vnesite iskalno poizvedbo", + "components.Discover.DiscoverNetwork.networkSeries": "{network} serije", + "components.Discover.CreateSlider.providetmdbkeywordid": "Navedite ID ključne besede TMDB", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "Filmi: {keywordTitle}", + "components.Discover.CreateSlider.validationTitlerequired": "Navesti morate naslov.", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Datum izdaje ↓", + "components.Discover.CreateSlider.nooptions": "Ni zadetkov.", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Ocena TMDB ↑", + "components.Discover.CreateSlider.searchKeywords": "Iskanje po ključnih besedah …", + "components.Discover.CreateSlider.addsuccess": "Ustvarjen nov drsnik in shranjene nastavitve prilagajanja odkrivanja.", + "components.Discover.DiscoverSliderEdit.deletesuccess": "Drsnik je bil uspešno izbrisan.", + "components.Discover.DiscoverMovies.discovermovies": "Filmi", + "components.Discover.DiscoverMovies.sortTitleAsc": "Naslov (a-ž) ↓", + "components.Discover.CreateSlider.providetmdbgenreid": "Navedite ID žanra TMDB", + "components.Discover.DiscoverTv.sortTitleDesc": "Naslov (a-ž) ↑", + "components.Discover.DiscoverSliderEdit.remove": "Odstrani", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "Ocena TMDB ↓", + "components.Discover.DiscoverTvGenre.genreSeries": "{genre} Series" +} diff --git a/src/i18n/locale/sv.json b/src/i18n/locale/sv.json index 6d39a549..17c309a0 100644 --- a/src/i18n/locale/sv.json +++ b/src/i18n/locale/sv.json @@ -2,9 +2,9 @@ "components.Setup.finishing": "Slutför…", "components.Setup.finish": "Slutför konfiguration", "components.Setup.continue": "Fortsätt", - "components.Setup.configureservices": "Konfigurera tjänster", + "components.Setup.configureservices": "Konfigurera Tjänster", "components.Setup.configureplex": "Konfigurera Plex", - "components.Settings.validationPortRequired": "Du måste ange ett giltigt portnummer", + "components.Settings.validationPortRequired": "Du måste ange ett giltigt port nummer", "components.Settings.validationHostnameRequired": "Du måste ange giltigt värdnamn eller IP-adress", "components.Settings.toastSettingsSuccess": "Inställningar sparade!", "components.Settings.toastSettingsFailure": "Något gick fel när inställningarna skulle sparas.", @@ -12,8 +12,8 @@ "components.Settings.toastApiKeyFailure": "Något gick fel vid skapandet av ny API-nyckel.", "components.Settings.startscan": "Starta scanning", "components.Settings.ssl": "SSL", - "components.Settings.sonarrsettings": "Sonarr-inställningar", - "components.Settings.radarrsettings": "Radarr-inställningar", + "components.Settings.sonarrsettings": "Sonarrinställningar", + "components.Settings.radarrsettings": "Radarrinställningar", "components.Settings.port": "Port", "components.Settings.plexsettingsDescription": "Konfigurera inställningarna för din Plex-server. Jellyseerr scannar din Plex-server för att avgöra om innehållet är tillgängligt.", "components.Settings.plexsettings": "Plexinställningar", @@ -25,8 +25,8 @@ "components.Settings.menuPlexSettings": "Plex", "components.Settings.menuNotifications": "Notifikationer", "components.Settings.menuLogs": "Loggar", - "components.Settings.menuJobs": "Jobb & cache", - "components.Settings.menuGeneralSettings": "Allmänt", + "components.Settings.menuJobs": "Jobb & Cache", + "components.Settings.menuGeneralSettings": "Allmänna", "components.Settings.menuAbout": "Om", "components.Settings.manualscanDescription": "Normalt körs denna var tjugofjärde timme. Jellyseerr kommer kontrollera till Plex Servers \"senast tillagda\" oftare. Om detta är första gången du konfigurerar din Plex Server är det starkt rekommenderat att göra en manuell full scan av ditt mediabibliotek!", "components.Settings.manualscan": "Manuell Biblioteksscan", @@ -44,19 +44,19 @@ "components.Settings.apikey": "API-nyckel", "components.Settings.addsonarr": "Lägg till Sonarr Server", "components.Settings.address": "Adress", - "components.Settings.addradarr": "Lägg till Radarr-server", - "components.Settings.activeProfile": "Aktiv profil", + "components.Settings.addradarr": "Lägg till Radarr Server", + "components.Settings.activeProfile": "Aktiv Profil", "components.Settings.SonarrModal.validationRootFolderRequired": "Du måste ange en root-mapp", "components.Settings.SonarrModal.validationProfileRequired": "Du måste ange en kvalitetsprofil", - "components.Settings.SonarrModal.validationPortRequired": "Du måste ange ett giltigt portnummer", + "components.Settings.SonarrModal.validationPortRequired": "Du måste ange ett giltigt port nummer", "components.Settings.SonarrModal.validationNameRequired": "Du måste ange ett servernamn", "components.Settings.SonarrModal.validationHostnameRequired": "Du måste ange giltigt värdnamn eller IP-adress", "components.Settings.SonarrModal.validationApiKeyRequired": "Du måste ange en API-nyckel", - "components.Settings.SonarrModal.testFirstRootFolders": "Testa anslutningen för att ladda root-mapparna", - "components.Settings.SonarrModal.testFirstQualityProfiles": "Testa anslutningen för att ladda kvalitetsprofilerna", + "components.Settings.SonarrModal.testFirstRootFolders": "Testa anslutningen för att ladda in root-mapparna", + "components.Settings.SonarrModal.testFirstQualityProfiles": "Testa anslutningen för att ladda in kvalitetsprofilerna", "components.Settings.SonarrModal.ssl": "Använd SSL", "components.Settings.SonarrModal.servername": "Servernamn", - "components.Settings.SonarrModal.server4k": "4K-server", + "components.Settings.SonarrModal.server4k": "4K Server", "components.Settings.SonarrModal.selectRootFolder": "Välj root-mapp", "components.Settings.SonarrModal.selectQualityProfile": "Välj kvalitetsprofil", "components.Settings.SonarrModal.seasonfolders": "Säsongsmappar", @@ -71,9 +71,9 @@ "components.Settings.SonarrModal.createsonarr": "Lägg till en ny Sonarr Server", "components.Settings.SonarrModal.baseUrl": "URL-bas", "components.Settings.SonarrModal.apiKey": "API-nyckel", - "components.Settings.SonarrModal.animerootfolder": "Anime root-mapp", - "components.Settings.SonarrModal.animequalityprofile": "Anime-kvalitetsprofil", - "components.Settings.SonarrModal.add": "Lägg till server", + "components.Settings.SonarrModal.animerootfolder": "Anime Root-mapp", + "components.Settings.SonarrModal.animequalityprofile": "Anime Kvalitetsprofil", + "components.Settings.SonarrModal.add": "Lägg till Server", "components.Settings.SettingsAbout.version": "Version", "components.Settings.SettingsAbout.totalrequests": "Totalt antal förfrågningar", "components.Settings.SettingsAbout.totalmedia": "Totalt antal mediaobjekt", @@ -84,72 +84,72 @@ "components.Settings.RadarrModal.validationProfileRequired": "Du måste ange en kvalitetsprofil", "components.Settings.RadarrModal.validationPortRequired": "Du måste ange en giltig port nummer", "components.Settings.RadarrModal.validationNameRequired": "Du måste ange ett servernamn", - "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Du måste välja lägsta tillgänglighet", - "components.Settings.RadarrModal.validationHostnameRequired": "Du måste ange ett giltigt värdnamn eller IP-adress", + "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Du måste välja minsta tillgänglighet", + "components.Settings.RadarrModal.validationHostnameRequired": "Du måste ange giltigt hostname eller IP-adress", "components.Settings.RadarrModal.validationApiKeyRequired": "Du måste ange en API-nyckel", "components.Settings.RadarrModal.toastRadarrTestSuccess": "Anslutningen till Radarr lyckades!", "components.Settings.RadarrModal.toastRadarrTestFailure": "Misslyckades att ansluta till Radarr.", - "components.Settings.RadarrModal.testFirstRootFolders": "Testa anslutningen för att ladda root-mapparna", - "components.Settings.RadarrModal.testFirstQualityProfiles": "Testa anslutningen för att ladda kvalitetsprofiler", + "components.Settings.RadarrModal.testFirstRootFolders": "Testa anslutningen för att ladda in root-mapparna", + "components.Settings.RadarrModal.testFirstQualityProfiles": "Testa anslutningen för att ladda in kvalitetsprofiler", "components.Settings.RadarrModal.ssl": "Använd SSL", "components.Settings.RadarrModal.servername": "Servernamn", - "components.Settings.RadarrModal.server4k": "4K-server", + "components.Settings.RadarrModal.server4k": "4K Server", "components.Settings.RadarrModal.selectRootFolder": "Välj root-mapp", "components.Settings.RadarrModal.selectQualityProfile": "Välj kvalitetsprofil", - "components.Settings.RadarrModal.selectMinimumAvailability": "Ange lägsta tillgänglighet", + "components.Settings.RadarrModal.selectMinimumAvailability": "Ange minsta tillgänglighet", "components.Settings.RadarrModal.rootfolder": "Root-mapp", "components.Settings.RadarrModal.qualityprofile": "Kvalitetsprofiler", "components.Settings.RadarrModal.port": "Port", - "components.Settings.RadarrModal.minimumAvailability": "Lägsta tillgänglighet", + "components.Settings.RadarrModal.minimumAvailability": "Minsta tillgänglighet", "components.Settings.RadarrModal.loadingrootfolders": "Laddar root-mappar…", "components.Settings.RadarrModal.loadingprofiles": "Laddar kvalitetsprofiler…", - "components.Settings.RadarrModal.hostname": "Värdnamn eller IP-adress", + "components.Settings.RadarrModal.hostname": "Hostname eller IP-adress", "components.Settings.RadarrModal.editradarr": "Ändra Radarr Server", "components.Settings.RadarrModal.defaultserver": "Standard Server", "components.Settings.RadarrModal.createradarr": "Lägg till en ny Radarr Server", "components.Settings.RadarrModal.baseUrl": "URL-bas", - "components.Settings.RadarrModal.apiKey": "API-nyckel", - "components.Settings.RadarrModal.add": "Lägg till server", + "components.Settings.RadarrModal.apiKey": "API-Nyckel", + "components.Settings.RadarrModal.add": "Lägg till Server", "components.Settings.Notifications.webhookUrl": "Webhook-URL", "components.Settings.Notifications.validationSmtpPortRequired": "Du måste ange ett giltigt port nummer", "components.Settings.Notifications.validationSmtpHostRequired": "Du måste ange ett giltigt värdnamn eller IP-adress", - "components.Settings.Notifications.smtpPort": "SMTP port", - "components.Settings.Notifications.smtpHost": "SMTP-server", - "components.Settings.Notifications.emailsettingssaved": "Notifikationsinställningar för e-post sparade!", + "components.Settings.Notifications.smtpPort": "SMTP Port", + "components.Settings.Notifications.smtpHost": "SMTP Server", + "components.Settings.Notifications.emailsettingssaved": "Notiferingsinställningar för e-post sparade!", "components.Settings.Notifications.discordsettingsfailed": "Notifieringsinställningar för Discord kunde inte sparas.", "components.Settings.Notifications.emailsettingsfailed": "Notifieringsinställningar för e-post kunde inte sparas.", "components.Settings.Notifications.emailsender": "Avsändaradress", - "components.Settings.Notifications.discordsettingssaved": "Notifikationsinställningar för Discord sparade!", - "components.Settings.Notifications.authUser": "SMTP-användarnamn", - "components.Settings.Notifications.authPass": "SMTP-lösenord", + "components.Settings.Notifications.discordsettingssaved": "Notiferingsinställningar för Discord sparade!", + "components.Settings.Notifications.authUser": "SMTP Användarnamn", + "components.Settings.Notifications.authPass": "SMTP Lösenord", "components.Settings.Notifications.agentenabled": "Aktivera agenten", "components.Search.searchresults": "Sökresultat", "components.RequestModal.selectseason": "Välj säsong(er)", "components.RequestModal.seasonnumber": "Säsong {number}", "components.RequestModal.season": "Säsong", - "components.RequestModal.requestfrom": "{username}s begäran väntar på godkännande.", - "components.RequestModal.requestadmin": "Förfrågan kommer godkännas automatiskt.", + "components.RequestModal.requestfrom": "{username}'s begäran väntar på godkännande.", + "components.RequestModal.requestadmin": "Förfrågan kommer bli godkännas automatiskt.", "components.RequestModal.requestSuccess": "{title} begärd!", "components.RequestModal.requestCancel": "Förfrågan för {title} avbruten.", - "components.RequestModal.pendingrequest": "Väntande förfrågan", - "components.RequestModal.numberofepisodes": "Antal avsnitt", + "components.RequestModal.pendingrequest": "Väntande Förfrågan", + "components.RequestModal.numberofepisodes": "Antal Avsnitt", "components.RequestModal.extras": "Extramaterial", - "components.RequestModal.cancel": "Ta bort begäran", + "components.RequestModal.cancel": "Avbryt förfrågan", "components.RequestList.requests": "Förfrågningar", - "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {säsong} other {säsonger}}", - "components.RequestCard.seasons": "{seasonCount, plural, one {säsong} other {säsonger}}", - "components.RequestBlock.seasons": "{seasonCount, plural, one {säsong} other {säsonger}}", + "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Säsong} other {Säsonger}}", + "components.RequestCard.seasons": "{seasonCount, plural, one {Säsong} other {Säsonger}}", + "components.RequestBlock.seasons": "{seasonCount, plural, one {Säsong} other {Säsonger}}", "components.PersonDetails.ascharacter": "som {character}", "components.PersonDetails.appearsin": "Kan ses i", - "components.MovieDetails.studio": "{studioCount, plural, one {studio} other {studior}}", - "components.MovieDetails.similar": "Liknande titlar", + "components.MovieDetails.studio": "{studioCount, plural, one {Studio} other {Studior}}", + "components.MovieDetails.similar": "Liknande Titlar", "components.MovieDetails.runtime": "{minutes} minuter", - "components.MovieDetails.revenue": "Intäkter", + "components.MovieDetails.revenue": "Inkomster", "components.MovieDetails.releasedate": "{releaseCount, plural, one {Utgivningsdatum} other {Utgivningsdatum}}", "components.MovieDetails.recommendations": "Rekommendationer", - "components.MovieDetails.overview": "Översikt", + "components.MovieDetails.overview": "Beskrivning", "components.MovieDetails.overviewunavailable": "Beskrivning otillgänglig.", - "components.MovieDetails.originallanguage": "Ursprungligt språk", + "components.MovieDetails.originallanguage": "Ursprungligt Språk", "components.MovieDetails.cast": "Roller", "components.MovieDetails.budget": "Budget", "components.MovieDetails.MovieCast.fullcast": "Rollista", @@ -158,7 +158,7 @@ "components.Layout.Sidebar.settings": "Inställningar", "components.Layout.Sidebar.requests": "Förfrågningar", "components.Layout.Sidebar.dashboard": "Upptäck", - "components.Layout.SearchInput.searchPlaceholder": "Sök filmer & TV-serier", + "components.Layout.SearchInput.searchPlaceholder": "Sök Filmer & TV-Serier", "components.Discover.upcomingmovies": "Kommande filmer", "components.Discover.upcoming": "Kommande filmer", "components.Discover.trending": "Trendande", @@ -173,19 +173,19 @@ "components.Setup.welcome": "Välkommen till Jellyseerr", "components.Setup.tip": "Tips", "components.Setup.signinMessage": "Kom igång genom att logga in med ditt Plex-konto", - "components.RequestModal.requestseasons": "Begär {seasonCount} {seasonCount, plural, one {säsong} other {säsonger}}", + "components.RequestModal.requestseasons": "Förfrågan {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}", "pages.returnHome": "Gå tillbaka till startsidan", - "pages.oops": "Hoppsan", + "pages.oops": "Ojsan", "i18n.unavailable": "Otillgänglig", - "i18n.tvshows": "Serier", + "i18n.tvshows": "TV-serier", "i18n.processing": "Behandlar", "i18n.pending": "Väntande", - "i18n.partiallyavailable": "Delvis tillgänglig", + "i18n.partiallyavailable": "Delvis Tillgänglig", "i18n.movies": "Filmer", "i18n.deleting": "Tar bort…", "i18n.delete": "Ta bort", - "i18n.declined": "Nekad", - "i18n.decline": "Neka", + "i18n.declined": "Avböjd", + "i18n.decline": "Avböj", "i18n.cancel": "Avbryt", "i18n.available": "Tillgänglig", "i18n.approved": "Godkänd", @@ -196,18 +196,18 @@ "components.UserList.user": "Användare", "components.UserList.totalrequests": "Förfrågningar", "components.UserList.role": "Roll", - "components.UserList.plexuser": "Plex-användare", + "components.UserList.plexuser": "Plex Användare", "components.UserList.deleteuser": "Ta bort användare", "components.UserList.deleteconfirm": "Är du säker på att du vill ta bort den här användaren? Alla förfrågningsdata tas bort permanent.", - "components.UserList.created": "Gick med", + "components.UserList.created": "Anslöt sig", "components.UserList.admin": "Administratör", "components.TvDetails.similar": "Liknande TV-serier", "components.TvDetails.showtype": "Serietyp", "components.TvDetails.recommendations": "Rekommendationer", "components.TvDetails.overviewunavailable": "Beskrivning otillgänglig.", "components.TvDetails.overview": "Beskrivning", - "components.TvDetails.originallanguage": "Ursprungligt språk", - "components.TvDetails.network": "{networkCount, plural, one {nätverk} other {nätverk}}", + "components.TvDetails.originallanguage": "Ursprungligt Språk", + "components.TvDetails.network": "{networkCount, plural, one {Nätverk} other {Nätverk}}", "components.TvDetails.cast": "Roller", "i18n.close": "Stäng", "components.Setup.loginwithplex": "Logga in med Plex", @@ -219,7 +219,7 @@ "components.Settings.SettingsAbout.Releases.versionChangelog": "{version} Ändringslogg", "components.Settings.SettingsAbout.Releases.releases": "Versioner", "components.Settings.SettingsAbout.Releases.releasedataMissing": "Versionsdata är för närvarande inte tillgänglig.", - "components.Settings.SettingsAbout.Releases.latestversion": "Senaste versionen", + "components.Settings.SettingsAbout.Releases.latestversion": "Senaste Versionen", "components.Settings.SettingsAbout.Releases.currentversion": "Aktuell", "components.UserList.importfrommediaserver": "Importera {mediaServerName}användare", "components.UserList.importfromplex": "Importera Plexanvändare", @@ -230,16 +230,16 @@ "components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "Notiferingsinställningar för Slack sparade!", "components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": "Notifieringsinställningar för Slack kunde inte sparas.", "components.Settings.Notifications.NotificationsSlack.agentenabled": "Aktiverad", - "components.MovieDetails.watchtrailer": "Se trailer", - "components.CollectionDetails.requestcollection": "Begär samling", + "components.MovieDetails.watchtrailer": "Kolla Trailer", + "components.CollectionDetails.requestcollection": "Begär Kollektion", "components.CollectionDetails.overview": "Översikt", - "components.CollectionDetails.numberofmovies": "{count} filmer", + "components.CollectionDetails.numberofmovies": "Antal Filmer: {count}", "i18n.retry": "Försök igen", "i18n.requested": "Begärd", - "i18n.request": "Begär", + "i18n.request": "Begäran", "i18n.failed": "Misslyckades", - "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {användare} other {användare}} har importerats!", - "components.TvDetails.viewfullcrew": "Visa alla medarbetare", + "components.UserList.importedfromplex": "{userCount} Plex {userCount, plural, one {user} other {users}} importerades framgångsrikt!", + "components.TvDetails.viewfullcrew": "Visa hela rollistan", "components.TvDetails.firstAirDate": "Första sändningsdatum", "components.TvDetails.TvCrew.fullseriescrew": "Hela rollistan", "components.StatusChacker.reloadOverseerr": "Ladda om", @@ -249,21 +249,21 @@ "components.Settings.SettingsAbout.documentation": "Dokumentation", "components.Settings.Notifications.validationChatIdRequired": "Du måste ange ett giltigt chatt-ID", "components.Settings.Notifications.validationBotAPIRequired": "Du måste ange en bot-auktoriseringstoken", - "components.Settings.Notifications.telegramsettingssaved": "Notifikationsinställningar för Telegram sparade!", + "components.Settings.Notifications.telegramsettingssaved": "Notiferingsinställningar för Telegram sparade!", "components.Settings.Notifications.telegramsettingsfailed": "Notifieringsinställningar för Telegram kunde inte sparas.", "components.Settings.Notifications.senderName": "Avsändarens namn", "components.Settings.Notifications.chatId": "Chatt-ID", "components.Settings.Notifications.botAPI": "Bot-auktoriseringstoken", - "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Notifikationsinställningar för webhook sparade!", + "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Notiferingsinställningar för webhook sparade!", "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Notifieringsinställningar för Webhook kunde inte sparas.", "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook-URL", "components.Settings.Notifications.NotificationsWebhook.validationJsonPayloadRequired": "Du måste ange en giltig JSON Payload", "components.Settings.Notifications.NotificationsWebhook.templatevariablehelp": "Lathund för variabler", "components.Settings.Notifications.NotificationsWebhook.resetPayloadSuccess": "JSON har återställts!", - "components.Settings.Notifications.NotificationsWebhook.resetPayload": "Återställ till standard", + "components.Settings.Notifications.NotificationsWebhook.resetPayload": "Återställ till Standard", "components.Settings.Notifications.NotificationsWebhook.customJson": "Ladda JSON", "components.Settings.Notifications.NotificationsWebhook.authheader": "Behörighetsrubrik", - "components.Settings.Notifications.NotificationsWebhook.agentenabled": "Aktivera agent", + "components.Settings.Notifications.NotificationsWebhook.agentenabled": "Aktiverad", "components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "Du måste ange en giltig användare eller gruppnyckel", "components.Settings.Notifications.NotificationsPushover.validationAccessTokenRequired": "Du måste ange en giltig applikationstoken", "components.Settings.Notifications.NotificationsPushover.userToken": "Användar- eller gruppnyckel", @@ -280,14 +280,14 @@ "components.RequestButton.viewrequest": "Visa begäran", "components.RequestButton.requestmore4k": "Begär mer i 4K", "components.RequestButton.requestmore": "Begär mer", - "components.RequestButton.declinerequests": "Neka {requestCount, plural, one {förfrågan} other {{requestCount} förfrågningar}}", - "components.RequestButton.declinerequest4k": "Neka 4K-begäran", - "components.RequestButton.declinerequest": "Neka begäran", - "components.RequestButton.decline4krequests": "Neka {requestCount, plural, one {4K-förfrågan} other {{requestCount} 4K-förfrågningar}}", + "components.RequestButton.declinerequests": "Avvisa {requestCount, plural, one {förfrågan} other {{requestCount} förfrågningar}}", + "components.RequestButton.declinerequest4k": "Avvisa 4K-begäran", + "components.RequestButton.declinerequest": "Neka förfrågan", + "components.RequestButton.decline4krequests": "Avvisa {requestCount, plural, one {4K förfrågan} other {{requestCount} 4K förfrågan}}", "components.RequestButton.approverequests": "Godkänn {requestCount, plural, one {förfrågan} other {{requestCount} förfrågningar}}", "components.RequestButton.approverequest4k": "Godkänn 4K-begäran", "components.RequestButton.approverequest": "Godkänn begäran", - "components.RequestButton.approve4krequests": "Godkänn {requestCount, plural, one {4K-förfrågning} other {{requestCount} 4K-förfrågningar}}", + "components.RequestButton.approve4krequests": "Godkänn {requestCount, plural, one {4K förfrågning} other {{requestCount} 4K förfrågningar}}", "components.NotificationTypeSelector.mediarequestedDescription": "Skicka meddelanden när användare skickar in nya medieförfrågningar som kräver godkännande.", "components.NotificationTypeSelector.mediarequested": "Begäran väntar på godkännande", "components.NotificationTypeSelector.mediafailedDescription": "Skicka meddelanden när medieförfrågningar inte kan läggas till i Radarr eller Sonarr.", @@ -300,22 +300,22 @@ "components.MovieDetails.MovieCrew.fullcrew": "Filmteam", "components.Settings.csrfProtection": "Aktivera CSRF-skydd", "components.UserList.userssaved": "Användarbehörigheter sparade!", - "components.UserList.bulkedit": "Massredigering", + "components.UserList.bulkedit": "Mass-redigering", "components.PermissionEdit.usersDescription": "Bevilja behörighet att hantera användare. Användare med denna behörighet kan inte ändra användare med eller bevilja administratörsbehörighet.", "components.PermissionEdit.users": "Hantera Användare", "components.PermissionEdit.settingsDescription": "Ge tillstånd att ändra globala inställningar. En användare måste ha denna behörighet för att ge den till andra.", "components.PermissionEdit.settings": "Hantera Inställningar", "components.PermissionEdit.requestDescription": "Ge tillstånd att skicka förfrågningar för icke-4K-media.", - "components.PermissionEdit.request4kTvDescription": "Bevilja förfrågningar av 4K-serier.", - "components.PermissionEdit.request4kTv": "Begära 4K-serier", - "components.PermissionEdit.request4kMoviesDescription": "Bevilja förfrågningar av 4K-filmer.", + "components.PermissionEdit.request4kTvDescription": "Bevilja tillstånd att skicka förfrågningar för 4K-serien.", + "components.PermissionEdit.request4kTv": "Begära 4K Serier", + "components.PermissionEdit.request4kMoviesDescription": "Ge tillstånd att skicka in förfrågningar om 4K-filmer.", "components.PermissionEdit.request4kMovies": "Begära 4K Filmer", "components.PermissionEdit.request4kDescription": "Bevilja behörighet att skicka förfrågningar om 4K-media.", "components.PermissionEdit.request4k": "Begära 4K", "components.PermissionEdit.request": "Begära", "components.PermissionEdit.managerequestsDescription": "Bevilja behörighet att hantera medieförfrågningar. Alla förfrågningar som görs av en användare med den här behörigheten kommer att godkännas.", - "components.PermissionEdit.managerequests": "Hantera förfrågningar", - "components.PermissionEdit.adminDescription": "Fullständig administratörsbehörighet. Överskrider alla andra behörigheter.", + "components.PermissionEdit.managerequests": "Hantera Förfrågningar", + "components.PermissionEdit.adminDescription": "Fullständig administratörsbehörighet. Överskrider alla andra behörighetskontroller.", "components.PlexLoginButton.signinwithplex": "Logga in", "components.PlexLoginButton.signingin": "Loggar in…", "components.RequestModal.requesterror": "Någonting gick fel vid behandling av din begäran.", @@ -331,7 +331,7 @@ "components.RequestModal.AdvancedRequester.qualityprofile": "Kvalitetsprofil", "components.RequestModal.AdvancedRequester.destinationserver": "Destinationsserver", "components.RequestModal.AdvancedRequester.default": "{name} (Standard)", - "components.RequestModal.AdvancedRequester.animenote": "*Den här serien är en anime.", + "components.RequestModal.AdvancedRequester.animenote": "* Denna serien är en anime.", "components.RequestModal.AdvancedRequester.advancedoptions": "Avancerat", "components.RequestBlock.server": "Destinationsserver", "components.RequestBlock.rootfolder": "Root-mapp", @@ -340,31 +340,31 @@ "components.UserList.validationpasswordminchars": "Lösenordet är för kort; det behöver innehålla minst 8 bokstäver", "components.UserList.usercreatedsuccess": "Användaren skapad!", "components.UserList.usercreatedfailed": "Någonting gick fel vid skapandet av användaren.", - "components.UserList.passwordinfodescription": "Konfigurera en applikations-URL och aktivera notifikationer via e-post för att tillåta automatisk lösenordsgenerering.", + "components.UserList.passwordinfodescription": "Aktivera e-postmeddelanden för att tillåta automatisk lösenordsgenerering.", "components.UserList.password": "Lösenord", - "components.UserList.localuser": "Lokal användare", + "components.UserList.localuser": "Lokal Användare", "components.UserList.email": "E-postadress", "components.UserList.creating": "Skapar…", "components.UserList.createlocaluser": "Skapa lokal användare", "components.UserList.create": "Skapa", "components.UserList.autogeneratepassword": "Generera lösenord automatiskt", - "components.PersonDetails.crewmember": "Medarbetare", + "components.PersonDetails.crewmember": "Besättningsmedlem", "components.Login.validationemailrequired": "Du måste ange en giltig e-postadress", "components.Login.email": "E-postadress", - "components.PermissionEdit.autoapproveSeriesDescription": "Bevilja icke-4K serieförfrågningar automatiskt.", - "components.PermissionEdit.autoapproveSeries": "Godkänn serier automatiskt", - "components.PermissionEdit.autoapproveMoviesDescription": "Bevilja automatiskt godkännande av icke-4K filmförfrågningar.", - "components.PermissionEdit.autoapproveMovies": "Godkänn filmer automatiskt", + "components.PermissionEdit.autoapproveSeriesDescription": "Bevilja automatiskt godkännande för icke-4K-serieförfrågningar.", + "components.PermissionEdit.autoapproveSeries": "Auto-Godkänn Serier", + "components.PermissionEdit.autoapproveMoviesDescription": "Bevilja automatiskt godkännande för icke-4K-filmförfrågningar.", + "components.PermissionEdit.autoapproveMovies": "Auto-Godkänn Filmer", "components.PermissionEdit.autoapproveDescription": "Bevilja automatiskt godkännande för alla icke-4K-medieförfrågningar.", - "components.PermissionEdit.autoapprove": "Godkänn automatiskt", + "components.PermissionEdit.autoapprove": "Auto-Godkänn", "components.PermissionEdit.advancedrequestDescription": "Ge behörighet att redigera avancerade inställningar vid en begäran.", - "components.PermissionEdit.advancedrequest": "Avancerade förfrågningar", + "components.PermissionEdit.advancedrequest": "Avancerade Förfrågningar", "components.PermissionEdit.admin": "Admin", "components.NotificationTypeSelector.mediadeclinedDescription": "Skicka meddelanden när medieförfrågningar avvisas.", "components.NotificationTypeSelector.mediadeclined": "Begäran nekad", "components.MovieDetails.play4konplex": "Spela upp i 4K på Plex", "components.MovieDetails.playonplex": "Spela upp på Plex", - "components.MovieDetails.markavailable": "Markera som tillgänglig", + "components.MovieDetails.markavailable": "Markera som Tillgänglig", "components.MovieDetails.mark4kavailable": "Markera som tillgänglig i 4K", "components.Login.validationpasswordrequired": "Du måste ange ett lösenord", "components.Login.signinwithplex": "Använd ditt Plex-konto", @@ -374,7 +374,7 @@ "components.Login.signin": "Logga in", "components.Login.password": "Lösenord", "components.Login.loginerror": "Någonting gick fel vid inloggningen.", - "components.Login.forgotpassword": "Glömt lösenordet?", + "components.Login.forgotpassword": "Glömt Lösenordet?", "components.Discover.discover": "Upptäck", "components.AppDataWarning.dockerVolumeMissingDescription": "Monteringen för {appDataPath} volymen är inte konfigurerad ordentligt. All data kommer att rensas när behållaren stoppas eller startas om.", "components.ResetPassword.validationpasswordrequired": "Du måste ange ett lösenord", @@ -388,11 +388,11 @@ "components.ResetPassword.gobacklogin": "Gå tillbaka till Login-sidan", "components.ResetPassword.emailresetlink": "Skicka återställningslänk", "components.ResetPassword.email": "E-postadress", - "components.ResetPassword.confirmpassword": "Bekräfta lösenord", - "components.TvDetails.nextAirDate": "Nästa sändningsdatum", + "components.ResetPassword.confirmpassword": "Bekräfta Lösenord", + "components.TvDetails.nextAirDate": "Nästa släpp", "components.UserList.validationEmail": "Du måste ange en giltig e-postadress", - "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "Bas-URL:n får inte avslutas med ett snedstreck", - "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "Bas-URL:n måste inledas med ett snedstreck", + "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "Bas-URL:n får inte avslutas med ett slash", + "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "Bas-URL:n måste inledas med ett slash", "components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "URL-basen får inte sluta med ett snedstreck", "components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "URL-basen måste ha ett inledande snedstreck", "components.Settings.Notifications.validationEmail": "Du måste ange en giltig e-postadress", @@ -424,8 +424,8 @@ "components.Settings.SettingsJobsCache.jobs": "Jobb", "components.Settings.SettingsJobsCache.jobname": "Jobbnamn", "components.Settings.SettingsJobsCache.jobcancelled": "{jobname} avbruten.", - "components.Settings.SettingsJobsCache.flushcache": "Rensa cache", - "components.Settings.SettingsJobsCache.canceljob": "Avbryt jobb", + "components.Settings.SettingsJobsCache.flushcache": "Rensa Cache", + "components.Settings.SettingsJobsCache.canceljob": "Avbryt Jobb", "components.Settings.SettingsJobsCache.cachevsize": "Storleksvärde", "components.Settings.SettingsJobsCache.cachename": "Cache-namn", "components.Settings.SettingsJobsCache.cachemisses": "Missar", @@ -439,22 +439,22 @@ "components.Settings.trustProxy": "Aktivera Proxy-stöd", "components.TvDetails.playonplex": "Spela upp på Plex", "components.TvDetails.play4konplex": "Spela upp i 4K på Plex", - "components.Settings.SonarrModal.syncEnabled": "Aktivera genomsökning", + "components.Settings.SonarrModal.syncEnabled": "Aktivera skanning", "components.Settings.SonarrModal.externalUrl": "Extern URL", "components.Settings.RadarrModal.syncEnabled": "Aktivera skanning", "components.Settings.RadarrModal.externalUrl": "Extern URL", - "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr-anslutning upprättad!", - "components.Settings.SonarrModal.toastSonarrTestFailure": "Kunde inte ansluta till Sonarr.", + "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr-anslutning etablerad!", + "components.Settings.SonarrModal.toastSonarrTestFailure": "Misslyckades att ansluta till Sonarr.", "components.Settings.toastPlexRefreshSuccess": "Hämtning av serverlistan från Plex lyckades!", - "components.Settings.toastPlexRefreshFailure": "Kunde inte hämta serverlistan från Plex.", + "components.Settings.toastPlexRefreshFailure": "Misslyckades att hämta serverlistan från Plex.", "components.Settings.toastPlexRefresh": "Hämtar serverlistan från Plex…", - "components.Settings.toastPlexConnectingSuccess": "Anslutning till Plex lyckades!", - "components.Settings.toastPlexConnectingFailure": "Kunde inte ansluta till Plex.", + "components.Settings.toastPlexConnectingSuccess": "Anslutnignen till Plex lyckades!", + "components.Settings.toastPlexConnectingFailure": "Misslyckades med att ansluta till Plex.", "components.Settings.toastPlexConnecting": "Försöker ansluta till Plex…", - "components.Settings.settingUpPlexDescription": "För att konfigurera Plex kan du antingen ställa in inställningarna manuellt eller välja en server som hämtats via plex.tv. Tryck på knappen till höger om rullgardinsmenyn för att hämta listan över tillgängliga servrar.", + "components.Settings.settingUpPlexDescription": "För att konfigurerar Plex, kan du antingen ställa in inställningarna manuellt eller välja en server som hämtats via plex.tv. Tryck på knappen till höger om rullgardinsmenyn för att hämta listan över tillgängliga servrar.", "components.Settings.serverpresetRefreshing": "Hämtar servrar…", "components.Settings.serverpresetManualMessage": "Manuell konfiguration", - "components.Settings.serverpresetLoad": "Klicka på knappen för att hämta tillgängliga servrar", + "components.Settings.serverpresetLoad": "Klicka på knappen för att ladda in tillgängliga servrar", "components.Settings.serverpreset": "Server", "components.Settings.serverRemote": "fjärr", "components.Settings.serverLocal": "lokal", @@ -462,19 +462,19 @@ "i18n.loading": "Laddar…", "components.UserProfile.recentrequests": "Senaste förfrågningar", "components.UserProfile.UserSettings.menuPermissions": "Behörigheter", - "components.UserProfile.UserSettings.menuNotifications": "Notifikationer", - "components.UserProfile.UserSettings.menuGeneralSettings": "Allmänt", + "components.UserProfile.UserSettings.menuNotifications": "Meddelanden", + "components.UserProfile.UserSettings.menuGeneralSettings": "Allmänna", "components.UserProfile.UserSettings.menuChangePass": "Lösenord", "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "Behörigheterna har sparats!", "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Något gick fel när inställningarna sparades.", "components.UserProfile.UserSettings.UserPermissions.permissions": "Behörigheter", - "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Lösenordet är för kort; måste vara minst 8 tecken långt", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Lösenordet är för kort; den ska vara minst 8 tecken lång", "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "Du måste ange ett nytt lösenord", "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "Du måste ange ditt nuvarande lösenord", "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Lösenorden måste matcha", "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "Du måste bekräfta det nya lösenordet", "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Lösenordet har ändrats!", - "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Något gick fel när lösenordet sparades.", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Något gick fel när du sparade lösenordet.", "components.UserProfile.UserSettings.UserPasswordChange.password": "Lösenord", "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "Du har inte behörighet att ändra användarens lösenord.", "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "Nytt lösenord", @@ -484,21 +484,21 @@ "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Du måste ange ett giltigt användar-ID", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Starta en chatt, lägg till @get_id_bot och använd kommandot /my_id ", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Chatt-ID", - "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Skicka notifikationer utan ljud", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Skicka meddelanden utan ljud", "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Skicka tyst", - "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Notifikationsinställningar", + "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Aviseringsinställningar", "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Det flersiffriga ID-numret som är kopplat till ditt användarkonto", "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "Användar-ID", "components.UserProfile.UserSettings.UserGeneralSettings.user": "Användare", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Inställningarna har sparats!", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Något gick fel när inställningarna sparades.", "components.UserProfile.UserSettings.UserGeneralSettings.role": "Roll", - "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filtrera innehåll efter regional tillgänglighet", - "components.UserProfile.UserSettings.UserGeneralSettings.region": "Region för upptäck-fliken", + "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Filtrera innehåll efter region tillgänglighet", + "components.UserProfile.UserSettings.UserGeneralSettings.region": "Upptäck region", "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex-användare", "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Ägare", "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "Filtrera innehåll efter originalspråk", - "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Språk för upptäck-fliken", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Upptäck språk", "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Lokala användare", "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "Allmänna Inställningar", "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Visningsnamn", @@ -511,12 +511,12 @@ "components.UserList.userfail": "Något gick fel när användarbehörigheter sparades.", "components.UserList.sortRequests": "Antal förfrågningar", "components.UserList.sortDisplayName": "Visningsnamn", - "components.UserList.sortCreated": "Registreringsdatum", + "components.UserList.sortCreated": "Datum för anslutning", "components.UserList.owner": "Ägare", - "components.UserList.edituser": "Redigera användarbehörigheter", + "components.UserList.edituser": "Redigera användarebehörigheter", "components.UserList.accounttype": "Typ", - "components.TvDetails.seasons": "{seasonCount, plural, one {# säsong} other {# säsonger}}", - "components.Setup.scanbackground": "Genomsökning körs i bakgrunden. Du kan fortsätta installationsprocessen under tiden.", + "components.TvDetails.seasons": "{seasonCount, plural, one {# Säsong} other {# Säsonger}}", + "components.Setup.scanbackground": "Skanning körs i bakgrunden. Du kan fortsätta installationsprocessen under tiden.", "components.Settings.webhook": "Webhook", "components.Settings.scanning": "Synkar…", "components.Settings.scan": "Skanna bibliotek", @@ -531,8 +531,8 @@ "components.Settings.SonarrModal.testFirstLanguageProfiles": "Testa anslutningen för att ladda språkprofiler", "components.Settings.SonarrModal.selectLanguageProfile": "Välj spåkprofil", "components.Settings.SonarrModal.loadinglanguageprofiles": "Laddar språkprofiler…", - "components.Settings.SonarrModal.languageprofile": "Språkprofil", - "components.Settings.SonarrModal.animelanguageprofile": "Anime-språkprofil", + "components.Settings.SonarrModal.languageprofile": "Spårkprofil", + "components.Settings.SonarrModal.animelanguageprofile": "Anime språkprofil", "components.Settings.SettingsUsers.userSettingsDescription": "Konfigurera globala och standardinställningar för användare.", "components.Settings.SettingsUsers.userSettings": "Anvädnarinställningar", "components.Settings.SettingsUsers.toastSettingsSuccess": "Användarinställningar sparade!", @@ -562,13 +562,13 @@ "components.RequestModal.AdvancedRequester.languageprofile": "Språkprofil", "components.RequestList.RequestItem.requested": "Begärd", "components.RequestList.RequestItem.modifieduserdate": "{date} av{user}", - "components.RequestList.RequestItem.modified": "Ändrad", + "components.RequestList.RequestItem.modified": "Modifierad", "components.RequestBlock.requestoverrides": "Begär åsidosättningar", "components.RegionSelector.regionServerDefault": "Standard ({region})", - "components.RegionSelector.regionDefault": "Alla regioner", - "components.PermissionEdit.autoapprove4kSeriesDescription": "Bevilja 4K serieförfrågningar automatiskt.", + "components.RegionSelector.regionDefault": "Alla Regioner", + "components.PermissionEdit.autoapprove4kSeriesDescription": "Bevilja automatiskt godkännande för 4K-serieförfrågningar.", "components.PermissionEdit.autoapprove4kSeries": "Godkänn automatiskt 4K-serier", - "components.PermissionEdit.autoapprove4kMoviesDescription": "Bevilja automatiskt godkännande av 4K-filmförfrågningar.", + "components.PermissionEdit.autoapprove4kMoviesDescription": "Bevilja automatiskt godkännande för 4K-filmförfrågningar.", "components.PermissionEdit.autoapprove4kMovies": "Godkänn automatiskt 4K-filmer", "components.PermissionEdit.autoapprove4kDescription": "Bevilja automatiskt godkännande för alla 4K-medieförfrågningar.", "components.PermissionEdit.autoapprove4k": "Automatiskt godkännande av 4K", @@ -577,15 +577,15 @@ "components.Layout.UserDropdown.settings": "Inställningar", "components.Layout.UserDropdown.myprofile": "Profil", "components.Discover.upcomingtv": "Kommande serier", - "components.Discover.StudioSlider.studios": "Studior", + "components.Discover.StudioSlider.studios": "Studio", "components.Discover.NetworkSlider.networks": "Nätverk", - "components.Discover.DiscoverTvLanguage.languageSeries": "{language} serier", - "components.Discover.DiscoverTvGenre.genreSeries": "{genre}serier", - "components.Discover.DiscoverStudio.studioMovies": "{studio} filmer", - "components.Discover.DiscoverNetwork.networkSeries": "{network}-serier", - "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} filmer", - "components.Discover.DiscoverMovieGenre.genreMovies": "{genre}filmer", - "components.CollectionDetails.requestcollection4k": "Begär samling i 4K", + "components.Discover.DiscoverTvLanguage.languageSeries": "{language} Serier", + "components.Discover.DiscoverTvGenre.genreSeries": "{genre} Serier", + "components.Discover.DiscoverStudio.studioMovies": "{studio} Filmer", + "components.Discover.DiscoverNetwork.networkSeries": "{network} Serier", + "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} Filmer", + "components.Discover.DiscoverMovieGenre.genreMovies": "{genre} Filmer", + "components.CollectionDetails.requestcollection4k": "Begär Kollektion i 4K", "components.UserProfile.UserSettings.unauthorizedDescription": "Du har inte behörighet att ändra den här användarens inställningar.", "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": "Du kan inte ändra dina egna behörigheter.", "components.Discover.MovieGenreList.moviegenres": "Filmgenrer", @@ -597,20 +597,20 @@ "components.TvDetails.episodeRuntime": "Avsnittets speltid", "components.Settings.partialRequestsEnabled": "Tillåt begäran av ofullständig serie", "components.Settings.Notifications.pgpPrivateKeyTip": "Signera krypterade e-postmeddelanden med OpenPGP ", - "components.Settings.Notifications.pgpPrivateKey": "PGP privat nyckel", + "components.Settings.Notifications.pgpPrivateKey": "PGP Privat nyckel", "components.Settings.Notifications.pgpPasswordTip": "Signera krypterade e-postmeddelanden med OpenPGP ", - "components.Settings.Notifications.pgpPassword": "PGP-lösenord", + "components.Settings.Notifications.pgpPassword": "PGP Lösenord", "components.RequestModal.alreadyrequested": "Redan begärd", - "components.UserProfile.UserSettings.UserGeneralSettings.general": "Allmänt", + "components.UserProfile.UserSettings.UserGeneralSettings.general": "Allmänna", "pages.somethingwentwrong": "Något gick fel", "pages.serviceunavailable": "Tjänsten är inte tillgänglig", - "pages.pagenotfound": "Sidan hittades inte", + "pages.pagenotfound": "Sidan hittas inte", "pages.internalservererror": "Internt serverfel", "pages.errormessagewithcode": "{statusCode} - {error}", "i18n.usersettings": "Användarinställningar", "i18n.settings": "Inställningar", "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Något gick fel när lösenordet sparades. Har ditt nuvarande lösenord skrivits in korrekt?", - "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Notifikationer", + "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Notifieringar", "components.Settings.services": "Tjänster", "components.Settings.plex": "Plex", "components.Settings.notifications": "Notifieringar", @@ -629,7 +629,7 @@ "components.Settings.SettingsLogs.filterInfo": "Info", "components.Settings.SettingsLogs.filterError": "Fel", "components.Settings.SettingsLogs.filterDebug": "Felsök", - "components.Settings.SettingsJobsCache.jobsandcache": "Jobb & cache", + "components.Settings.SettingsJobsCache.jobsandcache": "Jobb & Cache", "components.Settings.SettingsAbout.about": "Om", "components.ResetPassword.passwordreset": "Återställning av lösenord", "components.Settings.cacheImagesTip": "Optimera och lagra alla bilder lokalt (använder en betydande mängd diskutrymme)", @@ -639,7 +639,7 @@ "components.Settings.SettingsLogs.copyToClipboard": "Kopiera till urklipp", "components.Settings.SettingsLogs.copiedLogMessage": "Kopierat loggmeddelande till urklipp.", "components.Settings.enablessl": "Använd SSL", - "components.UserList.nouserstoimport": "Det finns inga Plex-användare att importera.", + "components.UserList.nouserstoimport": "Det finns inga Plexanvändare att importera.", "components.PersonDetails.lifespan": "{birthdate} – {deathdate}", "components.PersonDetails.birthdate": "Född {birthdate}", "components.PersonDetails.alsoknownas": "Även känd som: {names}", @@ -651,12 +651,12 @@ "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {film} other {filmer}}", "components.RequestModal.QuotaDisplay.movie": "film", "components.RequestModal.QuotaDisplay.allowedRequestsUser": "Denna användare är tillåten att begära {limit} {type} varje {days} dagar.", - "components.RequestModal.QuotaDisplay.allowedRequests": "Du är kan begära {limit} {type} varje {days} dagar.", + "components.RequestModal.QuotaDisplay.allowedRequests": "Du är tillåten att begära {limit} {type} varje {days} dagar.", "components.QuotaSelector.unlimited": "Obegränsat", "i18n.view": "Visa", "i18n.tvshow": "Serier", "i18n.testing": "Testar…", - "i18n.test": "Testa", + "i18n.test": "Test", "i18n.status": "Status", "i18n.showingresults": "Visar {from} till {to} av {total} resultat", "i18n.saving": "Sparar…", @@ -665,32 +665,32 @@ "i18n.requesting": "Begär…", "i18n.request4k": "Begär i 4K", "i18n.previous": "Föregående", - "i18n.notrequested": "Inte begärd", + "i18n.notrequested": "Inte efterfrågad", "i18n.noresults": "Inga resultat.", "i18n.next": "Nästa", "i18n.movie": "Film", "i18n.canceling": "Avbryter…", - "i18n.back": "Tillbaka", + "i18n.back": "Bakåt", "i18n.areyousure": "Är du säker?", "i18n.all": "Alla", "components.UserProfile.unlimited": "Obegränsat", - "components.UserProfile.totalrequests": "Totalt antal förfrågningar", + "components.UserProfile.totalrequests": "Totalt antal förfrågnignar", "components.UserProfile.seriesrequest": "Serieförfrågningar", "components.UserProfile.requestsperdays": "{limit} kvar", "components.UserProfile.pastdays": "{type} (senaste {days} dagar)", "components.UserProfile.movierequests": "Filmförfrågningar", "components.UserProfile.limit": "{remaining} av {limit}", - "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Gräns för serieförfrågningar", - "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Gräns för filmförfrågningar", + "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Serieförfrågnings gräns", + "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Filmförfrågnings gräns", "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Överskrid den globala gränsen", "components.Settings.SettingsUsers.tvRequestLimitLabel": "Global serieförfrågningsgräns", "components.Settings.SettingsUsers.movieRequestLimitLabel": "Global filmförfrågningsgräns", - "components.RequestModal.QuotaDisplay.requiredquotaUser": "Den här användaren behöver ha minst {seasons} {seasons, plural, one {säsongsförfrågan} other {säsongsförfrågningar}} kvar för att begära denna serie.", + "components.RequestModal.QuotaDisplay.requiredquotaUser": "Den här användaren behöver ha minst {seasons} {seasons, plural, one {säsongsförfrågan} other {säsongsförfrågningar}} kvar för att skicka in en begäran om denna serie.", "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {säsong} other {säsonger}}", "components.RequestModal.QuotaDisplay.season": "säsong", - "components.RequestModal.QuotaDisplay.requiredquota": "Du behöver minst{seasons} {seasons, plural, one {säsongförfrågan} other {säsongsförfrågningar}} kvar för att begära denna serie.", + "components.RequestModal.QuotaDisplay.requiredquota": "Du behöver minst{seasons} {seasons, plural, one {säsongförfrågan request} other {säsongsförfrågningar}} kvar för att lämna in en begäran om denna serie.", "components.TvDetails.originaltitle": "Originaltitel", - "components.MovieDetails.originaltitle": "Originaltitel", + "components.MovieDetails.originaltitle": "Originaltitle", "components.LanguageSelector.originalLanguageDefault": "Alla språk", "components.LanguageSelector.languageServerDefault": "Standard ({language})", "components.Settings.SonarrModal.testFirstTags": "Testa anslutningen för att ladda taggar", @@ -699,16 +699,16 @@ "components.Settings.SonarrModal.notagoptions": "Inga taggar.", "components.Settings.SonarrModal.loadingTags": "Laddar taggar…", "components.Settings.SonarrModal.edit4ksonarr": "Redigera 4K Sonarr-server", - "components.Settings.SonarrModal.default4kserver": "Standard 4K-server", + "components.Settings.SonarrModal.default4kserver": "Standard 4K server", "components.Settings.SonarrModal.create4ksonarr": "Lägg till ny 4K Sonarr-server", - "components.Settings.SonarrModal.animeTags": "Anime-taggar", + "components.Settings.SonarrModal.animeTags": "Anime taggar", "components.Settings.RadarrModal.testFirstTags": "Testa anslutningen för att ladda taggar", "components.Settings.RadarrModal.tags": "Taggar", "components.Settings.RadarrModal.selecttags": "Välj taggar", "components.Settings.RadarrModal.create4kradarr": "Lägg till ny 4K Radarr-server", "components.Settings.RadarrModal.notagoptions": "Inga taggar.", - "components.Settings.RadarrModal.edit4kradarr": "Redigera 4K-Radarr-server", - "components.Settings.RadarrModal.default4kserver": "Standard 4K-server", + "components.Settings.RadarrModal.edit4kradarr": "Redigera 4K Radarr-server", + "components.Settings.RadarrModal.default4kserver": "Standard 4K server", "components.RequestModal.AdvancedRequester.tags": "Taggar", "components.RequestModal.AdvancedRequester.selecttags": "Välj taggar", "components.RequestModal.AdvancedRequester.notagoptions": "Inga taggar.", @@ -719,17 +719,17 @@ "components.RequestCard.deleterequest": "Ta bort begäran", "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Notifieringsinställningar för Discord kunde inte sparas.", "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "Du måste ange en giltig offentlig PGP-nyckel", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Notifikationsinställningar för Telegram har sparats!", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Notifikationsinställningar för Telegram kunde inte sparas.", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Notiferingsinställningar för Telegram sparade!", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Notifieringsinställningar för Telegram kunde inte sparas.", "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "Kryptera e-postmeddelanden med OpenPGP", "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "Offentlig PGP-nyckel", - "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "Inställningarna för e-postnotifikationer har sparats!", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "Inställningar för e-postavisering har sparats!", "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": "Notifieringsinställningar för e-post kunde inte sparas.", "components.UserProfile.UserSettings.UserNotificationSettings.email": "E-post", - "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "Notifieringsinställningar för Discord sparade!", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "Notiferingsinställningar för Discord sparade!", "components.Settings.Notifications.validationPgpPrivateKey": "Du måste ange en giltig privat PGP-nyckel", "components.Settings.Notifications.validationPgpPassword": "Du måste ange ett PGP-lösenord", - "components.Settings.Notifications.botUsernameTip": "Låt användare också starta en chatt med din bot och konfigurera sina egna notifikationer", + "components.Settings.Notifications.botUsernameTip": "Låt användare också starta en chatt med din bot och konfigurera sina egna aviseringar", "components.RequestModal.pendingapproval": "Din begäran väntar på godkännande.", "components.RequestList.RequestItem.cancelRequest": "Avbryt begäran", "components.NotificationTypeSelector.notificationTypes": "Notifikationstyper", @@ -739,9 +739,9 @@ "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {ändring} other {ändringar}} efter", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Ditt konto har för närvarande inget lösenord. Konfigurera ett lösenord nedan för att aktivera inloggning som en \"lokal användare\" med din e-postadress.", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Det här användarkontot har för närvarande inget lösenord. Konfigurera ett lösenord nedan så att det här kontot kan logga in som en \"lokal användare.\"", - "components.Settings.serviceSettingsDescription": "Konfigurera din {serverType}-server nedan. Du kan ansluta till flera {serverType}-servrar, men bara två av dem kan markeras som standard (en icke-4K och en 4K). Administratörer kan åsidosätta servern som används för att behandla nya förfrågningar innan godkännande.", - "components.Settings.noDefaultServer": "Minst en {serverType}-server måste markeras som standard för att {mediaType}-förfrågningar ska kunna behandlas.", - "components.Settings.noDefaultNon4kServer": "Om du bara har en {serverType}-server för både 4K och icke-4K innehåll (eller om du bara laddar ner 4K-innehåll) bör din {serverType}-server INTE betecknas som en 4K-server.", + "components.Settings.serviceSettingsDescription": "Konfigurera din server {serverType} nedan. Du kan ansluta flera {serverType} servrar, men bara två av dem kan markeras som standard (en icke-4K och en 4K). Administratörer kan åsidosätta servern som används för att behandla nya förfrågningar innan godkännande.", + "components.Settings.noDefaultServer": "Minst en {serverType} server måste markeras som standard för att {mediaType} -förfrågningar ska kunna behandlas.", + "components.Settings.noDefaultNon4kServer": "Om du bara har en enda {serverType} server för både icke-4K- och 4K-innehåll (eller om du bara laddar ner 4K-innehåll) bör din {serverType} server INTE betecknas som en 4K-server.", "components.Settings.mediaTypeSeries": "serier", "components.Settings.mediaTypeMovie": "film", "components.Settings.SettingsAbout.uptodate": "Aktuell", @@ -758,45 +758,45 @@ "components.UserProfile.UserSettings.UserGeneralSettings.applanguage": "Visningsspråk", "components.Settings.webpush": "Webb-push", "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "Webb-push-meddelandeinställningar sparades!", - "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": "Webb-push-notifikationsinställningar kunde inte sparas.", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": "Webb-push-aviseringsinställningar kunde inte sparas.", "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Aktivera agent", "components.Settings.Notifications.NotificationsLunaSea.webhookUrl": "Webhook-URL", "components.Settings.Notifications.NotificationsLunaSea.validationWebhookUrl": "Du måste ange en giltig URL", - "components.Settings.Notifications.NotificationsLunaSea.settingsSaved": "LunaSea-notifikationsinställningar har sparats!", - "components.Settings.Notifications.NotificationsLunaSea.settingsFailed": "LunaSea-notifikationsinställningarna kunde inte sparas.", + "components.Settings.Notifications.NotificationsLunaSea.settingsSaved": "LunaSea-aviseringsinställningar har sparats!", + "components.Settings.Notifications.NotificationsLunaSea.settingsFailed": "LunaSea-aviseringsinställningarna kunde inte sparas.", "components.Settings.Notifications.NotificationsLunaSea.profileNameTip": "Krävs endast om du inte använder default", "components.Settings.Notifications.NotificationsLunaSea.profileName": "Profilnamn", "components.Settings.Notifications.NotificationsLunaSea.agentenabled": "Aktivera agent", - "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSending": "Skickar Pushbullet testmeddelande…", - "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": "Pushbullet-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSending": "Skickar Pushbullet testmeddelande …", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": "Pushbullet-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSuccess": "LunaSea testmeddelande skickat!", - "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSending": "Skickar LunaSea testmeddelande…", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSending": "Skickar LunaSea testmeddelande …", "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestFailed": "LunaSea-testmeddelandet kunde inte skickas.", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Notifikationsinställningar för webb-push har sparats!", - "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Notifikationsinställningar för webb-push kunde inte sparas.", - "components.Settings.noDefault4kServer": "En 4K {serverType}-server måste markeras som standard för att användare ska kunna skicka 4K-{mediaType}förfrågningar.", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Inställningar för webb push-meddelanden har sparats!", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Webb-push-aviseringsinställningar kunde inte sparas.", + "components.Settings.noDefault4kServer": "En 4K {serverType}-server måste markeras som standard för att användare ska kunna skicka 4K {mediaType}-förfrågningar.", "components.Settings.is4k": "4K", "components.Settings.Notifications.toastTelegramTestSuccess": "Telegram testmeddelande skickat!", - "components.Settings.Notifications.toastTelegramTestSending": "Skickar Telegram-testmeddelande…", - "components.Settings.Notifications.toastTelegramTestFailed": "Telegram-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.toastTelegramTestSending": "Skickar telegramtestmeddelande…", + "components.Settings.Notifications.toastTelegramTestFailed": "Telegram-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.toastEmailTestSuccess": "E-post testmeddelande skickat!", "components.Settings.Notifications.toastEmailTestSending": "Skickar e-posttestmeddelande…", - "components.Settings.Notifications.toastEmailTestFailed": "Testmeddelandet via e-post kunde inte skickas.", + "components.Settings.Notifications.toastEmailTestFailed": "E-post testmeddelandet kunde inte skickas.", "components.Settings.Notifications.toastDiscordTestSuccess": "Discord testmeddelande skickat!", "components.Settings.Notifications.toastDiscordTestSending": "Skickar Discord testmeddelande…", - "components.Settings.Notifications.toastDiscordTestFailed": "Discord-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.toastDiscordTestFailed": "Discord-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSuccess": "Webhook testmeddelande skickat!", "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSending": "Skickar webhook testmeddelande…", - "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": "Webhook-testmeddelande kunde inte skickas.", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Webbpush-testmeddelande skickat!", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Skickar webbpush-testmeddelande…", - "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Webbpush-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": "Webhook-testmeddelandet kunde inte skickas.", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Webb-push-testmeddelande skickat!", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Skicka webb push-testmeddelande…", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Webb-push-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.NotificationsSlack.toastSlackTestSuccess": "Slack testmeddelande skickat!", - "components.Settings.Notifications.NotificationsSlack.toastSlackTestSending": "Skickar Slack testmeddelande…", - "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": "Slack-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestSending": "Skickar Slack testmeddelande …", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": "Slack-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSuccess": "Pushover testmeddelande skickat!", - "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "Skickar Pushover-testmeddelande…", - "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": "Pushover-testmeddelande kunde inte skickas.", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "Skickar Pushover-testmeddelande …", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": "Pushover-testmeddelandet kunde inte skickas.", "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSuccess": "Pushbullet testmeddelande skickat!", "components.Settings.SettingsUsers.newPlexLoginTip": "Tillåt {mediaServerName}-användare att logga in utan att först importeras", "components.Settings.SettingsUsers.newPlexLogin": "Aktivera ny {mediaServerName}-inloggning", @@ -819,9 +819,9 @@ "components.Settings.Notifications.NotificationsPushover.userTokenTip": "Din 30-tecken användare eller gruppidentifierare", "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Registrera en applikation för användning med Jellyseerr", "components.Settings.Notifications.NotificationsPushbullet.accessTokenTip": "Skapa en token från dina kontoinställningar", - "components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Din användar- eller enhetsbaserade webbhook-URL för notifikationer", + "components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Din användar- eller enhetsbaserade webbhook-URL för avisering", "components.DownloadBlock.estimatedtime": "Beräknad {time}", - "components.Settings.webAppUrlTip": "Valfri omdirigering av användare till webbappen på din server istället för host-webbappen", + "components.Settings.webAppUrlTip": "Alternativt dirigera användare till webbappen på din server istället för den \"värd\" webbappen", "components.Settings.webAppUrl": "Webbapp URL", "components.UserList.localLoginDisabled": "Inställningen Aktivera lokal inloggning är för närvarande inaktiverad.", "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "För att kunna ta emot push-notiser måste Jellyseerr serveras via HTTPS.", @@ -829,21 +829,21 @@ "components.RequestList.RequestItem.requesteddate": "Begärd", "components.RequestCard.failedretry": "Något gick fel vid nytt försök av begäran.", "components.Settings.SettingsUsers.localLoginTip": "Tillåt användare att logga in med sin e-postadress och lösenord istället för Plex OAuth", - "components.Settings.SettingsUsers.defaultPermissionsTip": "Behörigheter som tilldelas nya användare", + "components.Settings.SettingsUsers.defaultPermissionsTip": "Inledande behörigheter som tilldelats nya användare", "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} per {quotaDays} {days}", "components.QuotaSelector.tvRequests": "{quotaLimit} {seasons} per {quotaDays} {days}", "components.QuotaSelector.seasons": "{count, plural, one {säsong} other {säsonger}}", "components.QuotaSelector.movies": "{count, plural, one {film} other {filmer}}", "components.QuotaSelector.days": "{count, plural, one {dag} other {dagar}}", - "components.Settings.Notifications.NotificationsLunaSea.validationTypes": "Du måste välja minst en notifikationstyp", - "components.Settings.Notifications.validationTypes": "Du måste välja minst en notifikationstyp", - "components.Settings.Notifications.NotificationsWebhook.validationTypes": "Du måste välja minst en notifikationstyp", - "components.Settings.Notifications.NotificationsSlack.validationTypes": "Du måste välja minst en notifikationstyp", - "components.Settings.Notifications.NotificationsPushover.validationTypes": "Du måste välja minst en notifikationstyp", - "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "Du måste välja minst en notifikationstyp", + "components.Settings.Notifications.NotificationsLunaSea.validationTypes": "Du måste välja minst en aviseringstyp", + "components.Settings.Notifications.validationTypes": "Du måste välja minst en aviseringstyp", + "components.Settings.Notifications.NotificationsWebhook.validationTypes": "Du måste välja minst en aviseringstyp", + "components.Settings.Notifications.NotificationsSlack.validationTypes": "Du måste välja minst en aviseringstyp", + "components.Settings.Notifications.NotificationsPushover.validationTypes": "Du måste välja minst en aviseringstyp", + "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "Du måste välja minst en aviseringstyp", "components.NotificationTypeSelector.usermediarequestedDescription": "Bli meddelad när andra användare skickar nya medieförfrågningar som kräver godkännande.", "components.NotificationTypeSelector.usermediafailedDescription": "Bli meddelad när medieförfrågningar inte kan läggas till i Radarr eller Sonarr.", - "components.NotificationTypeSelector.usermediadeclinedDescription": "Skicka notifikationer när dina medieförfrågningar nekas.", + "components.NotificationTypeSelector.usermediadeclinedDescription": "Få avisering när dina medieförfrågningar avvisas.", "components.NotificationTypeSelector.usermediaavailableDescription": "Bli meddelad när dina medieförfrågningar blir tillgängliga.", "components.NotificationTypeSelector.usermediaapprovedDescription": "Bli meddelad när dina medieförfrågningar godkänns.", "components.NotificationTypeSelector.usermediaAutoApprovedDescription": "Bli meddelad när andra användare skickar in nya medieförfrågningar som automatiskt godkänns.", @@ -894,7 +894,7 @@ "components.IssueDetails.reopenissueandcomment": "Återöppna med en kommentar", "components.IssueDetails.season": "Säsong {seasonNumber}", "components.IssueDetails.toasteditdescriptionsuccess": "Problembeskrivningen har redigerats!", - "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {säsong} other {säsonger}}", + "components.IssueList.IssueItem.seasons": "{seasonCount, plural, one {Säsong} other {Säsonger}}", "components.IssueList.IssueItem.unknownissuetype": "Okänd", "components.IssueList.IssueItem.openeduserdate": "{date} av {user}", "components.IssueList.IssueItem.viewissue": "Visa problem", @@ -907,19 +907,19 @@ "components.IssueDetails.nocomments": "Inga kommentarer.", "components.IssueDetails.toaststatusupdated": "Problemstatus har uppdaterats!", "components.IssueDetails.toastissuedeleted": "Problemet har tagits bort!", - "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {avsnitt} other {avsnitt}}", + "components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {Avsnitt} other {Avsnitt}}", "components.IssueList.IssueItem.issuestatus": "Status", "components.IssueList.IssueItem.issuetype": "Typ", "components.IssueList.IssueItem.problemepisode": "Påverkat avsnitt", "components.IssueModal.CreateIssueModal.toastSuccessCreate": "Problemrapport för {title} har skickats in!", - "components.ManageSlideOver.tvshow": "serier", + "components.ManageSlideOver.tvshow": "serie", "components.ManageSlideOver.openarr4k": "Öppna i 4K {arr}", "components.IssueModal.CreateIssueModal.reportissue": "Rapportera ett problem", "components.IssueModal.CreateIssueModal.season": "Säsong {seasonNumber}", "components.IssueModal.issueOther": "Övrigt", "components.PermissionEdit.manageissuesDescription": "Bevilja behörighet att hantera medieproblem.", "components.IssueModal.CreateIssueModal.toastFailedCreate": "Något gick fel när problemet skickades in.", - "components.IssueModal.CreateIssueModal.submitissue": "Skicka problem", + "components.IssueModal.CreateIssueModal.submitissue": "Skicka in problemet", "components.ManageSlideOver.movie": "film", "components.IssueModal.CreateIssueModal.whatswrong": "Vad är fel?", "components.IssueModal.issueAudio": "Ljud", @@ -930,25 +930,25 @@ "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "Användar- eller gruppnyckel", "components.ManageSlideOver.openarr": "Öppna i {arr}", "components.ManageSlideOver.manageModalTitle": "Hantera {mediaType}", - "components.ManageSlideOver.mark4kavailable": "Markera som tillgänglig i 4K", - "components.NotificationTypeSelector.adminissuecommentDescription": "Skicka notifikation när ett problem får nya kommentarer.", + "components.ManageSlideOver.mark4kavailable": "Markera som tillgängligt i 4K", + "components.NotificationTypeSelector.adminissuecommentDescription": "Få aviseringar när ett problem får nya kommentarer.", "components.ManageSlideOver.manageModalRequests": "Förfrågningar", "components.PermissionEdit.manageissues": "Hantera problem", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Inställningarna för Pushover-notifikationer kunde inte sparas.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Inställningarna för pushover-meddelanden kunde inte sparas.", "components.ManageSlideOver.markavailable": "Markera som tillgänglig", "components.NotificationTypeSelector.issuecomment": "Problemkommentar", - "components.NotificationTypeSelector.issuecommentDescription": "Skicka notifikationer när problem får nya kommentarer.", + "components.NotificationTypeSelector.issuecommentDescription": "Skicka aviseringar när problem får nya kommentarer.", "components.PermissionEdit.createissuesDescription": "Bevilja behörighet att rapportera medieproblem.", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "Din 30-teckens användar- eller gruppidentifierare", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "Registrera en applikation för användning med {applicationTitle}", - "components.PermissionEdit.viewissuesDescription": "Bevilja behörighet att se medieproblem som rapporterats av andra användare.", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "API-token för applikation", - "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "Inställningarna för Pushover-notifikationer har sparats!", + "components.PermissionEdit.viewissuesDescription": "Ge tillstånd att se medieproblem som rapporterats av andra användare.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Api-token för program", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "Inställningar för pushover-meddelanden har sparats!", "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "Du måste tillhandahålla en giltig applikationstoken", "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Du måste ange en giltig användar- eller gruppnyckel", "components.IssueModal.CreateIssueModal.problemseason": "Påverkad säsong", "components.IssueModal.CreateIssueModal.toastviewissue": "Visa problem", - "components.NotificationTypeSelector.issuecreated": "Problem rapporterat", + "components.NotificationTypeSelector.issuecreated": "Problem rappoterat", "components.PermissionEdit.createissues": "Rapportera problem", "components.PermissionEdit.viewissues": "Visa problem", "components.ManageSlideOver.manageModalClearMediaWarning": "* Detta tar bort all data för denna {mediaType}, inklusive eventuella begäranden, på ett oåterkalleligt sätt. Om det här objektet finns i ditt {mediaServerName}-bibliotek kommer medieinformationen att återskapas vid nästa genomsökning.", @@ -965,42 +965,42 @@ "components.IssueModal.CreateIssueModal.providedetail": "Ge en detaljerad förklaring av det problem du stötte på.", "components.ManageSlideOver.downloadstatus": "Nedladdningar", "components.ManageSlideOver.manageModalClearMedia": "Rensa data", - "components.NotificationTypeSelector.issuecreatedDescription": "Skicka notifikationer när problem rapporteras.", + "components.NotificationTypeSelector.issuecreatedDescription": "Skicka aviseringar när problem rapporteras.", "components.NotificationTypeSelector.issueresolved": "Problem löst", - "components.NotificationTypeSelector.issueresolvedDescription": "Skicka notifikationer när problem blir lösta.", + "components.NotificationTypeSelector.issueresolvedDescription": "Skicka aviseringar när problem blir lösta.", "components.NotificationTypeSelector.userissuecommentDescription": "Få meddelanden när ett problem som du har mottagit har får nya kommentarer.", "components.NotificationTypeSelector.userissuecreatedDescription": "Få meddelanden när andra användare rapporterar problem.", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "Skapa en token från dina kontoinställningar", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": "Inställningar för Pushbullet-notifikationer kunde inte sparas.", - "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Inställningar för Pushbullet-notifikationer har sparats!", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": "Inställningar för Pushbullet-aviseringar kunde inte sparas.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Inställningar för Pushbullet-aviseringar har sparats!", "i18n.open": "Öppna", - "i18n.resolved": "Lösta", + "i18n.resolved": "Löst", "components.ManageSlideOver.manageModalIssues": "Öppna problem", "components.IssueModal.CreateIssueModal.extras": "Extras", - "components.NotificationTypeSelector.adminissuereopenedDescription": "Skicka notifikationer när en användare återöppnar ett problem.", + "components.NotificationTypeSelector.adminissuereopenedDescription": "Få avisering när en användare återöppnar ett problem.", "components.NotificationTypeSelector.issuereopened": "Problem återöppnat", - "components.NotificationTypeSelector.issuereopenedDescription": "Skicka notifikation när ett problem at återöppnats.", - "components.NotificationTypeSelector.userissuereopenedDescription": "Skicka notifikationer när ett problem som du har rapporterat har återöppnats.", - "components.NotificationTypeSelector.adminissueresolvedDescription": "Få en notifikation när ett problem har blivit löst.", + "components.NotificationTypeSelector.issuereopenedDescription": "Skicka avisering när ett problem at återöppnats.", + "components.NotificationTypeSelector.userissuereopenedDescription": "Få avisering när ett problem som du har rapporterat har återöppnats.", + "components.NotificationTypeSelector.adminissueresolvedDescription": "Få avisering när ett problem har blivit löst.", "components.MovieDetails.productioncountries": "Produktions{countryCount, plural, one {land} other {länder}}", - "components.RequestModal.selectmovies": "Välj film(er)", + "components.RequestModal.selectmovies": "Välj Film(er)", "components.IssueDetails.commentplaceholder": "Lägg till en kommentar…", - "components.RequestModal.approve": "Godkänn begäran", - "components.RequestModal.requestApproved": "Begäran av {title} godkänd!", - "components.RequestModal.requestmovies": "Begär {count} {count, plural, one {film} other {filmer}}", - "components.RequestModal.requestmovies4k": "Begär {count} {count, plural, one {film} other {filmer}} i 4K", - "components.RequestModal.requestseasons4k": "Begär {seasonCount} {seasonCount, plural, one {säsong} other {säsonger}} i 4K", - "components.Settings.RadarrModal.inCinemas": "På bio", + "components.RequestModal.approve": "Godkänn efterfrågan", + "components.RequestModal.requestApproved": "förfrågan av {title} godkänd!", + "components.RequestModal.requestmovies": "Begär {count} {count, plural, one {Film} other {Filmer}}", + "components.RequestModal.requestmovies4k": "Begär {count} {count, plural, one {Film} other {Filmer}} i 4K", + "components.RequestModal.requestseasons4k": "Begär {seasonCount} {seasonCount, plural, one {Säsong} other {Säsonger}} i 4K", + "components.Settings.RadarrModal.inCinemas": "På Bio", "components.Settings.RadarrModal.released": "Släppt", - "components.Settings.RadarrModal.announced": "Tillkännagiven", + "components.Settings.RadarrModal.announced": "Annonserat", "components.TvDetails.productioncountries": "Produktions{countryCount, plural, one {land} other {länder}}", - "components.Settings.Notifications.enableMentions": "Aktivera omnämnanden", + "components.Settings.Notifications.enableMentions": "Aktivera omnämnande", "components.Settings.Notifications.NotificationsGotify.agentenabled": "Aktivera agent", "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": "Inställningarna för Gotify-meddelanden sparades inte.", "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "Inställningarna för Gotify-meddelanden har sparats!", "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": "Det gick inte att skicka Gotify-testmeddelandet.", - "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "Skickar Gotify testmeddelande…", - "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "Gotify testmeddelande skickat!", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "Skicka Gotify testmeddelande…", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "Gotify testmeddelande skickas!", "components.Settings.Notifications.NotificationsGotify.token": "Applikationstoken", "components.Settings.Notifications.NotificationsGotify.url": "Server URL", "components.Settings.Notifications.NotificationsGotify.validationTokenRequired": "Du måste ange ett applikationstoken", @@ -1012,8 +1012,8 @@ "i18n.importing": "Importerar…", "components.ManageSlideOver.alltime": "All tid", "components.ManageSlideOver.manageModalAdvanced": "Avancerad", - "components.ManageSlideOver.playedby": "Spelad av", - "components.Settings.toastTautulliSettingsFailure": "Kunde inte spara Tautulli-inställningarna.", + "components.ManageSlideOver.playedby": "Spelas av", + "components.Settings.toastTautulliSettingsFailure": "Något gick fel när du sparade Tautulli-inställningarna.", "components.Settings.Notifications.NotificationsPushbullet.channelTag": "Kanaltagg", "components.Settings.urlBase": "URL-bas", "components.Settings.validationApiKey": "Du måste ange en API-nyckel", @@ -1041,43 +1041,43 @@ "components.RequestBlock.languageprofile": "Språkprofil", "components.StatusChecker.reloadApp": "Ladda om {applicationTitle}", "i18n.restartRequired": "Omstart krävs", - "components.Settings.deleteServer": "Radera {serverType}-server", - "components.StatusChecker.appUpdated": "{applicationTitle} uppdaterad", - "components.StatusChecker.appUpdatedDescription": "Klicka på knappen nedan för att ladda om programmet.", + "components.Settings.deleteServer": "Radera {serverType} Server", + "components.StatusChecker.appUpdated": "{applicationTitle} Uppdaterad", + "components.StatusChecker.appUpdatedDescription": "Klicka på knappen under för att ladda om programmet.", "components.StatusChecker.restartRequired": "Servern behöver startas om", "components.StatusChecker.restartRequiredDescription": "Starta om servern för att verkställa uppdaterade inställningar.", "components.MovieDetails.digitalrelease": "Digital utgåva", "components.MovieDetails.physicalrelease": "Fysisk utgåva", "components.PermissionEdit.viewrecent": "Se nyligen tillagda", - "components.Discover.DiscoverWatchlist.discoverwatchlist": "Din Plex Watchlist", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "Din Plex spellista", "components.RequestCard.tmdbid": "TMDB ID", - "components.Discover.plexwatchlist": "Din Plex Watchlist", - "components.Discover.DiscoverWatchlist.watchlist": "Plex bevakningslista", + "components.Discover.plexwatchlist": "Din Plex spellista", + "components.Discover.DiscoverWatchlist.watchlist": "Plex spellista", "components.MovieDetails.reportissue": "Rapportera ett problem", "components.AirDateBadge.airedrelative": "Sändes {relativeTime}", "components.AirDateBadge.airsrelative": "Sänds {relativeTime}", - "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "Begärda serier", + "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "Önskade serier", "components.RequestCard.approverequest": "Godkänn begäran", "components.RequestCard.tvdbid": "TheTVDB ID", "components.RequestBlock.approve": "Godkänn begäran", "components.RequestBlock.decline": "Neka begäran", "components.RequestBlock.delete": "Ta bort begäran", - "components.RequestBlock.edit": "Redigera begäran", - "components.RequestBlock.lastmodifiedby": "Senast ändrad av", + "components.RequestBlock.edit": "Editera begäran", + "components.RequestBlock.lastmodifiedby": "Senast modifierad av", "components.RequestCard.declinerequest": "Neka begäran", - "components.RequestCard.cancelrequest": "Avbryt begäran", - "components.MovieDetails.theatricalrelease": "Biografpremiär", + "components.RequestCard.cancelrequest": "Annullera begäran", + "components.MovieDetails.theatricalrelease": "Teaterutgåva", "components.PermissionEdit.autorequest": "Automatisk begäran", - "components.MovieDetails.managemovie": "Hantera film", - "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "Begärda filmer", - "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes publikbetyg", - "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes tomatometer", - "components.MovieDetails.tmdbuserscore": "TMDB användarpoäng", + "components.MovieDetails.managemovie": "Hantera Film", + "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "Filmbegäran", + "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes publiksiffror", + "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", + "components.MovieDetails.tmdbuserscore": "TMDB Användarpoäng", "components.NotificationTypeSelector.mediaautorequested": "Begäran skickas automatiskt", "components.NotificationTypeSelector.mediaautorequestedDescription": "Få meddelande när nya medieförfrågningar skickas in automatiskt för objekt på din bevakningslista för Plex.", "components.Discover.emptywatchlist": "Media som lagts till i din Plex Watchlist visas här.", - "components.Layout.UserDropdown.requests": "Förfrågningar", - "components.TvDetails.episodeCount": "{episodeCount, plural, one {# avsnitt} other {# avsnitt}}", + "components.Layout.UserDropdown.requests": "Efterfråga", + "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Avsnitt} other {# Avsnitt}}", "components.TvDetails.seasonstitle": "Säsonger", "components.Settings.SettingsJobsCache.image-cache-cleanup": "Rensning av bildcache", "components.Settings.SettingsJobsCache.imagecache": "Bildcache", @@ -1086,57 +1086,57 @@ "components.Settings.experimentalTooltip": "Om du aktiverar den här inställningen kan det leda till oväntat beteende i programmet", "components.TitleCard.tmdbid": "TMDB ID", "components.PermissionEdit.autorequestDescription": "Ge tillstånd att automatiskt skicka in förfrågningar för medier som inte är 4K-medier via Plex Watchlist.", - "components.PermissionEdit.autorequestSeriesDescription": "Bevilja automatiska förfrågningar för icke-4K-serier via Plex Watchlist.", - "components.PermissionEdit.autorequestSeries": "Begär serier automatiskt", + "components.PermissionEdit.autorequestSeriesDescription": "Ge tillstånd att automatiskt skicka in förfrågningar för icke-4K-serier via Plex Watchlist.", + "components.PermissionEdit.autorequestSeries": "Begär automatiskt serier", "components.RequestList.RequestItem.tmdbid": "TMDB ID", "components.RequestList.RequestItem.tvdbid": "TheTVDB ID", "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Synkronisering av Plex Watchlist", "components.Settings.advancedTooltip": "Om du konfigurerar den här inställningen felaktigt kan det leda till att funktionerna inte fungerar", - "components.Settings.restartrequiredTooltip": "Jellyseerr måste startas om för att ändringarna i den här inställningen ska träda i kraft", + "components.Settings.restartrequiredTooltip": "Overseerr måste startas om för att ändringarna i den här inställningen ska träda i kraft", "components.TvDetails.reportissue": "Rapportera ett problem", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Begär serier automatiskt", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Begär serier automatiskt från din Plex Watchlist", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Begär automatiskt filmer från din Plex Watchlist", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Begär automatiskt serier", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Begär automatiskt serier på din Plex Watchlist", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Begär automatiskt filmer på din Plex Watchlist", "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Nuvarande frekvens", "components.PermissionEdit.viewrecentDescription": "Ge tillstånd att visa listan över nyligen tillagda medier.", "components.StatusBadge.managemedia": "Hantera {mediaType}", "components.StatusBadge.playonplex": "Spela upp på Plex", "components.TitleCard.tvdbid": "TheTVDB ID", "components.Settings.SettingsLogs.viewdetails": "Visa detaljer", - "components.Settings.SettingsJobsCache.imagecacheDescription": "När det är aktiverat i inställningarna kommer Jellyseerrr att göra proxy- och cache-bilder från förkonfigurerade externa källor. Cachade bilder sparas i din konfigurationsmapp. Du hittar filerna i {appDataPath}/cache/images.", + "components.Settings.SettingsJobsCache.imagecacheDescription": "När det är aktiverat i inställningarna kommer Overseerr att göra proxy- och cache-bilder från förkonfigurerade externa källor. Cachade bilder sparas i din konfigurationsmapp. Du hittar filerna i {appDataPath}/cache/images.", "components.TitleCard.cleardata": "Rensa data", - "components.TitleCard.mediaerror": "{mediaType} hittades inte", - "components.TvDetails.rtcriticsscore": "Rotten Tomatoes tomatometer", - "components.TvDetails.tmdbuserscore": "TMDB användarbetyg", + "components.TitleCard.mediaerror": "{mediaType} Hittades inte", + "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", + "components.TvDetails.tmdbuserscore": "TMDB Användarpoäng", "components.UserProfile.emptywatchlist": "Media som lagts till i din Plex Watchlist visas här.", "components.UserProfile.plexwatchlist": "Plex Watchlist", - "components.TvDetails.Season.noepisodes": "Avsnittslistan är inte tillgänglig.", + "components.TvDetails.Season.noepisodes": "Episodlistan är inte tillgänglig.", "components.PermissionEdit.viewwatchlistsDescription": "Ge tillstånd att visa andra användares Plex Watchlists.", - "components.PermissionEdit.autorequestMovies": "Begär filmer automatiskt", - "components.PermissionEdit.autorequestMoviesDescription": "Bevilja automatiska förfrågningar för icke-4K filmer via Plex Watchlist.", + "components.PermissionEdit.autorequestMovies": "Begär automatiskt filmer", + "components.PermissionEdit.autorequestMoviesDescription": "Ge tillstånd att automatiskt skicka in förfrågningar för filmer som inte är 4K-filmer via Plex Watchlist.", "components.PermissionEdit.viewwatchlists": "Visa Plex Watchlist", "components.RequestModal.SearchByNameModal.nomatches": "Vi kunde inte hitta någon matchning för denna serie.", "components.RequestBlock.requestdate": "Datum för begäran", "components.RequestBlock.requestedby": "Begärt av", - "components.RequestCard.editrequest": "Redigera begäran", + "components.RequestCard.editrequest": "Editera begäran", "components.RequestModal.requestcollection4ktitle": "Begär samling i 4K", - "components.RequestModal.requestcollectiontitle": "Begär samling", + "components.RequestModal.requestcollectiontitle": "Begär Kollektion", "components.RequestModal.requestmovie4ktitle": "Begär film i 4K", "components.RequestModal.requestmovietitle": "Begär film", "components.RequestModal.requestseries4ktitle": "Begär serien i 4K", "components.RequestModal.requestseriestitle": "Begär serie", "components.StatusBadge.openinarr": "Öppna i {arr}", - "components.TvDetails.Season.somethingwentwrong": "Kunde inte hämta säsongsdata.", - "components.TvDetails.manageseries": "Hantera serier", + "components.TvDetails.Season.somethingwentwrong": "Något gick fel när säsongsdata hämtades.", + "components.TvDetails.manageseries": "Hantera Serier", "components.TvDetails.rtaudiencescore": "Rotten Tomatoes publiksiffror", "components.TvDetails.seasonnumber": "Säsong {seasonNumber}", "components.TvDetails.status4k": "4K {status}", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Begär filmer automatiskt", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Begär automatiskt filmer", "components.Discover.DiscoverMovies.discovermovies": "Filmer", - "components.Discover.DiscoverMovies.sortPopularityAsc": "Popularitet stigande", + "components.Discover.DiscoverMovies.sortPopularityAsc": "Populärt Stigande", "components.Discover.DiscoverTv.discovertv": "Serier", - "components.Discover.CreateSlider.nooptions": "Inga resultat.", - "components.Discover.CreateSlider.searchStudios": "Sök studior…", + "components.Discover.CreateSlider.nooptions": "Inga Resultat.", + "components.Discover.CreateSlider.searchStudios": "Sök studios…", "components.Discover.CreateSlider.addcustomslider": "Skapa anpassat skjutreglage", "components.Discover.CreateSlider.addfail": "Det gick inte att skapa nytt skjutreglage.", "components.Discover.CreateSlider.addSlider": "Lägg till skjutreglage", @@ -1148,15 +1148,15 @@ "components.Discover.FilterSlideover.filters": "Filter", "components.Discover.FilterSlideover.firstAirDate": "Första sändningsdatum", "components.Discover.FilterSlideover.from": "Från", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} filmer", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filmer", "components.Discover.moviegenres": "Filmgenrer", "components.Discover.networks": "Nätverk", "components.Settings.SettingsMain.apikey": "API-nyckel", "components.Settings.SettingsMain.applicationTitle": "Applikationstitel", "components.Settings.SettingsMain.applicationurl": "Applikations-URL", "components.Settings.SettingsMain.csrfProtection": "Aktivera CSRF-skydd", - "components.Settings.SettingsMain.trustProxy": "Aktivera proxy-stöd", - "components.Settings.SettingsMain.hideAvailable": "Dölj tillgängliga medier", + "components.Settings.SettingsMain.trustProxy": "Aktivera Proxy-stöd", + "components.Settings.SettingsMain.hideAvailable": "Dölja tillgängliga medier", "components.Settings.SettingsMain.toastSettingsFailure": "Något gick fel när du sparade inställningarna.", "components.Settings.SettingsMain.toastSettingsSuccess": "Inställningarna har sparats!", "components.Settings.SettingsMain.validationApplicationTitle": "Du måste ange en applikationstitel", @@ -1164,7 +1164,7 @@ "components.Discover.tmdbmoviestreamingservices": "TMDB filmströmningstjänster", "components.Discover.tmdbnetwork": "TMDB nätverk", "components.Discover.tmdbsearch": "TMDB sök", - "components.Discover.tmdbstudio": "TMDB studio", + "components.Discover.tmdbstudio": "TMDB Studio", "components.Discover.tmdbtvgenre": "TMDB seriegenrer", "components.Discover.tmdbtvkeyword": "TMDB Series Nyckelord", "components.Discover.tmdbtvstreamingservices": "TMDB TV-strömningstjänster", @@ -1173,7 +1173,7 @@ "components.Selector.searchKeywords": "Sök nyckelord…", "components.Selector.nooptions": "Inga resultat.", "components.Selector.searchGenres": "Välj genrer…", - "components.Selector.searchStudios": "Sök studior…", + "components.Selector.searchStudios": "Sök studios…", "components.Selector.showmore": "Visa mer", "components.Selector.starttyping": "Börja skriva för att söka.", "components.Settings.SettingsJobsCache.availability-sync": "Synkronisering av mediatillgänglighet", @@ -1183,26 +1183,26 @@ "components.Settings.SettingsMain.csrfProtectionTip": "Ställ in extern API-åtkomst till skrivskyddad (kräver HTTPS)", "components.Settings.SettingsMain.general": "Allmänt", "components.Settings.SettingsMain.generalsettings": "Generella inställningar", - "components.Settings.SettingsMain.generalsettingsDescription": "Konfigurera globala och standard-inställningar för Jellyseerr.", + "components.Settings.SettingsMain.generalsettingsDescription": "Konfigurera globala och standard-inställningar för Overseerr.", "components.Settings.SettingsMain.locale": "Visningsspråk", - "components.Settings.SettingsMain.originallanguage": "Språk för upptäck-fliken", + "components.Settings.SettingsMain.originallanguage": "Upptäck språk", "components.Settings.SettingsMain.originallanguageTip": "Filtrera innehållet efter originalspråk", - "components.Settings.SettingsMain.partialRequestsEnabled": "Tillåt delvisa serieförfrågningar", - "components.Settings.SettingsMain.region": "Region för upptäck-fliken", + "components.Settings.SettingsMain.partialRequestsEnabled": "Tillåt partiella serieförfrågningar", + "components.Settings.SettingsMain.region": "Upptäck region", "components.Settings.SettingsMain.regionTip": "Filtrera innehållet efter regional tillgänglighet", "components.Settings.SettingsMain.toastApiKeyFailure": "Något gick fel när du genererade en ny API-nyckel.", "components.Settings.SettingsMain.toastApiKeySuccess": "Ny API-nyckel genererades framgångsrikt!", - "components.Settings.SettingsMain.trustProxyTip": "Tillåt Jellyseerr att korrekt registrera klienternas IP-adresser bakom en proxy", + "components.Settings.SettingsMain.trustProxyTip": "Tillåt Overseerr att korrekt registrera klienternas IP-adresser bakom en proxy", "components.Discover.resetwarning": "Återställer alla skjutreglage till standardvärdet. Detta raderar också alla anpassade skjutreglage!", "components.Discover.stopediting": "Sluta redigera", "components.Discover.tmdbmoviegenre": "TMDB filmgenre", - "components.Discover.studios": "Studior", + "components.Discover.studios": "Studios", "components.Discover.tmdbmoviekeyword": "TMDB film nyckelord", "components.Discover.tvgenres": "Seriegenrer", - "components.Discover.updatesuccess": "Uppdaterade anpassningsinställningarna för upptäck-fliken.", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Var {jobScheduleSeconds, plural, one {sekund} other {{jobScheduleSeconds} sekund}}", + "components.Discover.updatesuccess": "Uppdaterade inställningar för anpassningar för \"Upptäckt\".", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Varje {jobScheduleSeconds, plural, one {sekund} other {{jobScheduleSeconds} sekunder}}", "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL får inte sluta med ett snedstreck", - "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# aktivt filter} other {# aktiva filter}}", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Aktivt filter} other {# Aktiva filter}}", "components.Discover.DiscoverMovies.sortPopularityDesc": "Popularitet fallande", "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Utgivningsdatum stigande", "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Utgivningsdatum fallande", @@ -1213,28 +1213,28 @@ "components.Discover.DiscoverSliderEdit.deletefail": "Det gick inte att ta bort skjutreglaget.", "components.Discover.DiscoverSliderEdit.deletesuccess": "Skjutreglage har tagits bort.", "components.Discover.DiscoverSliderEdit.remove": "Ta bort", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# aktivt filter} other {# aktiva filter}}", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Aktivt filter} other {# Aktiva filter}}", "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Första sändningsdatum stigande", "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Första sändningsdatum fallande", - "components.Discover.DiscoverTv.sortPopularityAsc": "Popularitet stigande", + "components.Discover.DiscoverTv.sortPopularityAsc": "Populärt stigande", "components.Discover.DiscoverTv.sortPopularityDesc": "Popularitet fallande", "components.Discover.DiscoverTv.sortTitleAsc": "Titel (A-Z) stigande", "components.Discover.DiscoverTv.sortTitleDesc": "Titel (Z-A) fallande", "components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMDB-betyg stigande", "components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMDB-betyg fallande", - "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} serier", - "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# aktivt filter} other {# aktiva filter}}", + "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Serie", + "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Aktivt filter} other {# Aktiva filter}}", "components.Discover.FilterSlideover.keywords": "Nyckelord", "components.Discover.FilterSlideover.originalLanguage": "Originalspråk", "components.Discover.FilterSlideover.ratingText": "Betyg mellan {minValue} och {maxValue}", "components.Discover.FilterSlideover.releaseDate": "Utgivningsdatum", "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} minuter speltid", "components.Discover.FilterSlideover.runtime": "Speltid", - "components.Discover.FilterSlideover.tmdbuserscore": "TMDB användarbetyg", + "components.Discover.FilterSlideover.tmdbuserscore": "TMDB Användarpoäng", "components.Discover.PlexWatchlistSlider.emptywatchlist": "Medier som lagts till i din Plex Watchlist visas här.", - "components.Discover.resetsuccess": "Återställde anpassningsinställningarna för upptäck-fliken.", + "components.Discover.resetsuccess": "Du har lyckats återställa inställningarna för anpassningar av \"Upptäckt\".", "components.Discover.resettodefault": "Återställ till standardinställning", - "components.Discover.updatefailed": "Kunde inte uppdatera anpassningsinställningarna för upptäck-fliken.", + "components.Discover.updatefailed": "Något gick fel när du uppdaterade inställningarna för anpassning av \"Upptäckt\".", "components.DownloadBlock.formattedTitle": "{titel}: Säsong {seasonNumber} Avsnitt{episodeNumber}", "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}", "components.RequestCard.unknowntitle": "Okänd titel", @@ -1245,13 +1245,13 @@ "components.Discover.PlexWatchlistSlider.plexwatchlist": "Din Plex bevakningslista", "components.Discover.RecentlyAddedSlider.recentlyAdded": "Nyligen tillagda", "components.Discover.createnewslider": "Skapa nytt skjutreglage", - "components.Discover.customizediscover": "Anpassa upptäck-fliken", - "components.Discover.resetfailed": "Något gick fel med återställningen av anpassningsinställningar för upptäck-fliken.", + "components.Discover.customizediscover": "Anpassa Upptäck", + "components.Discover.resetfailed": "Något gick fel när du återställde inställningarna för upptäcktsanpassning.", "components.Selector.showless": "Visa mindre", - "components.Discover.CreateSlider.addsuccess": "Skapade ett nytt skjutreglage och sparade anpassningsinställningar för upptäck-fliken.", + "components.Discover.CreateSlider.addsuccess": "Skapade ett nytt skjutreglage och sparade inställningar för anpassningar.", "components.Discover.CreateSlider.editSlider": "Redigera skjutreglage", "components.Discover.CreateSlider.editfail": "Det gick inte att redigera skjutreglaget.", - "components.Discover.CreateSlider.editsuccess": "Redigerade skjutreglaget och sparade anpassningsinställningar för upptäck-fliken.", + "components.Discover.CreateSlider.editsuccess": "Redigerat skjutreglage och sparat anpassningsinställningar för upptäckt.", "components.Discover.CreateSlider.needresults": "Du måste ha minst 1 resultat.", "components.Discover.CreateSlider.providetmdbgenreid": "Ange ett TMDB Genre-ID", "components.Discover.CreateSlider.providetmdbkeywordid": "Ange ett TMDB nyckelords-ID", @@ -1265,15 +1265,81 @@ "components.Discover.FilterSlideover.tmdbuservotecount": "Antal röster från TMDB-användare", "components.Discover.FilterSlideover.voteCount": "Antal röster mellan {minValue} och {maxValue}", "components.Settings.RadarrModal.tagRequests": "Taggförfrågningar", - "components.MovieDetails.imdbuserscore": "IMDB användarpoäng", + "components.MovieDetails.imdbuserscore": "IMDB Användarpoäng", "components.Settings.SonarrModal.tagRequests": "Taggförfrågningar", "components.Settings.RadarrModal.tagRequestsInfo": "Lägg automatiskt till ytterligare tagg med förfrågarens användar-ID och visningsnamn", - "components.Settings.SonarrModal.tagRequestsInfo": "Lägg till en ytterligare tagg med förfrågarens användar-ID och visningsnamn automatiskt", - "i18n.collection": "Samling", - "components.Settings.SonarrModal.animeSeriesType": "Anime-serie typ", - "components.Settings.SonarrModal.seriesType": "Typ av serie", - "components.Settings.Notifications.NotificationsPushover.sound": "Notifikationsljud", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Enhetsstandard", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Notifikationsljud", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Enhetsstandard" + "components.Settings.SonarrModal.tagRequestsInfo": "Lägg automatiskt till ytterligare tagg med förfrågarens användar-ID och visningsnamn", + "i18n.collection": "Kollektion", + "components.Layout.UserWarnings.passwordRequired": "Ett lösenord krävs.", + "components.Login.description": "Eftersom det är första gången du loggar in på {applicationName} måste du lägga till en giltig e-postadress.", + "components.Layout.UserWarnings.emailInvalid": "Ogiltig e-postadress.", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Anslut", + "components.Login.initialsigningin": "Ansluter…", + "components.Login.save": "Lägg till", + "components.Login.saving": "Lägger till…", + "components.Login.signinwithjellyfin": "Använd ditt {mediaServerName}-konto", + "components.Login.title": "Lägg till e-post", + "components.Login.username": "Användarnamn", + "components.Login.validationEmailFormat": "Ogiltig e-postadress", + "components.Login.validationemailformat": "En giltig e-postadress krävs", + "components.Login.validationhostformat": "Giltig URL krävs", + "components.Login.validationhostrequired": "Webbadress för {mediaServerName} krävs", + "components.Login.validationusernamerequired": "Användarnamn krävs", + "components.ManageSlideOver.removearr4k": "Ta bort från 4K {arr}", + "components.MovieDetails.downloadstatus": "Nedladdningsstatus", + "components.MovieDetails.openradarr": "Öppna film i Radarr", + "components.MovieDetails.openradarr4k": "Öppna film i 4K Radarr", + "components.MovieDetails.play4k": "Spela upp i 4K på {mediaServerName}", + "components.Settings.Notifications.NotificationsPushover.sound": "Notifikations Ljud", + "components.Settings.jellyfinsettings": "{mediaServerName} Inställningar", + "components.Settings.internalUrl": "Intern adress", + "components.Settings.jellyfinSettings": "{mediaServerName} Inställningar", + "components.Settings.jellyfinSettingsFailure": "Något gick fel när {mediaServerName} inställningarna sparades.", + "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} inställningarna sparades!", + "components.Settings.jellyfinlibraries": "{mediaServerName} Bibliotek", + "components.Settings.manualscanJellyfin": "Manuell Biblioteks Skanning", + "components.Settings.save": "Spara Ändringar", + "components.Settings.saving": "Sparar…", + "components.Settings.syncing": "Synkroniserar", + "components.Settings.timeout": "Tidsgräns uppnådd", + "components.Setup.signin": "Logga In", + "components.Setup.signinWithPlex": "Använd ditt Jellyfin konto", + "components.TitleCard.addToWatchList": "Lägg till i bevakningslistan", + "components.TitleCard.watchlistCancel": "Bevakningslista för {title} avbruten.", + "components.TitleCard.watchlistError": "Något gick fel, försök igen.", + "components.TitleCard.watchlistSuccess": "{title} lades till i bevakningslistan!", + "components.TvDetails.play4k": "Spela upp i 4K på {mediaServerName}", + "components.UserList.mediaServerUser": "{mediaServerName} Användare", + "components.UserList.noJellyfinuserstoimport": "Det finns inga {mediaServerName} användare att importera.", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Spara Ändringar", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Sparar…", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Notifikations Ljud", + "components.Login.credentialerror": "Användarnamnet eller lösenordet är ogiltigt.", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Detta kommer oåterkalleligt att ta bort denna {mediaType} från {arr}, inklusive alla filer.", + "components.ManageSlideOver.removearr": "Ta bort från {arr}", + "components.Settings.jellyfinsettingsDescription": "Konfigurerar inställningarna för din {mediaServerName} server. {mediaServerName} skannar dina {mediaServerName} bibliotek för att se vilket innehåll som är tillgängligt.", + "components.Settings.SettingsAbout.supportjellyseerr": "Stötta Jellyseerr", + "components.UserList.importfromJellyfin": "Importera {mediaServerName} Användare", + "components.Layout.UserWarnings.emailRequired": "En giltig e-postadress krävs.", + "components.Login.emailtooltip": "Adressen behöver inte vara associerad med din {mediaServerName} instans.", + "components.Login.validationEmailRequired": "Du måste ange ett e-postadress", + "components.Settings.jellyfinlibrariesDescription": "Biblioteket {mediaServerName} scannas efter tittlar. Klicka på knappen nedan om inga bibliotek visas.", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Enhets standard", + "components.Settings.SonarrModal.seriesType": "Serie Typ", + "components.Setup.signinWithJellyfin": "Använd ditt {mediaServerName} konto", + "components.TitleCard.watchlistDeleted": "{title} Har tagits bort från bevakningslistan!", + "components.TvDetails.play": "Spela upp på {mediaServerName}", + "components.UserList.importfromJellyfinerror": "Något gick fel när {mediaServerName} användarna importerades.", + "components.UserList.newJellyfinsigninenabled": "Aktivera Ny {mediaServerName} Inloggning inställningen är aktiverad. {mediaServerName} användare med biblioteks behörighetbehöver inte importeras för att logga in.", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} Användare", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Enhets Standard", + "components.MovieDetails.play": "Spela upp på {mediaServerName}", + "components.Settings.manualscanDescriptionJellyfin": "Normalt sätt kommer detta enbart att köras var 24:e timme. Jellyseerr kommer att kontrollera {mediaServerName} serverns nydligen tillagda oftare. Om det här är första gången du konfigurerar Jellyseerr, rekommenderas en första fullständig manuell skanning.", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.syncJellyfin": "Synkronisera Bibliotek", + "components.Setup.configuremediaserver": "Konfigurera Media Server", + "components.UserProfile.localWatchlist": "{username}'s Bevakningslista", + "components.Settings.Notifications.userEmailRequired": "Kräv användarens e-postadress", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-post" } diff --git a/src/i18n/locale/tr.json b/src/i18n/locale/tr.json new file mode 100644 index 00000000..7c55823e --- /dev/null +++ b/src/i18n/locale/tr.json @@ -0,0 +1,1308 @@ +{ + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "TMBD Derecelendirmesi (Artan)", + "components.Discover.moviegenres": "Film Türleri", + "components.Discover.CreateSlider.editsuccess": "Gösterge düzenlendi ve keşfetin sekmesinin ayarları kaydedildi.", + "components.Discover.FilterSlideover.studio": "Stüdyo", + "components.CollectionDetails.numberofmovies": "{count} Film", + "components.Discover.PlexWatchlistSlider.emptywatchlist": "Plex İzleme Listenize eklenen içerikler burada gözükeceklerdir.", + "components.Discover.CreateSlider.slidernameplaceholder": "Kayan Göstergenin İsmi", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "Yakın Zamanda Eklenenler", + "components.Discover.FilterSlideover.keywords": "Anahtar Kelimeler", + "components.Discover.FilterSlideover.ratingText": "{minValue} ile {maxValue} arasında ki değerlendirmeler", + "components.Discover.DiscoverTv.sortFirstAirDateAsc": "İlk Yayınlanma Tarihi (Yeni)", + "components.AppDataWarning.dockerVolumeMissingDescription": "Bağlanmış {appDataPath} dizini düzgün bir şekilde yapılandırılmamış. Tüm veriler konteyner yeniden başlatıldığında veya durdurulduğunda temizlenecektir.", + "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Dizileri", + "components.Discover.DiscoverMovies.sortPopularityDesc": "Popülerlik (Azalan)", + "components.Discover.StudioSlider.studios": "Stüdyolar", + "components.AirDateBadge.airsrelative": "Yayınlanıyor {relativeTime}", + "components.Discover.DiscoverTv.sortTmdbRatingDesc": "TMBD Derecelendirmesi (Azalan)", + "components.Discover.customizediscover": "Keşfet'i Özelleştir", + "components.Discover.emptywatchlist": "Plex İzleme Listenize eklenen içerikler burada gözükeceklerdir.", + "components.Discover.populartv": "Popüler Diziler", + "components.CollectionDetails.overview": "Özet", + "components.Discover.DiscoverMovies.activefilters": "Etkin Filtre", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "TMBD Derecelendirmesi (Artan)", + "components.Discover.DiscoverTvGenre.genreSeries": "{genre} Dizileri", + "components.AirDateBadge.airedrelative": "Yayınlandı {relativeTime}", + "components.Discover.FilterSlideover.clearfilters": "Etkin Filtreleri Temizle", + "components.Discover.CreateSlider.searchStudios": "Stüdyolarda Ara.…", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Çıkış Tarihi (Eski)", + "components.Discover.CreateSlider.providetmdbnetwork": "TMBD'den TV Ağı etiketi seç", + "components.Discover.MovieGenreSlider.moviegenres": "Film Türleri", + "components.Discover.networks": "TV Ağları", + "components.Discover.CreateSlider.addfail": "Kayan Gösterge oluşturulamadı.", + "components.CollectionDetails.requestcollection": "Koleksiyonu İste", + "components.Discover.TvGenreSlider.tvgenres": "Dizi Türleri", + "components.Discover.DiscoverMovieGenre.genreMovies": "{genre} Filmleri", + "components.Discover.DiscoverMovieLanguage.languageMovies": "{language} Filmler", + "components.Discover.TvGenreList.seriesgenres": "Dizi Türleri", + "components.Discover.DiscoverTv.activefilters": "Etkin Filtre", + "components.Discover.DiscoverTvLanguage.languageSeries": "{language} Diziler", + "components.Discover.DiscoverMovies.sortPopularityAsc": "Popülerlik (Artan)", + "components.Discover.NetworkSlider.networks": "TV Ağları", + "components.Discover.FilterSlideover.streamingservices": "Yayın Hizmetleri", + "components.Discover.CreateSlider.needresults": "Seçtiğin etiketler en az 1 adet sonuç göstermelidir.", + "components.Discover.popularmovies": "Popüler Filmler", + "components.Discover.FilterSlideover.activefilters": "Etkin Filtre", + "components.Discover.plexwatchlist": "İzleme Listeniz", + "components.Discover.CreateSlider.addcustomslider": "Özel Gösterge Oluştur", + "components.Discover.FilterSlideover.tmdbuserscore": "TMBD Kullanıcı Skoru", + "components.Discover.DiscoverTv.sortPopularityAsc": "Popülerlik (Artan)", + "components.Discover.CreateSlider.editSlider": "Kayan Göstergeyi düzenle", + "components.Discover.DiscoverTv.sortTitleAsc": "İsim (A-Z) (Baştan)", + "components.Discover.CreateSlider.validationDatarequired": "Bir veri girdisi sağlamalısın.", + "components.Discover.DiscoverTv.sortFirstAirDateDesc": "İlk Yayınlanma Tarihi (Eski)", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "İzleme Listeniz", + "components.Discover.FilterSlideover.releaseDate": "Çıkış Tarihi", + "components.Discover.DiscoverTv.discovertv": "Diziler", + "components.Discover.recentlyAdded": "Yakın Zamanda Eklenenler", + "components.Discover.DiscoverSliderEdit.deletefail": "Gösterge silinemedi.", + "components.Discover.CreateSlider.providetmdbstudio": "TMBD'den Stüdyo etiketi seç", + "components.Discover.FilterSlideover.runtime": "Yayım Süresi", + "components.Discover.FilterSlideover.from": "Tarihinden", + "components.Discover.DiscoverMovies.sortTitleDesc": "İsim (Z-A) (Sondan)", + "components.Discover.DiscoverStudio.studioMovies": "{studio} Filmleri", + "components.Discover.DiscoverTv.sortPopularityDesc": "Popülerlik (Azalan)", + "components.Discover.CreateSlider.searchGenres": "Türlerde ara…", + "components.Discover.CreateSlider.editfail": "Gösterge düzenlenirken hata oluştu.", + "components.Discover.CreateSlider.starttyping": "Aramak için yazmaya başla.", + "components.Discover.createnewslider": "Yeni Kayan Gösterge Oluştur", + "components.Discover.FilterSlideover.filters": "Filtreler", + "components.Discover.DiscoverWatchlist.watchlist": "Plex İzleme Listen", + "components.Discover.discover": "Keşfet", + "components.Discover.DiscoverSliderEdit.enable": "Görünürlüğü Değiştir", + "components.Discover.CreateSlider.addSlider": "Kayan Gösterge Ekle", + "components.CollectionDetails.requestcollection4k": "Koleksiyonu 4K Kalitesiyle İste", + "components.Discover.FilterSlideover.firstAirDate": "İlk Yayın Tarihi", + "components.Discover.CreateSlider.providetmdbsearch": "Bir arama sorgusu girin", + "components.Discover.DiscoverNetwork.networkSeries": "{network} Dizileri", + "components.Discover.CreateSlider.providetmdbkeywordid": "TMBD'den Anahtar Kelime etiketi seç", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "{keywordTitle} Filmleri", + "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} dakika yayım süresi", + "components.Discover.CreateSlider.validationTitlerequired": "Bir başlık girdisi sağlamalısın.", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "İzleme Listeniz", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Çıkış Tarihi (Yeni)", + "components.Discover.FilterSlideover.genres": "Türler", + "components.Discover.FilterSlideover.originalLanguage": "Orjinal Dili", + "components.Discover.CreateSlider.nooptions": "Sonuç yok.", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "TMBD Derecelendirmesi (Azalan)", + "components.Discover.CreateSlider.searchKeywords": "Anahtar Kelimelerde ara…", + "components.Discover.FilterSlideover.tmdbuservotecount": "TMBD Oy Kullanan Kullanıcı Sayısı", + "components.Discover.CreateSlider.addsuccess": "Yeni kayan gösterge oluşturuldu ve keşfet sekmesinin ayarları kaydedildi.", + "components.Discover.DiscoverSliderEdit.deletesuccess": "Gösterge başarıyla silindi.", + "components.Discover.DiscoverMovies.discovermovies": "Filmler", + "components.Discover.MovieGenreList.moviegenres": "Film Türleri", + "components.Discover.DiscoverMovies.sortTitleAsc": "İsim (A-Z) (Baştan)", + "components.Discover.FilterSlideover.voteCount": "{minValue} ile {maxValue} sayıları arasında oya sahip olanlar", + "components.Discover.CreateSlider.providetmdbgenreid": "TMBD'den Tür etiketi seç", + "components.Discover.DiscoverTv.sortTitleDesc": "İsim (Z-A) (Sondan)", + "components.Discover.recentrequests": "Yakın Vakitteki İstekler", + "components.Discover.DiscoverSliderEdit.remove": "Kaldır", + "components.Discover.FilterSlideover.to": "Tarihine", + "components.Discover.resetfailed": "Beklenmedik bşir hata oluştu, keşfetin ayarları sıfırlanıyor.", + "components.Discover.resetsuccess": "Keşfetin ayarları başarıyla sıfırlandı.", + "components.Discover.resettodefault": "Varsayılan Ayarlara Sıfırla", + "components.Discover.resetwarning": "Tüm göstergeleri varsayılan ayarlara sıfırlar. Bu seçenek eklediğiniz bütün akış göstergelerini de silecektir!", + "components.Discover.stopediting": "Düzenlemeyi Bırak", + "components.Discover.studios": "Stüdyolar", + "components.Discover.tmdbmoviegenre": "TMBD Film Türleri", + "components.Discover.tmdbmoviekeyword": "TMDB Film Anahtar Kelimeleri", + "components.Discover.tmdbmoviestreamingservices": "TMDB Dijital Yayın Platformu Filmleri", + "components.Discover.tmdbnetwork": "TMBD TV Ağları", + "components.Discover.tmdbsearch": "TMBD Arama", + "components.Discover.tmdbstudio": "TMBD Stüdyolar", + "components.Discover.tmdbtvkeyword": "TMBD Dizi Anahtar Kelimeleri", + "components.Discover.tmdbtvstreamingservices": "TMBD Dijital Platform Dizileri", + "components.Discover.trending": "Popüler", + "components.Discover.tvgenres": "Dizi Türleri", + "components.Discover.upcoming": "Gösterime Girecek Filmler", + "components.Discover.upcomingmovies": "Gösterime Girecek Filmler", + "components.Discover.upcomingtv": "Yayınlanacak Diziler", + "components.Discover.updatesuccess": "Keşfet ayarları güncellendi.", + "components.DownloadBlock.estimatedtime": "Tahmini Bitiş: {time}", + "components.DownloadBlock.formattedTitle": "{title}: Sezon {seasonNumber} Bölüm {episodeNumber}", + "components.IssueDetails.IssueComment.delete": "Yorumu Sil", + "components.IssueDetails.IssueComment.edit": "Yorumu Düzenle", + "components.IssueDetails.IssueComment.postedby": "{relativeTime} Tarihinde, {username} tarafından gönderildi", + "components.IssueDetails.IssueComment.postedbyedited": "{relativeTime} Tarihinde, {username} tarafından gönderildi (Düzenlenmiş)", + "components.IssueDetails.IssueComment.validationComment": "Bir mesaj girmelisiniz", + "components.IssueDetails.IssueDescription.deleteissue": "Sorun Bildirimini Sil", + "components.IssueDetails.IssueDescription.description": "Açıklama", + "components.IssueDetails.IssueDescription.edit": "Açıklamayı Düzenle", + "components.IssueDetails.allepisodes": "Tüm Bölümler", + "components.IssueDetails.allseasons": "Tüm Sezonlar", + "components.IssueDetails.closeissue": "Sorun Bildirimini Kapat", + "components.IssueDetails.commentplaceholder": "Yorum ekle…", + "components.IssueDetails.comments": "Yorumlar", + "components.IssueDetails.deleteissue": "Sorun Bildirimini Sil", + "components.IssueDetails.episode": "Bölüm {episodeNumber}", + "components.IssueDetails.issuepagetitle": "Sorun", + "components.IssueDetails.issuetype": "Türü", + "components.IssueDetails.lastupdated": "En Son Güncelleme Zamanı", + "components.IssueDetails.leavecomment": "Yorum Ekle", + "components.IssueDetails.nocomments": "Yorum yok.", + "components.IssueDetails.openin4karr": "{arr} ile 4K'da Aç", + "components.IssueDetails.openinarr": "{arr} ile Aç", + "components.IssueDetails.play4konplex": "{mediaServerName} ile 4K'da Oynat", + "components.IssueDetails.playonplex": "{mediaServerName} ile Oynat", + "components.IssueDetails.problemepisode": "Sorundan Etkilenen Bölümler", + "components.IssueDetails.problemseason": "Sorundan Etkilenen Sezonlar", + "components.IssueDetails.reopenissue": "Sorun Bildirisini Yeniden Aç", + "components.IssueDetails.season": "Sezon {seasonNumber}", + "components.IssueDetails.toasteditdescriptionfailed": "Sorunun açıklamasını düzenlerken beklenmedik bir hata oluştu.", + "components.IssueDetails.toasteditdescriptionsuccess": "Sorunun açıklaması başarıyla düzenlendi!", + "components.IssueDetails.toastissuedeletefailed": "Sorunun açıklaması silinirken beklenmedik bir hata oluştu.", + "components.IssueDetails.toaststatusupdated": "Sorun başarıyla güncellendi!", + "components.IssueDetails.unknownissuetype": "Bilinmiyor", + "components.IssueList.IssueItem.episodes": "{episodeCount} Bölüm", + "components.IssueList.IssueItem.issuestatus": "Durum", + "components.IssueList.IssueItem.issuetype": "Türü", + "components.IssueList.IssueItem.opened": "Açıldı", + "components.IssueList.IssueItem.openeduserdate": "{date}, {user} tarafından", + "components.IssueList.IssueItem.problemepisode": "Sorundan Etkilenen Bölümler", + "components.IssueList.IssueItem.seasons": "{seasonCount} Sezon", + "components.IssueList.IssueItem.unknownissuetype": "Bilinmiyor", + "components.IssueList.IssueItem.viewissue": "Sorunu Gör", + "components.IssueList.issues": "Sorunlar", + "components.IssueList.showallissues": "Tüm Sorunları Göster", + "components.IssueList.sortAdded": "En Son Eklenen", + "components.IssueList.sortModified": "En Son Düzenlenen", + "components.IssueModal.CreateIssueModal.allseasons": "Tüm Sezonlar", + "components.IssueModal.CreateIssueModal.episode": "Bölüm {episodeNumber}", + "components.IssueModal.CreateIssueModal.extras": "Ekstralar", + "components.IssueModal.CreateIssueModal.problemepisode": "Sorundan Etkilenen Bölümler", + "components.IssueModal.CreateIssueModal.problemseason": "Sorundan Etkilenen Sezonlar", + "components.IssueModal.CreateIssueModal.season": "Sezon {seasonNumber}", + "components.IssueModal.CreateIssueModal.submitissue": "Sorunu Bildir", + "components.IssueModal.CreateIssueModal.toastSuccessCreate": "{title} için oluşturduğunuz sorun bildirisi gönderilmiştir!", + "components.IssueModal.CreateIssueModal.toastviewissue": "Sorunu Gör", + "components.IssueModal.CreateIssueModal.validationMessageRequired": "Lütfen açıklamayı doldurunuz", + "components.IssueModal.CreateIssueModal.whatswrong": "Sorun nedir?", + "components.IssueModal.issueAudio": "Ses", + "components.IssueModal.issueOther": "Diğer", + "components.IssueModal.issueSubtitles": "Altyazı", + "components.IssueModal.issueVideo": "Video", + "components.LanguageSelector.languageServerDefault": "Varsayılan ({language})", + "components.LanguageSelector.originalLanguageDefault": "Tüm Diller", + "components.Layout.SearchInput.searchPlaceholder": "Filmlerde & Dizilerde Ara", + "components.Layout.Sidebar.browsemovies": "Filmler", + "components.Layout.Sidebar.browsetv": "Diziler", + "components.Layout.Sidebar.dashboard": "Keşfet", + "components.Layout.Sidebar.issues": "Sorunlar", + "components.Layout.Sidebar.requests": "İstekler", + "components.Layout.Sidebar.settings": "Ayarlar", + "components.Layout.Sidebar.users": "Kullanıcılar", + "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "Film İstekleri", + "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "Dizi İstekleri", + "components.Layout.UserDropdown.myprofile": "Profil", + "components.Layout.UserDropdown.requests": "İstekler", + "components.Layout.UserDropdown.settings": "Ayarlar", + "components.Layout.UserDropdown.signout": "Çıkış Yap", + "components.Layout.UserWarnings.emailRequired": "Bir e-mail adresi gereklidir.", + "components.Layout.UserWarnings.passwordRequired": "Bir şifre girmeniz gerekmektedir.", + "components.Layout.VersionStatus.outofdate": "Eski Sürüm", + "components.Layout.VersionStatus.streamdevelop": "Overseerr Geliştirme Sürümü", + "components.Layout.VersionStatus.streamstable": "Overseerr Stabil Sürüm", + "components.Login.credentialerror": "Kullanıcı adı ya da şifre yanlış.", + "components.Login.description": "{applicationName} uygulamasına ilk defa giriş yaptığınız için geçerli bir e-mail adresi eklemeniz gerekmektedir.", + "components.Login.email": "E-mail Adresi", + "components.Login.forgotpassword": "Şifrenizi mi unuttunuz?", + "components.Login.host": "{mediaServerName} URL'si", + "components.Login.initialsignin": "Bağlan", + "components.Login.initialsigningin": "Bağlanıyor…", + "components.Login.password": "Şifre", + "components.Login.save": "Ekle", + "components.Login.saving": "Ekleniyor…", + "components.Login.signin": "Oturum Aç", + "components.Login.signingin": "Oturum Açılıyor…", + "components.Login.signinwithjellyfin": "{mediaServerName} hesabını kullan", + "components.Login.signinwithoverseerr": "{applicationTitle} hesabını kullan", + "components.Login.signinwithplex": "Plex hesabını kullan", + "components.Login.title": "E-mail Ekle", + "components.Login.username": "Kullanıcı Adı", + "components.Login.validationEmailFormat": "Geçersiz e-mail", + "components.Login.validationemailformat": "Geçerli bir e-mail adresi gereklidir", + "components.Login.validationemailrequired": "Geçerli bir e-mail adresi sağlamalısınız", + "components.Login.validationhostformat": "Geçerli bir URl gereklidir", + "components.Login.validationhostrequired": "{mediaServerName} URL'si gereklidir", + "components.Login.validationusernamerequired": "Kullanıcı Adı girmeniz gereklidir", + "components.ManageSlideOver.alltime": "Tüm Zamanlar", + "components.ManageSlideOver.downloadstatus": "İndirilenler", + "components.ManageSlideOver.manageModalAdvanced": "Gelişmiş", + "components.ManageSlideOver.manageModalClearMedia": "Veriyi Temizle", + "components.Discover.tmdbtvgenre": "TMBD Dizi Türleri", + "components.Discover.updatefailed": "Keşfet ayarları güncellenirken beklenmedik bir hata oluştu.", + "components.IssueDetails.IssueComment.areyousuredelete": "Bu yorumu silmek istediğinize emin misiniz?", + "components.IssueDetails.closeissueandcomment": "Yorum ekleyerek Kapat", + "components.IssueDetails.deleteissueconfirm": "Bu sorun bildirisini silmek istediğinize emin misiniz?", + "components.IssueDetails.openedby": "#{issueId} {relativeTime} tarihinde {username} tarafından açıldı", + "components.IssueDetails.reopenissueandcomment": "Bir Yorumla beraber Yeniden Aç", + "components.IssueDetails.toastissuedeleted": "Sorun Bildirisi başarıyla silindi!", + "components.IssueDetails.toaststatusupdatefailed": "Sorunun açıklaması güncellenirken beklenmedik bir hata oluştu.", + "components.IssueModal.CreateIssueModal.allepisodes": "Tüm Bölümler", + "components.IssueModal.CreateIssueModal.providedetail": "Lütfen karşılaştığınız sorunu detaylı bir biçimde açıklayınız.", + "components.IssueModal.CreateIssueModal.reportissue": "Bir Sorun Bildir", + "components.Layout.LanguagePicker.displaylanguage": "Görüntüleme Dili", + "components.Login.validationpasswordrequired": "Bir şifre girmeniz gereklidir", + "components.IssueModal.CreateIssueModal.toastFailedCreate": "Sorunu bildirirken beklenmedik bir hata oluştu.", + "components.Layout.UserWarnings.emailInvalid": "E-mail adresi geçersiz.", + "components.Login.signinheader": "Devam etmek için oturum aç", + "components.Login.validationEmailRequired": "Bir e-mail adresi girmelisiniz", + "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} behind", + "components.Login.emailtooltip": "E-mail adresi {mediaServerName} uygulamasıyla ilişik olmak zorunda değildir.", + "components.Login.loginerror": "Oturum açarken bir hata oluştu.", + "components.ManageSlideOver.manageModalClearMediaWarning": "* Bu işlem geri döndürülemez bir biçimde {mediaType} ile alakalı her türlü istekte dahil olmak üzere tüm verileri silecektir. Eğer ki bu öğe {mediaServerName} kütüphanenizde bulunuyorsa, tüm medya bilgileri bir sonra ki taramada yeniden oluşturulucaktır.", + "components.ManageSlideOver.manageModalIssues": "Sorun Bildir", + "components.ManageSlideOver.manageModalMedia": "İçerik", + "components.ManageSlideOver.manageModalMedia4k": "4K İçerik", + "components.ManageSlideOver.manageModalNoRequests": "Herhangi bir istek yok.", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Bu işlem geri döndürülemez bir biçimde {arr}'dan {mediaType} öğesini ve dosyalarını kaldıracaktır.", + "components.ManageSlideOver.manageModalRequests": "İstekler", + "components.ManageSlideOver.manageModalTitle": "{mediaType} içeriğini yönet", + "components.ManageSlideOver.markallseasons4kavailable": "Tüm Sezonları 4K'da Oynatılabilir Olarak İşaretle", + "components.ManageSlideOver.mark4kavailable": "4K'da Oynatılabilir Olarak İşaretle", + "components.ManageSlideOver.markallseasonsavailable": "Tüm Sezonları Oynatılabilir Olarak İşaretle", + "components.ManageSlideOver.markavailable": "Oynatılabilir Olarak İşaretle", + "components.ManageSlideOver.movie": "film", + "components.ManageSlideOver.openarr": "{arr}'da Aç", + "components.ManageSlideOver.openarr4k": "{arr}'da 4K Olarak Aç", + "components.ManageSlideOver.opentautulli": "Tautuli'de Aç", + "components.ManageSlideOver.pastdays": "{days, number} Gün Geçti", + "components.ManageSlideOver.playedby": "Tarafından Oynatıldı", + "components.ManageSlideOver.plays": "{playCount, number} oynatma", + "components.ManageSlideOver.removearr": "{arr}'dan Kaldır", + "components.ManageSlideOver.tvshow": "dizi", + "components.MediaSlider.ShowMoreCard.seemore": "Daha Fazla", + "components.MovieDetails.MovieCast.fullcast": "Tüm Kadro", + "components.MovieDetails.MovieCrew.fullcrew": "Tüm Ekip", + "components.MovieDetails.budget": "Bütçe", + "components.MovieDetails.cast": "Kadro", + "components.MovieDetails.digitalrelease": "Dijital Sürüm", + "components.MovieDetails.downloadstatus": "İndirme Durumu", + "components.MovieDetails.managemovie": "Filmi Yönet", + "components.MovieDetails.mark4kavailable": "4K'da Oynatılabilir Olarak İşaretle", + "components.MovieDetails.markavailable": "Oynatılabilir Olarak İşaretle", + "components.MovieDetails.openradarr": "Filmi Radarr'da Aç", + "components.MovieDetails.openradarr4k": "Filmi 4K Radarr'da Aç", + "components.MovieDetails.originallanguage": "Orjinal Dil", + "components.MovieDetails.originaltitle": "Ana Dilindeki Başlık", + "components.MovieDetails.overview": "Özet", + "components.MovieDetails.physicalrelease": "Fiziksel Sürüm", + "components.MovieDetails.play": "İçeriği {mediaServerName} içinde oynat", + "components.MovieDetails.play4k": "İçeriği {mediaServerName} içinde 4K'da oynat", + "components.MovieDetails.recommendations": "Önerilenler", + "components.MovieDetails.releasedate": "{releaseCount} Çıkış Tarihi", + "components.MovieDetails.reportissue": "Bir Sorun Bildir", + "components.MovieDetails.revenue": "Hasılat", + "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes İzleyici Puanı", + "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", + "components.MovieDetails.runtime": "{minutes} dakika", + "components.MovieDetails.showmore": "Daha Fazla Göster", + "components.MovieDetails.showless": "Daha Az Göster", + "components.MovieDetails.similar": "Benzer İçerikler", + "components.MovieDetails.studio": "{studioCount, plural, one {Stüdyo} other {Stüdyolar}}", + "components.MovieDetails.theatricalrelease": "Sinemaya Çıkışı", + "components.MovieDetails.tmdbuserscore": "TMBD Kullanıcı Skoru", + "components.MovieDetails.viewfullcrew": "Tüm Ekibi Gör", + "components.MovieDetails.watchtrailer": "Fragmanı İzle", + "components.NotificationTypeSelector.adminissuereopenedDescription": "Sorun bildirileri kullanıcılar tarafından tekrardan açıldığında bildirim gönder.", + "components.NotificationTypeSelector.adminissueresolvedDescription": "Sorun bildirileri kullanıcılar tarafından çözüldüğünde bildirim gönder.", + "components.NotificationTypeSelector.issuecomment": "Sorun Bildirimi Yorumları", + "components.NotificationTypeSelector.issuecreatedDescription": "Sorunlar bildirildiğinde bildirim gönder.", + "components.NotificationTypeSelector.issuereopened": "Sorun Yeniden Açıldı", + "components.NotificationTypeSelector.issueresolved": "Sorun Çözüldü", + "components.NotificationTypeSelector.issueresolvedDescription": "Sorunlar çözüldüğünde bildirim gönder.", + "components.NotificationTypeSelector.mediaAutoApproved": "İstek Otomatik Olarak İşlendi", + "components.NotificationTypeSelector.mediaAutoApprovedDescription": "Kullanıcıların içerik isteklerinden otomatik olarak kabul edilenler için bildirim gönder.", + "components.NotificationTypeSelector.mediaapproved": "İstek Kabul Edildi", + "components.NotificationTypeSelector.mediaautorequested": "İstek Otomatize Olarak İşleme Alındı", + "components.NotificationTypeSelector.mediaautorequestedDescription": "Kullanıcıların otomatik içerik istekleri eğer ki sizin İzleme Listenizden bir içerik varsa bildirim gönder.", + "components.NotificationTypeSelector.mediaavailable": "İçerik İzlenebilir", + "components.NotificationTypeSelector.mediaavailableDescription": "İçerik izlenebilir olduğunda bildirim gönder.", + "components.NotificationTypeSelector.mediadeclined": "İstek Reddedildi", + "components.NotificationTypeSelector.mediafailed": "İstek İşlemdeyken İptal Oldu", + "components.NotificationTypeSelector.mediafailedDescription": "İçerik istekleri Sonarr'a ya da Radarr'a eklenirken hata oluşursa bildirim gönder.", + "components.NotificationTypeSelector.mediarequested": "İstek Doğrulama Bekliyor", + "components.NotificationTypeSelector.notificationTypes": "Bildirim Türleri", + "components.NotificationTypeSelector.userissuecommentDescription": "Gönderdiğiniz sorun bildirimine yorum geldiğinde bildirim gönder.", + "components.NotificationTypeSelector.userissuecreatedDescription": "Diğer kullanıcılar sorun bildirdiklerinde bildirim gönder.", + "components.NotificationTypeSelector.userissuereopenedDescription": "Gönderdiğiniz sorun bildirimi yeniden açıldığında bildirim gönder.", + "components.NotificationTypeSelector.userissueresolvedDescription": "Gönderdiğiniz sorun bildirimi çözüldüğünde bildirim gönder.", + "components.NotificationTypeSelector.usermediaapprovedDescription": "Gönderdiğiniz içerik isteği kabul edildiğinde bildirim gönder.", + "components.NotificationTypeSelector.usermediaavailableDescription": "Gönderdiğiniz içerik isteği izlenebilir olduğunda bildirim gönder.", + "components.NotificationTypeSelector.usermediadeclinedDescription": "Gönderdiğiniz içerik isteği reddedildiğinde bildirim gönder.", + "components.NotificationTypeSelector.usermediarequestedDescription": "Diğer kullanıcılar doğrulama gerektiren içerik istekleri gönderdiğinde bildirim gönder.", + "components.PermissionEdit.admin": "Yönetici", + "components.PermissionEdit.adminDescription": "Tam yönetici erişimi. Diğer tüm izin denetlemelerini görmezden gelir.", + "components.PermissionEdit.advancedrequest": "Gelişmiş İstekler", + "components.PermissionEdit.advancedrequestDescription": "Gelişmiş istek ayarlarını düzenleyebilme izni ver.", + "components.PermissionEdit.autoapprove": "Otomatik Onay", + "components.PermissionEdit.autoapprove4kMovies": "4K Filmler İçin Otomatik Onay", + "components.PermissionEdit.autoapprove4kMoviesDescription": "4K Film isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autoapprove4kSeries": "4K Diziler İçin Otomatik Onay", + "components.PermissionEdit.autoapprove4kSeriesDescription": "4K Dizi isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autoapproveDescription": "4K olmayan içerik isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autoapproveMovies": "Filmler İçin Otomatik Onay", + "components.PermissionEdit.autoapproveSeries": "Diziler İçin Otomatik Onay", + "components.PermissionEdit.autoapproveSeriesDescription": "4K olmayan dizi isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autorequest": "Otomatize İstek", + "components.PermissionEdit.autorequestMovies": "Filmleri Otomatize İstet", + "components.PermissionEdit.autorequestMoviesDescription": "Kullanıcının Plex İzleme listesinde olan ve 4K olmayan filmler için otomatik olarak istetme iznini ver.", + "components.PermissionEdit.autorequestSeries": "Dizileri Otomatize İstet", + "components.PermissionEdit.autorequestSeriesDescription": "Kullanıcının Plex İzleme listesinde olan ve 4K olmayan diziler için otomatik olarak istetme iznini ver.", + "components.PermissionEdit.createissues": "Sorun Bildirme", + "components.PermissionEdit.manageissues": "Sorunları Yönetme", + "components.PermissionEdit.manageissuesDescription": "İçeriklerde ki sorunları yönetme iznini ver.", + "components.PermissionEdit.managerequests": "İstekleri Yönet", + "components.PermissionEdit.request": "İstek", + "components.PermissionEdit.request4k": "4K'da İstek", + "components.PermissionEdit.request4kDescription": "4K içerik istekleri gönderme iznini ver.", + "components.PermissionEdit.request4kMovies": "4K'da Film İstetme", + "components.PermissionEdit.request4kMoviesDescription": "4K film istekleri gönderme iznini ver.", + "components.PermissionEdit.request4kTv": "4K'da Dizi İstetme", + "components.PermissionEdit.request4kTvDescription": "4K dizi istekleri gönderme iznini ver.", + "components.PermissionEdit.requestDescription": "4K dışında ki içerik isteklerini gönderme iznini ver.", + "components.PermissionEdit.requestMovies": "Film İstetme", + "components.PermissionEdit.requestMoviesDescription": "4K dışında ki film isteklerini gönderme iznini ver.", + "components.PermissionEdit.requestTv": "Dizi İstetme", + "components.PermissionEdit.requestTvDescription": "4K dışında ki dizi isteklerini gönderme iznini ver.", + "components.PermissionEdit.users": "Kullanıcıları Yönetme", + "components.PermissionEdit.viewissues": "Sorunları Görme", + "components.PermissionEdit.viewissuesDescription": "İçeriklerle alakalı dieğr kullanıcıların raporladığı sorunları görme iznini ver.", + "components.PermissionEdit.viewrequests": "İstekleri Görme", + "components.PermissionEdit.viewrequestsDescription": "Diğer kullanıcılar tarafından istenen içerikleri görme iznini ver.", + "components.PermissionEdit.viewwatchlists": "{mediaServerName} İzleme Listelerini Görme", + "components.PersonDetails.alsoknownas": "Nam-ı Diğer: {names}", + "components.PersonDetails.appearsin": "Yer Aldığı İçerikler", + "components.PersonDetails.ascharacter": "{character} rolüyle", + "components.PersonDetails.birthdate": "Doğumu {birthdate}", + "components.PersonDetails.crewmember": "Ekibi", + "components.PersonDetails.lifespan": "{birthdate} – {deathdate}", + "components.PlexLoginButton.signingin": "Giriş Yapılıyor…", + "components.PlexLoginButton.signinwithplex": "Giriş Yap", + "components.QuotaSelector.days": "{count} gün", + "components.QuotaSelector.movies": "{count} film", + "components.QuotaSelector.seasons": "{count} sezon", + "components.QuotaSelector.tvRequests": "{quotaLimit} {sezonlar} per {quotaDays} {gün}", + "components.QuotaSelector.unlimited": "Sınırsız", + "components.RegionSelector.regionDefault": "Tüm Bölgeler", + "components.RegionSelector.regionServerDefault": "Varsayılan ({region})", + "components.RequestBlock.approve": "İsteği Kabul Et", + "components.RequestBlock.decline": "İsteği Reddet", + "components.RequestBlock.delete": "İsteği Sil", + "components.RequestBlock.edit": "İsteği Düzenle", + "components.RequestBlock.languageprofile": "Dil Seçeneği", + "components.RequestBlock.profilechanged": "Kalite Seçeneği", + "components.RequestBlock.requestdate": "İsteme Tarihi", + "components.RequestBlock.requestedby": "İsteyen", + "components.RequestBlock.requestoverrides": "Geçersiz İstekler", + "components.RequestBlock.rootfolder": "Ana Klasör", + "components.RequestBlock.seasons": "{seasonCount, plural, one {Sezon} other {Sezonlar}}", + "components.RequestBlock.server": "Varış Sunucusu", + "components.RequestButton.approverequest": "İsteği Kabul Et", + "components.RequestButton.approverequest4k": "4K İsteği Kabul Et", + "components.RequestButton.approverequests": "{requestCount, plural, one {İsteği} Kabul Et other {{requestCount} Requests}}", + "components.RequestButton.declinerequest": "İsteği Reddet", + "components.RequestButton.declinerequest4k": "4K İsteği Reddet", + "components.RequestButton.declinerequests": "{requestCount, plural, one {İsteği} Reddet other {{requestCount} Requests}}", + "components.RequestButton.requestmore4k": "4K'da Daha Fazla İste", + "components.RequestButton.viewrequest": "İstekleri Gör", + "components.RequestButton.viewrequest4k": "4K İstekleri Gör", + "components.RequestCard.approverequest": "İsteği Kabul Et", + "components.RequestCard.cancelrequest": "İsteği İptal Et", + "components.RequestCard.declinerequest": "İsteği Reddet", + "components.RequestCard.deleterequest": "İsteği Sil", + "components.RequestCard.failedretry": "Tekrardan istek gönderirken bir hata oluştu.", + "components.RequestCard.mediaerror": "{mediaType} Bulunamadı", + "components.RequestCard.seasons": "{seasonCount} Sezon", + "components.RequestCard.tmdbid": "TMDB ID'si", + "components.RequestCard.tvdbid": "TheTVDB ID'si", + "components.RequestCard.unknowntitle": "Bilinmeyen İçerik", + "components.RequestList.RequestItem.deleterequest": "İsteği Sil", + "components.RequestList.RequestItem.editrequest": "İsteği Düzenle", + "components.RequestList.RequestItem.failedretry": "Tekrardan istek gönderirken bir hata oluştu.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} Bulunamadı", + "components.RequestList.RequestItem.modified": "Düzenlendi", + "components.RequestList.RequestItem.modifieduserdate": "{user} tarafından {date} vaktinde", + "components.RequestList.RequestItem.requested": "İstetildi", + "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Sezon} other {Sezonlar}}", + "components.RequestList.RequestItem.tmdbid": "TMDB ID'si", + "components.RequestList.RequestItem.tvdbid": "TheTVDB ID'si", + "components.RequestList.RequestItem.unknowntitle": "Bilinmeyen İçerik", + "components.RequestList.requests": "İstekler", + "components.RequestList.showallrequests": "Tüm İstekleri Göster", + "components.RequestList.sortAdded": "En Son Eklenen", + "components.RequestModal.AdvancedRequester.advancedoptions": "Gelişmiş", + "components.RequestModal.AdvancedRequester.animenote": "* Bu dizi bir animedir.", + "components.RequestModal.AdvancedRequester.default": "{name} (Varsayılan)", + "components.RequestModal.AdvancedRequester.destinationserver": "Varış Sunucusu", + "components.RequestModal.AdvancedRequester.folder": "{path} ({space})", + "components.RequestModal.AdvancedRequester.languageprofile": "Dil Seçeneği", + "components.RequestModal.AdvancedRequester.notagoptions": "Etiket yok.", + "components.RequestModal.AdvancedRequester.qualityprofile": "Kalite Seçeneği", + "components.RequestModal.AdvancedRequester.rootfolder": "Ana Klasör", + "components.RequestModal.AdvancedRequester.selecttags": "Etiketleri seç", + "components.RequestModal.AdvancedRequester.tags": "Etiketler", + "components.RequestModal.QuotaDisplay.movie": "film", + "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {film} other {filmler}}", + "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Sezon isteteme limitiniz yetmemektedir", + "components.RequestModal.QuotaDisplay.quotaLink": "Profil sayfanızdan istetme limitiniz hakkında ki bilgileri görebilirsiniz.", + "components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {No} other {#}} {type} istek kaldı", + "components.RequestModal.QuotaDisplay.requiredquotaUser": "Bu kullanıcının bu dizinin {sezonlarını} istetebilmek için en azından {seasons, plural, one {sezon isteği} other {sezon istekleri}} kadar istek göndermesi gerekiyor.", + "components.RequestModal.QuotaDisplay.season": "sezonlar", + "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {sezon} other {sezonlar}}", + "components.RequestModal.SearchByNameModal.nomatches": "Bu dizi için uygun bir eşleşme bulamadık.", + "components.RequestModal.SearchByNameModal.notvdbiddescription": "Bu diziyi otomatik olarak eşleştiremedik, lütfen aşağıdan bir eşleşme seçin.", + "components.RequestModal.alreadyrequested": "Zaten İstenildi", + "components.RequestModal.approve": "İsteği Kabul Et", + "components.RequestModal.autoapproval": "Otomatik Kabul", + "components.RequestModal.cancel": "İsteği İptal Et", + "components.RequestModal.edit": "İsteği Düzenle", + "components.RequestModal.errorediting": "İstek düzenlenirken beklenmedik bir hatayla karşılaşıldı.", + "components.RequestModal.extras": "Ekstralar", + "components.RequestModal.numberofepisodes": "# Bölümleri", + "components.RequestModal.pending4krequest": "4K İstek Beklemede", + "components.RequestModal.pendingapproval": "Gönderdiğiniz istek onaylama aşamasında.", + "components.RequestModal.pendingrequest": "İstek Beklemede", + "components.RequestModal.requestCancel": "{title} için gönderdiğiniz istek reddedildi.", + "components.RequestModal.requestSuccess": "{title} isteği başarıyla gönderildi!", + "components.RequestModal.requestcancelled": "{title} için gönderdiğiniz istek reddedildi.", + "components.RequestModal.requestcollection4ktitle": "Koleksiyonu 4K Kalitesiyle İste", + "components.RequestModal.requestcollectiontitle": "Koleksiyonu İste", + "components.RequestModal.requestedited": "{title} için gönderilen istek başarıyla düzenlendi!", + "components.RequestModal.requestfrom": "{username} kullanıcısının isteği onay bekliyor.", + "components.RequestModal.requestmovie4ktitle": "Filmi 4K'da İste", + "components.RequestModal.requestmovies": "{count} Filmi İste", + "components.RequestModal.requestmovietitle": "Filmi İste", + "components.RequestModal.requestseasons4k": "{seasonCount} Sezonu 4K'da İste", + "components.RequestModal.requestseries4ktitle": "Diziyi 4K'da İste", + "components.RequestModal.requestseriestitle": "Diziyi İste", + "components.RequestModal.season": "Sezon", + "components.RequestModal.seasonnumber": "{number}. Sezon", + "components.RequestModal.selectmovies": "Film(leri) Seç", + "components.RequestModal.selectseason": "Dizi(leri) Seç", + "components.ResetPassword.confirmpassword": "Şifreyi Doğrula", + "components.ResetPassword.email": "E-mail Adresi", + "components.ResetPassword.emailresetlink": "E-mail Kurtarma Linki", + "components.ResetPassword.password": "Şifre", + "components.ResetPassword.passwordreset": "Şifreyi Sıfırla", + "components.ResetPassword.resetpassword": "Şifrenizi sıfırlayın", + "components.ResetPassword.resetpasswordsuccessmessage": "Şifre sıfırlama başarılı!", + "components.ResetPassword.validationemailrequired": "Geçerli bir e-mail adresi sağlamalısınız", + "components.ResetPassword.validationpasswordmatch": "Girdiğiniz şifreler uyuşmuyor", + "components.ResetPassword.validationpasswordminchars": "Girdiğiniz şifre çok kısa, en az 8 karakterden oluşmalıdır", + "components.ResetPassword.validationpasswordrequired": "Bir şifre girmeniz gereklidir", + "components.Search.search": "Ara", + "components.Search.searchresults": "Arama Sonuçları", + "components.Selector.nooptions": "Sonuç yok.", + "components.Selector.searchGenres": "Türleri seç…", + "components.Selector.searchKeywords": "Anahtar kelimeleri arat…", + "components.Selector.searchStudios": "Stüdyolarda ara…", + "components.Selector.showless": "Daha Az Göster", + "components.Selector.showmore": "Daha Fazla Göster", + "components.Selector.starttyping": "Aramak için yazmaya başla.", + "components.Settings.Notifications.NotificationsGotify.gotifysettingsfailed": "Gotify bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsGotify.gotifysettingssaved": "Gotify bildirim ayarları kaydedildi!", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": "Gotify deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSuccess": "Gotify deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsGotify.token": "Uygulama Token'i", + "components.Settings.Notifications.NotificationsGotify.url": "Sunucu URL'si", + "components.Settings.Notifications.NotificationsGotify.validationTokenRequired": "Bir uygulama Token'i girmeniz gerekmektedir", + "components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "Geçerli bir URL girmelisiniz", + "components.ManageSlideOver.removearr4k": "4K {arr}'dan Kaldır", + "components.NotificationTypeSelector.adminissuecommentDescription": "Diğer kullanıcılar sorun bildirilerine yorum yaptıklarında bildirim gönder.", + "components.NotificationTypeSelector.mediadeclinedDescription": "İstek reddedildiğinde bildirim gönder.", + "components.PermissionEdit.createissuesDescription": "İçeriklerde ki sorunları bildirme iznini ver.", + "components.PermissionEdit.viewrecentDescription": "Yakın zamanda eklenen içerikleri görme iznini ver.", + "components.RequestBlock.lastmodifiedby": "En Son Düzenlenleyen", + "components.RequestButton.decline4krequests": "{requestCount, plural, one {4K İsteği} Reddet other {{requestCount} 4K Requests}}", + "components.RequestButton.requestmore": "Daha Fazla İste", + "components.RequestCard.editrequest": "İsteği Düzenle", + "components.RequestModal.requestApproved": "{title} için gönderdiğiniz istek onaylandı!", + "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "Gotify deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsGotify.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.MovieDetails.imdbuserscore": "IMDB Kullanıcı Puanı", + "components.MovieDetails.overviewunavailable": "Özet mevcut değil.", + "components.MovieDetails.productioncountries": "Yapımcı {countryCount} Ülke", + "components.MovieDetails.streamingproviders": "İçertiğin Erişilebilir Olduğu Platformlar", + "components.NotificationTypeSelector.issuecommentDescription": "Sorun bildirilerine yeni yorum geldiğinde bildirim gönder.", + "components.NotificationTypeSelector.issuecreated": "Sorun Bildirildi", + "components.NotificationTypeSelector.issuereopenedDescription": "Sorunlar yeniden açıldığında bildirim gönder.", + "components.NotificationTypeSelector.mediaapprovedDescription": "Kullanıcıların içerik istekleri elle kabul edildiğinde bildirim gönder.", + "components.NotificationTypeSelector.mediarequestedDescription": "Kullanıcılar doğrulama gerektiren içerik istekleri gönderdiğinde bildirim gönder.", + "components.NotificationTypeSelector.usermediaAutoApprovedDescription": "Diğer kullanıcılar otomatize içerik istekleri gönderdiğinde bildirim gönder.", + "components.NotificationTypeSelector.usermediafailedDescription": "Gönderdiğiniz içerik isteği Sonarr ya da Radarr'a eklenemediğinde bildirim gönder.", + "components.PermissionEdit.autoapprove4k": "4K İçin Otomatik Onay", + "components.PermissionEdit.autoapprove4kDescription": "4K İçerik isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autoapproveMoviesDescription": "4K olmayan film isteklerinin otomatik onaylanması iznini ver.", + "components.PermissionEdit.autorequestDescription": "Kullanıcının Plex İzleme listesinde olan içerikler için otomatik olarak istetme iznini ver.", + "components.PermissionEdit.managerequestsDescription": "İçerik isteklerini yönetme iznini ver. Bu izne sahip kullanıcının gönderdiği tüm istekler otomatik olarak kabul edilir.", + "components.PermissionEdit.viewrecent": "Yakın Zamanda Eklenenleri Görme", + "components.RequestModal.AdvancedRequester.requestas": "Şu Mahlasla İstet:", + "components.RequestModal.QuotaDisplay.allowedRequests": "{limit} {type} adet istetme limitin var, her {days} günde bir bu limit yenilenecektir.", + "components.RequestModal.QuotaDisplay.allowedRequestsUser": "Bu kullanıcının {limit} {type} adet istetme limiti var, her {days} günde bir bu limiti yenilenecektir.", + "components.PermissionEdit.usersDescription": "Kullanıcıları yönetme iznini ver. Bu izne sahip kullanıcılar Yönetici yetkisine sahip kişileri düzenleyemezler ya da Yönetici yetkisi veremezler.", + "components.PermissionEdit.viewwatchlistsDescription": "Diğer kullanıcıların {mediaServerName} İzleme Listelerini görme iznini ver.", + "components.RequestList.RequestItem.cancelRequest": "İsteği İptal Et", + "components.RequestList.sortModified": "En Son Düzenlenen", + "components.RequestModal.QuotaDisplay.quotaLinkUser": "Kullanıcıların profil sayfasından istetme limitleri hakkında ki bilgileri görebilirsiniz.", + "components.RequestModal.requesterror": "İsteği gönderirken beklenmedik bir hata oluştu.", + "components.RequestModal.requestseasons": "{seasonCount} Sezonu İste", + "components.Settings.Notifications.NotificationsGotify.agentenabled": "Köprüyü Aktif Et", + "components.QuotaSelector.movieRequests": "{quotaLimit} {filmler} per {quotaDays} {gün}", + "components.RequestList.RequestItem.requesteddate": "İstetildi", + "components.RequestModal.requestadmin": "Bu istek otomatik olarak onaylanacaktır.", + "components.RequestButton.approve4krequests": "{requestCount, plural, one {4K İsteği} Kabul Et other {{requestCount} 4K Requests}}", + "components.RequestModal.QuotaDisplay.requiredquota": "Bu dizinin {sezonlarını} istetebilmek için en azından {seasons, plural, one {sezon isteği} other {sezon istekleri}} kadar istek göndermeniz gerekiyor.", + "components.RequestModal.requestmovies4k": "{count} Filmi 4K'da İste", + "components.ResetPassword.gobacklogin": "Oturum Açma Sayfasına Dön", + "components.ResetPassword.requestresetlinksuccessmessage": "Eğer ki girilen e-mail adresi gerçek bir kişiyle ilişkiliyse şifre sıfırlama linki gönderilecektir.", + "components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "URL'nizin sonunda slash (eğik çizgi) olmamalıdır", + "components.Settings.Notifications.NotificationsLunaSea.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsLunaSea.profileName": "Profil İsmi", + "components.Settings.Notifications.NotificationsLunaSea.profileNameTip": "Eğer ki varsayılan profil kullanılmıyorsa gereklidir", + "components.Settings.Notifications.NotificationsLunaSea.settingsFailed": "LunaSea bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestFailed": "LunaSea deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSending": "LunaSea deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsLunaSea.toastLunaSeaTestSuccess": "LunaSea deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsLunaSea.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.NotificationsLunaSea.validationWebhookUrl": "Geçerli bir URL girmelisiniz", + "components.Settings.Notifications.NotificationsLunaSea.webhookUrl": "Webhook URL'si", + "components.Settings.Notifications.NotificationsPushbullet.accessToken": "Erişim Token'i", + "components.Settings.Notifications.NotificationsPushbullet.accessTokenTip": "Hesap Ayarlarınızdan bir token oluşturun", + "components.Settings.Notifications.NotificationsPushbullet.agentEnabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsPushbullet.channelTag": "Kanal Etiketi", + "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsFailed": "Pushbullet bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsSaved": "Pushbullet bildirim ayarları kaydedildi!", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSending": "Pushbullet deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestSuccess": "Pushbullet deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsPushbullet.validationAccessTokenRequired": "Bir erişim token'i sağlamalısınız", + "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.NotificationsPushover.accessToken": "Uygulama API'sinin Token'i", + "components.Settings.Notifications.NotificationsLunaSea.settingsSaved": "LunaSea bildirim ayarları kaydedildi!", + "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": "Pushbullet deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsLunaSea.webhookUrlTip": "Kullanıcı ya da cihaz tabanlı webhook bildirimi URL'niz", + "components.Settings.RadarrModal.released": "Çıkış Yaptı", + "components.Settings.RadarrModal.rootfolder": "Ana Klasör", + "components.Settings.Notifications.NotificationsPushover.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Cihaz Varsayılanı", + "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": "Pushover bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "Pushover bildirim ayarları kaydedildi!", + "components.Settings.Notifications.NotificationsPushover.sound": "Bildirim Sesi", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestFailed": "Pushover deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Overseerr ile kullanmanız için uygulamanızı kaydedin", + "components.Settings.SettingsAbout.helppaycoffee": "Bize Bir Kahve Ismarla", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSuccess": "Pushover deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsPushover.validationAccessTokenRequired": "Geçerli bir uygulama token'i sağlamalısınız", + "components.Settings.Notifications.NotificationsPushover.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.NotificationsPushover.userToken": "Kullanıcı ya da Grup Kimliği", + "components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "Geçerli bir kullanıcı ya da grup kimliği sağlamalısınız", + "components.Settings.Notifications.NotificationsSlack.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": "Slack bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": "Slacxk deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestSending": "Slack deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsSlack.toastSlackTestSuccess": "Slack deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsSlack.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.NotificationsSlack.validationWebhookUrl": "Geçerli bir URL girmelisiniz", + "components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook URL'si", + "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "Web'den bildirim gönderilebilmesi için Overseerr'ın HTTPS (SSL) olarak erişiliyor olması lazımdır.", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Web deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Web deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingssaved": "Web bildirim ayarları kaydedildi!", + "components.Settings.Notifications.NotificationsWebhook.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.NotificationsWebhook.authheader": "Yetkilendirme Üstbilgisi (Header'ı)", + "components.Settings.Notifications.NotificationsWebhook.customJson": "JSON Payload'I", + "components.Settings.Notifications.NotificationsWebhook.resetPayload": "Varsayılan Ayarlara Sıfırla", + "components.Settings.Notifications.NotificationsWebhook.resetPayloadSuccess": "JSON Payload'ı başarıyla sıfırlandı!", + "components.Settings.Notifications.NotificationsWebhook.templatevariablehelp": "Şablon Değişkeni İçin Yardım", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSending": "Webhook deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestSuccess": "Webhook deneme bildirimi gönderildi!", + "components.Settings.Notifications.NotificationsWebhook.validationJsonPayloadRequired": "Geçerli bir JSON Payload'ı sağlamalısınız", + "components.Settings.Notifications.NotificationsWebhook.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.NotificationsWebhook.webhookUrl": "Webhook URL'si", + "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Webhook bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Webhook bildirim ayarları kaydedildi!", + "components.Settings.Notifications.agentenabled": "Köprüyü Aktif Et", + "components.Settings.Notifications.allowselfsigned": "Kendine İmzalı Sertifikaları Kabul Et", + "components.Settings.Notifications.authPass": "SMPT Şifresi", + "components.Settings.Notifications.botAPI": "Bot Yetkilendirme Token'i", + "components.Settings.Notifications.botApiTip": "Overseerr ile kullanmak için bir Bot oluşturun", + "components.Settings.Notifications.botAvatarUrl": "Bot Profil Resmi URL'si", + "components.Settings.Notifications.botUsername": "Bot'un Kullanıcı Adı", + "components.Settings.Notifications.chatId": "Sohbet ID'si", + "components.Settings.Notifications.chatIdTip": "Botunuzla sohbet etmek için @get_id_bot isimiyle ekleyin, /my_id komutunu kullanın ve sohbeti başlatın", + "components.Settings.Notifications.discordsettingsfailed": "Discord bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.emailsender": "Gönderen Adresi", + "components.Settings.Notifications.emailsettingsfailed": "E-mail bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.emailsettingssaved": "E-mail bildirim ayarları kaydedildi!", + "components.Settings.Notifications.enableMentions": "Bahsetmeleri Etkinleştirin", + "components.Settings.Notifications.encryption": "Şifreleme Yöntemi", + "components.Settings.Notifications.encryptionDefault": "Mümkünse STARTTLS kullan", + "components.Settings.Notifications.encryptionImplicitTls": "Örtülü (Implict) TLS Kullan", + "components.Settings.Notifications.encryptionNone": "Hiçbiri", + "components.Settings.Notifications.encryptionTip": "Genel olarak Örtülü (Implict) TLS 465 numaralı portu kullanır, STARTTLS ise 587 numaralı portu kullanır", + "components.Settings.Notifications.pgpPassword": "PGP Şifresi", + "components.Settings.Notifications.pgpPrivateKey": "PGP Gizli Anahtar", + "components.Settings.Notifications.pgpPrivateKeyTip": "OpenPGP kullanarak e-mailleri imzala ve şifrele", + "components.Settings.Notifications.sendSilently": "Sessizce Gönder", + "components.Settings.Notifications.sendSilentlyTip": "Ses olmadan bildirim gönder", + "components.Settings.Notifications.senderName": "Gönderen İsmi", + "components.Settings.Notifications.smtpHost": "SMPT Sunucu Adresi", + "components.Settings.Notifications.smtpPort": "SMPT Port'u", + "components.Settings.Notifications.telegramsettingsfailed": "Telegram bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.telegramsettingssaved": "Telegram bildirim ayarları kaydedildi!", + "components.Settings.Notifications.toastDiscordTestSuccess": "Discord deneme bildirimi gönderildi!", + "components.Settings.Notifications.toastEmailTestFailed": "E-mail deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.toastEmailTestSuccess": "E-mail deneme bildirimi gönderildi!", + "components.Settings.Notifications.toastTelegramTestFailed": "Telegram test bildirimi gönderilemedi.", + "components.Settings.Notifications.toastTelegramTestSending": "Telegram deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.toastTelegramTestSuccess": "Telegram deneme bildirimi gönderildi!", + "components.Settings.Notifications.userEmailRequired": "Kullanıcı e-mail'ini zorunlu tut", + "components.Settings.Notifications.validationChatIdRequired": "Geçerli bir sohbet ID'si sağlamalısın", + "components.Settings.Notifications.validationEmail": "Geçerli bir e-mail adresi sağlamalısın", + "components.Settings.Notifications.validationPgpPassword": "Bir PGP şifresi sağlamalısın", + "components.Settings.Notifications.validationPgpPrivateKey": "Geçerli bir PGP gizli anahtarı sağlamalısın", + "components.Settings.Notifications.validationSmtpPortRequired": "Geçerli bir port numarası girmelisin", + "components.Settings.Notifications.validationTypes": "En azından bir adet bildirim türü seçmelisiniz", + "components.Settings.Notifications.validationUrl": "Geçerli bir URL girmelisiniz", + "components.Settings.Notifications.webhookUrl": "Webhook URL'si", + "components.Settings.RadarrModal.add": "Sunucu Ekle", + "components.Settings.RadarrModal.announced": "Duyurulmuş", + "components.Settings.RadarrModal.apiKey": "API Anahtarı", + "components.Settings.RadarrModal.baseUrl": "Temel URL Adresi", + "components.Settings.RadarrModal.create4kradarr": "Yeni Bir 4K Radarr Sunucusu Ekle", + "components.Settings.RadarrModal.createradarr": "Yeni Bir Radarr Sunucusu Ekle", + "components.Settings.RadarrModal.default4kserver": "Varsayılan 4K Sunucu", + "components.Settings.RadarrModal.defaultserver": "Varsayılan Sunucu", + "components.Settings.RadarrModal.edit4kradarr": "4K Radarr Sunucusunu Düzenle", + "components.Settings.RadarrModal.editradarr": "Radarr Sunucusunu Düzenle", + "components.Settings.RadarrModal.enableSearch": "Otomatik Aramayı Etkinleştir", + "components.Settings.RadarrModal.externalUrl": "Harici URL", + "components.Settings.RadarrModal.hostname": "Domain ya da IP Adresi", + "components.Settings.RadarrModal.inCinemas": "Sinemalarda", + "components.Settings.RadarrModal.loadingTags": "Etiketler yükleniyor…", + "components.Settings.RadarrModal.loadingrootfolders": "Ana klasör yükleniyor…", + "components.Settings.RadarrModal.minimumAvailability": "Asgari Erişilebilirlik Ayarı", + "components.Settings.RadarrModal.notagoptions": "Etiket yok.", + "components.Settings.RadarrModal.port": "Port", + "components.Settings.RadarrModal.qualityprofile": "Kalite Seçeneği", + "components.Settings.RadarrModal.selectQualityProfile": "Kaliteyi seç", + "components.Settings.RadarrModal.selectRootFolder": "Ana klasörü seç", + "components.Settings.RadarrModal.selecttags": "Etiketleri seç", + "components.Settings.RadarrModal.server4k": "4K Sunucu", + "components.Settings.RadarrModal.servername": "Sunucu İsmi", + "components.Settings.RadarrModal.ssl": "SSL Kullan", + "components.Settings.RadarrModal.syncEnabled": "Taramayı Etkinleştir", + "components.Settings.RadarrModal.tagRequests": "Etiket İstekleri", + "components.Settings.RadarrModal.tags": "Etiketler", + "components.Settings.RadarrModal.testFirstQualityProfiles": "Kalite seçeneklerini yüklemek için bağlantıyı test et", + "components.Settings.RadarrModal.testFirstRootFolders": "Ana klasörleri yüklemek için bağlantıyı test et", + "components.Settings.RadarrModal.testFirstTags": "Etiketleri yüklemek için bağlantıyı test et", + "components.Settings.RadarrModal.toastRadarrTestFailure": "Radarr'a bağlanılamadı.", + "components.Settings.RadarrModal.toastRadarrTestSuccess": "Radarr'a başarıyla bağlanıldı!", + "components.Settings.RadarrModal.validationApplicationUrl": "Geçerli bir URL adresi girmelisiniz", + "components.Settings.RadarrModal.validationApplicationUrlTrailingSlash": "URL'nizin sonunda slash (eğik çizgi) olmamalıdır", + "components.Settings.RadarrModal.validationBaseUrlLeadingSlash": "URL'nizin başında slash (eğik çizgi) olmalıdır", + "components.Settings.RadarrModal.validationBaseUrlTrailingSlash": "Temel URL slash (eğik çizgi) ile bitmemelidir", + "components.Settings.RadarrModal.validationMinimumAvailabilityRequired": "Asgari erişilebilirlik ayarı seçmelisin", + "components.Settings.RadarrModal.validationNameRequired": "Bir sunucu ismi girmelisin", + "components.Settings.RadarrModal.validationPortRequired": "Geçerli bir port adresi girmelisin", + "components.Settings.RadarrModal.validationProfileRequired": "Bir kalite seçeneği girmelisin", + "components.Settings.RadarrModal.validationRootFolderRequired": "Bir ana klasör seçmelisin", + "components.Settings.SettingsAbout.Releases.currentversion": "Şimdiki", + "components.Settings.SettingsAbout.Releases.latestversion": "En Son", + "components.Settings.SettingsAbout.Releases.releases": "Piyasaya Çıkışlar", + "components.Settings.SettingsAbout.Releases.versionChangelog": "{version} Sürüm Notları", + "components.Settings.SettingsAbout.Releases.viewchangelog": "Sürüm Notlarını Gör", + "components.Settings.SettingsAbout.Releases.viewongithub": "Github'da Görüntüle", + "components.Settings.SettingsAbout.about": "Hakkında", + "components.Settings.SettingsAbout.appDataPath": "Veri Dizini", + "components.Settings.SettingsAbout.documentation": "Dökümantasyon", + "components.Settings.SettingsAbout.gettingsupport": "Destek Al", + "components.Settings.SettingsAbout.githubdiscussions": "GitHub Tartışmaları", + "components.Settings.SettingsAbout.preferredmethod": "Tercih Edilen", + "components.Settings.SettingsAbout.supportoverseerr": "Overseerr'ı Destekle", + "components.Settings.SettingsAbout.supportjellyseerr": "Jellyseerr'ı Destekle", + "components.Settings.SettingsAbout.timezone": "Zaman Dilimi", + "components.Settings.SettingsAbout.totalmedia": "Toplam İçerik", + "components.Settings.SettingsAbout.totalrequests": "Toplam İstek", + "components.Settings.SettingsAbout.uptodate": "Güncel", + "components.Settings.SettingsAbout.version": "Sürüm", + "components.Settings.SettingsJobsCache.availability-sync": "İçerik Kullanılabilirliğini Eşitle", + "components.Settings.SettingsJobsCache.cache": "Önbelleğe Al", + "components.Settings.SettingsJobsCache.cacheDescription": "Overseer harici API'dan gelen istekleri önbelleğe alır, böylece performans optimize edilir ve gereksiz yere API'a istek atılmaz.", + "components.Settings.SettingsJobsCache.cacheflushed": "{cachename} önbelleği temizlendi.", + "components.Settings.SettingsJobsCache.cachehits": "Önbellek İsabet Yüzdesi", + "components.Settings.SettingsJobsCache.cachekeys": "Toplam Anahtar", + "components.Settings.SettingsJobsCache.cacheksize": "Anahtar Boyutu", + "components.Settings.SettingsJobsCache.cachemisses": "Önbelleğin Kaçırma Yüzdesi", + "components.Settings.SettingsJobsCache.cachename": "Önbellek İsmi", + "components.Settings.SettingsJobsCache.canceljob": "İşlemi Durdur", + "components.Settings.SettingsJobsCache.command": "Komutlar", + "components.Settings.SettingsJobsCache.download-sync": "İndirilenleri Eşitle", + "components.Settings.SettingsJobsCache.download-sync-reset": "İndirilenler Eşitlemesini Sıfırla", + "components.Settings.SettingsJobsCache.editJobSchedule": "İşlemi Düzenle", + "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Mevcut Frekans", + "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Yeni Frekans", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Her {jobScheduleMinutes, plural, one {dakikada} other {{jobScheduleMinutes} minutes}}", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Her {jobScheduleSeconds, plural, one {saniyede} other {{jobScheduleSeconds} seconds}}", + "components.Settings.SettingsJobsCache.flushcache": "Önbelleği Temizle", + "components.Settings.SettingsJobsCache.image-cache-cleanup": "Resim Önbelleği Temizlemesi", + "components.Settings.SettingsJobsCache.imagecache": "Resim Önbelleği", + "components.Settings.SettingsJobsCache.imagecachecount": "Resimler Önbelleğe Alındı", + "components.Settings.SettingsJobsCache.imagecachesize": "Toplam Önbellek Boyutu", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Tam Jellyfin Kütüphane Taraması", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Jellyfin En Son Eklenenler Taraması", + "components.Settings.SonarrModal.animerootfolder": "Anime Ana Dizini", + "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "İşlem başarıyla düzenlendi!", + "components.Settings.SettingsJobsCache.jobcancelled": "{jobname} iptal edildi.", + "components.Settings.SettingsJobsCache.jobname": "İşlem İsmi", + "components.Settings.SettingsJobsCache.jobsandcache": "İşlemler & Önbellek", + "components.Settings.SettingsJobsCache.jobstarted": "{jobname} başladı.", + "components.Settings.SettingsJobsCache.jobtype": "İşlemin Türü", + "components.Settings.SettingsJobsCache.nextexecution": "Sonraki İşlem", + "components.Settings.SettingsJobsCache.plex-full-scan": "Tam Plex Kütüphane Taraması", + "components.Settings.SettingsJobsCache.plex-recently-added-scan": "Plex En Son Eklenenler Taraması", + "components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex İzleme Listesi Eşitle", + "components.Settings.SettingsJobsCache.process": "Süreç", + "components.Settings.SettingsJobsCache.radarr-scan": "Radarr Taraması", + "components.Settings.SettingsJobsCache.runnow": "Şimdi Başlat", + "components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr Taraması", + "components.Settings.SettingsJobsCache.unknownJob": "Bilinmeyen İşlem", + "components.Settings.SettingsLogs.copiedLogMessage": "Loglar panoya kopyalandı.", + "components.Settings.SettingsLogs.copyToClipboard": "Panoya Kopyala", + "components.Settings.SettingsLogs.extraData": "İlave Veri", + "components.Settings.SettingsLogs.filterDebug": "Hata Ayıkla", + "components.Settings.SettingsLogs.filterError": "Hata", + "components.Settings.SettingsLogs.filterInfo": "Bilgilendirme", + "components.Settings.SettingsLogs.filterWarn": "Uyarı", + "components.Settings.SettingsLogs.label": "Etiket", + "components.Settings.SettingsLogs.level": "Önem Derecesi", + "components.Settings.SettingsLogs.logDetails": "Log Detayları", + "components.Settings.SettingsLogs.logs": "Loglar", + "components.Settings.SettingsLogs.message": "Mesaj", + "components.Settings.SettingsLogs.pauseLogs": "Durdur", + "components.Settings.SettingsLogs.resumeLogs": "Devam Ettir", + "components.Settings.SettingsLogs.showall": "Tüm Logları Göster", + "components.Settings.SettingsLogs.time": "Tarih Bilgisi", + "components.Settings.SettingsLogs.viewdetails": "Detayları Görüntüle", + "components.Settings.SettingsMain.apikey": "API Anahtarı", + "components.Settings.SettingsMain.applicationTitle": "Uygulama Başlığı", + "components.Settings.SettingsMain.applicationurl": "Uygulama URL'si", + "components.Settings.SettingsMain.cacheImages": "Resim Önbelleklemeyi Etkinleştir", + "components.Settings.SettingsMain.csrfProtection": "CSRF Korumasını Etkinleştir", + "components.Settings.SonarrModal.apiKey": "API Anahtarı", + "components.Settings.SettingsMain.csrfProtectionTip": "Dışa açık API erişim noktasını salt-okunur hale getirin (HTTPS gerektirir)", + "components.Settings.SettingsMain.general": "Genel", + "components.Settings.SettingsMain.generalsettings": "Genel Ayarlar", + "components.Settings.SettingsMain.hideAvailable": "Kullanılabilir İçerikleri Gizle", + "components.Settings.SettingsMain.locale": "Görüntüleme Dili", + "components.Settings.SettingsMain.originallanguage": "Keşfet Dili", + "components.Settings.SettingsMain.originallanguageTip": "İçerikleri orjinal dillerine göre filtrele", + "components.Settings.SettingsMain.partialRequestsEnabled": "Kısmi Dizi İsteklerini Kabul Et", + "components.Settings.SettingsMain.region": "Keşfet'in Bölgesi", + "components.Settings.SettingsMain.regionTip": "İçerikleri bölgesel erişilebilirliklerine göre filtrele", + "components.Settings.SettingsMain.toastApiKeyFailure": "Yeni API anahtarı denenirken beklenmedik bir hata oluştu.", + "components.Settings.SettingsMain.toastSettingsFailure": "Ayarlar kaydedilirken beklenmedik bir hata oluştu.", + "components.Settings.SettingsMain.toastSettingsSuccess": "Ayarlar başarıyla kaydedildi!", + "components.Settings.SettingsMain.trustProxy": "Proxy (Vekil Sunucu) Desteğini Etkinleştir", + "components.Settings.SettingsMain.validationApplicationTitle": "Bir uygulama başlığı girmelisiniz", + "components.Settings.SettingsMain.validationApplicationUrl": "Geçerli bir URL adresi girmelisiniz", + "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL'nizin sonunda slash (eğik çizgi) olmamalıdır", + "components.Settings.SettingsUsers.defaultPermissions": "Öntanımlı İzinler", + "components.Settings.SettingsUsers.defaultPermissionsTip": "Yeni kayıt yapmış kullanıcılara verilen izinler", + "components.Settings.SettingsUsers.localLogin": "Local (Yerel) Oturum Açmayı Etkinleştir", + "components.Settings.SettingsUsers.movieRequestLimitLabel": "Genel Film İsteme Sınırı", + "components.Settings.SettingsUsers.newPlexLogin": "{mediaServerName}'den Kayıtsız Oturum Açmayı Etkinleştir", + "components.Settings.SettingsUsers.newPlexLoginTip": "{mediaServerName} kullanıcılarının içe aktarılmadan oturum açmalarına izin ver", + "components.Settings.SettingsUsers.toastSettingsFailure": "Ayarlar kaydedilirken beklenmedik bir hata oluştu.", + "components.Settings.SettingsUsers.toastSettingsSuccess": "Kullanıcı ayarları başarıyla kaydedildi!", + "components.Settings.SettingsUsers.tvRequestLimitLabel": "Genel Dizi İsteme Sınırı", + "components.Settings.SettingsUsers.userSettings": "Kullanıcı Ayarları", + "components.Settings.SettingsUsers.users": "Kullanıcılar", + "components.Settings.SonarrModal.add": "Sunucu Ekle", + "components.Settings.SonarrModal.animeSeriesType": "Anime Dizisi Türü", + "components.Settings.SonarrModal.animeTags": "Anime Etiketleri", + "components.Settings.SonarrModal.animelanguageprofile": "Anime Dil Seçenekleri", + "components.Settings.SonarrModal.animequalityprofile": "Anime Kalite Seçenekleri", + "components.Settings.SonarrModal.create4ksonarr": "Yeni 4K Sonarr Sunucusu Ekle", + "components.Settings.SonarrModal.createsonarr": "Yeni Sonarr Sunucusu Ekle", + "components.Settings.SonarrModal.default4kserver": "Varsayılan 4K Sunucu", + "components.Settings.SonarrModal.defaultserver": "Varsayılan Sunucu", + "components.Settings.SonarrModal.edit4ksonarr": "4K Sonarr Sunucusunu Düzenle", + "components.Settings.SonarrModal.enableSearch": "Otomatik Aramayı Etkinleştir", + "components.Settings.SonarrModal.externalUrl": "Harici URL", + "components.Settings.SonarrModal.hostname": "Domain ya da IP Adresi", + "components.Settings.SonarrModal.languageprofile": "Dil Seçeneği", + "components.Settings.SonarrModal.loadingTags": "Etiketler yükleniyor…", + "components.Settings.SonarrModal.loadingprofiles": "Kalite seçenekleri yükleniyor…", + "components.Settings.SonarrModal.loadingrootfolders": "Ana klasör yükleniyor…", + "components.Settings.SonarrModal.notagoptions": "Etiket yok.", + "components.Settings.SonarrModal.port": "Port", + "components.Settings.SonarrModal.qualityprofile": "Kalite Seçeneği", + "components.Settings.SonarrModal.rootfolder": "Ana Klasör", + "components.Settings.SonarrModal.seasonfolders": "Sezon Klasörleri", + "components.Settings.SonarrModal.selectLanguageProfile": "Dil seçeneğini seç", + "components.Settings.SonarrModal.selectQualityProfile": "Kaliteyi seç", + "components.Settings.SonarrModal.selectRootFolder": "Ana klasörü seç", + "components.Settings.SonarrModal.selecttags": "Etiketleri seç", + "components.Settings.SonarrModal.seriesType": "Dizinin Türü", + "components.Settings.SonarrModal.server4k": "4K Sunucu", + "components.Settings.SonarrModal.ssl": "SSL Kullan", + "components.Settings.SonarrModal.syncEnabled": "Taramayı Etkinleştir", + "components.Settings.SonarrModal.tagRequests": "Etiket İstekleri", + "components.Settings.SonarrModal.tags": "Etiketler", + "components.Settings.SonarrModal.testFirstLanguageProfiles": "Dil seçeneklerini görmek için bağlantıyı test et", + "components.Settings.SonarrModal.testFirstQualityProfiles": "Kalite seçeneklerini görmek için bağlantıyı test et", + "components.Settings.SonarrModal.testFirstRootFolders": "Ana klasörleri yüklemek için bağlantıyı test et", + "components.Settings.SonarrModal.testFirstTags": "Etiketleri yüklemek için bağlantıyı test et", + "components.Settings.SonarrModal.toastSonarrTestFailure": "Sonarr'a bağlanılamadı.", + "components.Settings.SonarrModal.validationApiKeyRequired": "Bir API anahtarı girmelisiniz", + "components.Settings.SonarrModal.validationApplicationUrl": "Geçerli bir URL adresi girmelisiniz", + "components.Settings.SonarrModal.validationApplicationUrlTrailingSlash": "URL'nizin sonunda slash (eğik çizgi) olmamalıdır", + "components.Settings.SonarrModal.validationBaseUrlLeadingSlash": "Temel URL'nizin başında slash (eğik çizgi) olmalıdır", + "components.Settings.SonarrModal.validationHostnameRequired": "Geçerli bir sunucu adresi girmelisin", + "components.Settings.SonarrModal.validationLanguageProfileRequired": "Bir dil seçeneği seçmelisin", + "components.Settings.SonarrModal.validationPortRequired": "Geçerli bir port numarası girmelisin", + "components.Settings.SonarrModal.validationProfileRequired": "Bir kalite seçeneği girmelisin", + "components.Settings.SonarrModal.validationRootFolderRequired": "Bir ana klasör seçmelisin", + "components.Settings.activeProfile": "Etkin Profil", + "components.Settings.addradarr": "Radarr Sunucusu Ekle", + "components.Settings.address": "Adres", + "components.Settings.addsonarr": "Sonarr Sunucusu Ekle", + "components.Settings.cancelscan": "Taramayı İptal Et", + "components.Settings.copied": "API Anahtarı panoya kopyalandı.", + "components.Settings.currentlibrary": "Mevcut Kütüphane: {name}", + "components.Settings.default": "Öntanımlı", + "components.Settings.default4k": "Öntanımlı 4K", + "components.Settings.deleteServer": "{serverType} Sunucusunu Sil", + "components.Settings.deleteserverconfirm": "Bu sunucuyu silmek istediğinize emin misiniz?", + "components.Settings.email": "E-mail", + "components.Settings.enablessl": "SSL Kullan", + "components.Settings.externalUrl": "Harici URL", + "components.Settings.hostname": "Domain ya da IP Adresi", + "components.Settings.internalUrl": "Dahili URL", + "components.Settings.is4k": "4K", + "components.Settings.jellyfinSettings": "{mediaServerName} Ayarları", + "components.Settings.jellyfinSettingsFailure": "{mediaServerName} ayarları kaydedilirken beklenmedik bir hata oluştu.", + "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} ayarları başarıyla kaydedildi!", + "components.Settings.jellyfinlibraries": "{mediaServerName} Kütüphaneleri", + "components.Settings.jellyfinlibrariesDescription": "{mediaServerName} içerikleri için tarama yapacaktır. Eğer ki aşağıda hiç bir kütüphane listelenmediyse bu butona tıklayın.", + "components.Settings.jellyfinsettings": "{mediaServerName} Ayarları", + "components.Settings.jellyfinsettingsDescription": "{mediaServerName} sunucunuz için ayarları düzenleyin. {mediaServerName} sunucunuzun kütüphaneleri içlerinde ki içerikleri tespiti için taranacaktır.", + "components.Settings.librariesRemaining": "Kalan Kütüphaneler: {count}", + "components.Settings.manualscan": "Elle Kütüphaneleri Tara", + "components.Settings.manualscanDescriptionJellyfin": "Normalde rutin olarak bu işlem 24 saatte bir yapılır. Jellyseerr {mediaServerName} sunucunuzun en son eklenenlerini sıklıkla kontrol eder. Eğer ki bu Jellyseerr'ı ilk defa düzenleyişiniz ise tek seferlik elle kütüphane taraması yapmanız önerilir!", + "components.Settings.mediaTypeMovie": "film", + "components.Settings.menuAbout": "Hakkında", + "components.Settings.menuGeneralSettings": "Genel", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.menuLogs": "Loglar", + "components.Settings.menuNotifications": "Bildirimler", + "components.Settings.menuPlexSettings": "Plex", + "components.Settings.menuServices": "Servisler", + "components.Settings.menuUsers": "Kullanıcılar", + "components.Settings.mediaTypeSeries": "dizi", + "components.Settings.noDefaultServer": "İsteğin işlenebilmesi için en azından bir {serverType} sunucusunun {mediaType} içerikleri iöin öntanımlı olarak işaretlenmesi gerekmektedir.", + "components.Settings.notificationAgentSettingsDescription": "Bildirim köprülerini düzenle ve etkinleştir.", + "components.Settings.notifications": "Bildirimler", + "components.Settings.notificationsettings": "Bildirim Ayarları", + "components.Settings.notrunning": "Çalışmıyor", + "components.Settings.plex": "Plex", + "components.Settings.plexlibraries": "Plex Kütüphaneleri", + "components.Settings.plexlibrariesDescription": "Overseerr kütüphanelerin içeriklerini tarar. Plex bağlantı ayarlarını kurun ve kaydedin, ardından eğer ki hiç bir kütüphane listelenmemişse aşağıda ki butona basın.", + "components.Settings.plexsettings": "Plex Ayarları", + "components.Settings.port": "Port", + "components.Settings.radarrsettings": "Radarr Ayarları", + "components.Settings.restartrequiredTooltip": "Bu kaydedilen ayarların etkin olması iöin Overseerr yendien başlatılmalıdır", + "components.Settings.save": "Değişiklikleri Kaydet", + "components.Settings.saving": "Kaydediliyor…", + "components.Settings.scan": "Kütüphaneleri Eşitle", + "components.Settings.scanning": "Eşitleniyor…", + "components.Settings.serverLocal": "local (yerel)", + "components.Settings.serverRemote": "remote (uzak)", + "components.Settings.serverSecure": "güvenli", + "components.Settings.serverpreset": "Sunucu", + "components.Settings.serverpresetLoad": "Kullanılabilir suncuuları yüklemek için butona bas", + "components.Settings.serverpresetManualMessage": "Özel ayarlamalar", + "components.Settings.serverpresetRefreshing": "Sunucular getiriliyor…", + "components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "Slack bildirim ayarları kaydedildi!", + "components.Settings.Notifications.authUser": "SMPT Kullanıcı Adı", + "components.Settings.Notifications.pgpPasswordTip": "OpenPGP kullanarak e-mailleri imzala ve şifrele", + "components.Settings.Notifications.toastDiscordTestFailed": "Discord test bildirimi gönderilemedi.", + "components.Settings.RadarrModal.selectMinimumAvailability": "Asgari erişilebilirlik ayarını seç", + "components.Settings.RadarrModal.validationHostnameRequired": "Geçerli bir sunucu adresi girmelisin", + "components.Settings.SettingsAbout.betawarning": "Bu bir BETA yazılımdır. Özellikler hatalı ya da dengesiz olabilir. Bir sorunla karşılaşırsanız lütfen GitHub'dan bildirin!", + "components.Settings.SettingsMain.cacheImagesTip": "Dış kaynaklardan alınan resimleri önbelleğe al (depolama kullanımını arttıracaktır)", + "components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Bir Webhook entegrasyonu oluştur", + "components.Settings.Notifications.discordsettingssaved": "Discord bildirim ayarları kaydedildi!", + "components.Settings.Notifications.encryptionOpportunisticTls": "Her daim STARTTLS kullan", + "components.Settings.Notifications.toastDiscordTestSending": "Discord deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.webhookUrlTip": "Sunucunuz için bir Webhook entegrasyonu oluştur", + "components.Settings.SettingsAbout.outofdate": "Eski Sürüm", + "components.Settings.SettingsAbout.runningDevelop": "Overseerr'ın geliştirici çatalını kullanıyorsunuz, bu sürüm geliştirmeye yardımcı olmak isteyenler ya da stabil olmayan özellikleri test etmek isteyenler içindir.", + "components.Settings.SettingsJobsCache.cachevsize": "Önbelleğin Büyüklüğü", + "components.Settings.SettingsJobsCache.imagecacheDescription": "Eğer ki ayarlardan etkinleştirilirse Overseer resimleri önceden belirli dış kaynaklardan çekip önbelleğe alır. Önbelleğe alınmış resimler ayarlanmış klasöre kaydedilir. Bu dosyalara {appDataPath}/cache/images lokasyonundan erişebilirsiniz.", + "components.Settings.SettingsUsers.userSettingsDescription": "Evrensel ve varsayılan kullanıcı ayarlarını düzenle.", + "components.Settings.SonarrModal.editsonarr": "Sonarr Sunucusunu Düzenle", + "components.Settings.SonarrModal.tagRequestsInfo": "Otomatik olarak isteyenin kullanıcı adını ek etiketlere ekle", + "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr'a başarıyla bağlanıldı!", + "components.Settings.experimentalTooltip": "Bu ayarı etkinleştirmek uygulamanın beklenmedik davranışlarda bulunmasına sebep olabilir", + "components.Settings.noDefaultNon4kServer": "Eğer ki hem 4K hem de 4K-olmayan içerikler için tek tip bir {serverType} sunucusu kullanıyorsan (ya da sadece 4K içerikleri indiriyorsan) {serverType} sunucunu KESİNLİKLE 4K olarak ayarlamaman gerekmektedir.", + "components.Settings.Notifications.NotificationsWebPush.webpushsettingsfailed": "Web bildirim ayarları kaydedilemedi.", + "components.Settings.Notifications.validationBotAPIRequired": "Bir bot yetkilendirme token'i sağlamalısın", + "components.Settings.RadarrModal.loadingprofiles": "Kalite seçenekleri yükleniyor…", + "components.Settings.RadarrModal.tagRequestsInfo": "Otomatik olarak isteyenin kullanıcı adını ek etiketlere ekle", + "components.Settings.RadarrModal.validationApiKeyRequired": "Bir API anahtarı girmelisiniz", + "components.Settings.SettingsAbout.Releases.releasedataMissing": "Çıkışıyla ilgili veri henüz mevcut değildir.", + "components.Settings.SettingsAbout.overseerrinformation": "Overseerr Hakkında", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Her {jobScheduleHours, plural, one {saatte} other {{jobScheduleHours} hours}}", + "components.Settings.SettingsJobsCache.jobScheduleEditFailed": "İşlem kaydedilirken beklenmedik bir hata oluştu.", + "components.Settings.SettingsJobsCache.jobs": "İşlemler", + "components.Settings.SettingsMain.generalsettingsDescription": "Overseerr'ın evrensel ve varsayılan ayarlarını düzenleyin.", + "components.Settings.SonarrModal.validationNameRequired": "Bir sunucu ismi girmelisin", + "components.Settings.jellyfinSettingsDescription": "İsterseniz {mediaServerName} sunucunuzun dahili ve harici URL'lerini düzenleyebilirsiniz. Genellikle harici URL dahili olan URL'den farklıdır. Özel bir şifre sıfırlama URL'sini de buraya girebilirsiniz, eğer ki {mediaServerName} kullandığınız farklı bir oturum açma yöneticisi varsa bu faydalı olabilir.", + "components.Settings.noDefault4kServer": "Kullanıcıların 4K {mediaType} istekleri yapabilmesi için 4K {serverType} sunucusu öntanımlı olarak işaretlenmelidir.", + "components.Settings.plexsettingsDescription": "Plex sunucunuzun ayarlarını düzenleyin. Overseerr Plex sunucunuzun kütüphanelerini izlenebilir içerikler için tarar.", + "components.Settings.Notifications.NotificationsWebhook.validationWebhookUrl": "Geçerli bir URL girmelisiniz", + "components.Settings.Notifications.botUsernameTip": "Kullanıcıların botunuzla yazışabilmesine ve kendi bildirim ayarlarını düzenlemelerine izin verin", + "components.Settings.Notifications.validationSmtpHostRequired": "Geçerli bir sunucu adresi girmelisin", + "components.Settings.SettingsUsers.localLoginTip": "{mediaServerName} sunucusunun OAuth oturum yöneticisiyle oturum açmak yerine e-mail ve şifreyle oturum açmayı kabul et", + "components.Settings.advancedTooltip": "Bu ayarı yanlış bir şekilde düzenlemek uygulamanın işlevselliğini bozabilir", + "components.Settings.Notifications.NotificationsPushover.toastPushoverTestSending": "Pushover deneme bildirimi gönderiliyor…", + "components.Settings.Notifications.NotificationsPushover.userTokenTip": "30 karakterlik kullanıcı ya da grup kimliği", + "components.Settings.Notifications.NotificationsWebhook.toastWebhookTestFailed": "Webhook deneme bildirimi gönderilemedi.", + "components.Settings.Notifications.toastEmailTestSending": "E-mail deneme bildirimi gönderiliyor…", + "components.Settings.SettingsMain.toastApiKeySuccess": "Yeni API anahtarı başarıyla oluşturuldu!", + "components.Settings.SettingsJobsCache.jobsDescription": "Overseer bazı gerekli bakım işlemlerini genel ayarlı işlemler çerçevesinde otomatik olarak tamamlar, ancak bu işlemleri elle de başlatabilirsiniz. Elle bir işlem başlatmanız diğer süreçleri bozmayacaktır.", + "components.Settings.SettingsLogs.logsDescription": "Logları ayrıca direkt olarakstdout'dan ya da {appDataPath}/logs/overseerr.log dosyasına bakarak görebilirsiniz.", + "components.Settings.SettingsMain.csrfProtectionHoverTip": "Eğer ki ne yaptığınızı bilmiyorsanız bu ayarı ETKİNLEŞTİRMEYİNİZ!", + "components.Settings.SettingsMain.trustProxyTip": "Overseerr bir proxy'nin arkasındayken IP adreslerini kadyetmesine izin ver", + "components.Settings.SonarrModal.baseUrl": "Temel URL Adresi", + "components.Settings.SonarrModal.loadinglanguageprofiles": "Dil seçenekleri yükleniyor…", + "components.Settings.SonarrModal.servername": "Sunucu İsmi", + "components.Settings.SonarrModal.validationBaseUrlTrailingSlash": "Temel URL slash (eğik çizgi) ile bitmemelidir", + "components.Settings.manualscanDescription": "Normalde rutin olarak bu işlem 24 saatte bir yapılır. Overseer Plex sunucunuzun en son eklenenlerini sıklıkla kontrol eder. Eğer ki bu Plex sunucunuzu ilk defa düzenleyişiniz ise tek seferlik elle kütüphane taraması yapmanız önerilir!", + "components.Settings.manualscanJellyfin": "Elle Kütüphaneleri Tara", + "components.Settings.menuJobs": "İşlemler & Önbellek", + "components.Settings.serviceSettingsDescription": "{serverType} sunucu(lar)ınızı aşağıdan düzenleyin. Birden fazla {serverType} sunucusuna bağlanabilirsiniz, ancak sadece iki tanesi varsayılan olarak tanımlanabilir (bir adet 4K-dışı ve bir adet 4K). Yöneticiler sunucuları her daim mutlak bir biçimde yönetebilirler.", + "components.Settings.services": "Servisler", + "components.Settings.settingUpPlexDescription": "Plex'İ kurmak için gerekli yerleri elle doldurabilir ya da plex.tv'den alınmış bir sunucu seöebilirsiniz. Açılır menünün sağında ki tuşa basarak kullanılabilir sunucuların listesini çekebilirsiniz.", + "components.Settings.ssl": "SSL", + "components.Settings.startscan": "Taramaya Başla", + "components.Settings.syncJellyfin": "Kütüphaneleri Eşitle", + "components.Settings.tautulliApiKey": "API Anahtarı", + "components.Settings.tautulliSettings": "Tautulli Ayarları", + "components.Settings.timeout": "Zaman Aşımı", + "components.Settings.toastPlexConnecting": "Plex'e yeniden bağlanılıyor…", + "components.Settings.toastPlexConnectingFailure": "Plex'e bağlanılamadı.", + "components.Settings.toastPlexConnectingSuccess": "Plex'e başarıyla bağlanıldı!", + "components.Settings.toastPlexRefresh": "Plex'den sunucu listesi alınıyor…", + "components.Settings.toastPlexRefreshFailure": "Plex'ten sunucu listesi alınamadı.", + "components.Settings.toastPlexRefreshSuccess": "Plex'ten sunucu listesi başarıyla alınıldı!", + "components.Settings.toastTautulliSettingsFailure": "Tautulli ayarlarınızı kaydederken beklenmedik bir hatayla karşılaşıldı.", + "components.Settings.toastTautulliSettingsSuccess": "Tautulli ayarları başarıyla kaydedildi!", + "components.Settings.urlBase": "Temel URL Adresi", + "components.Settings.validationHostnameRequired": "Geçerli bir sunucu adresi girmelisin", + "components.Settings.validationPortRequired": "Geçerli bir port numarası girmelisin", + "components.Settings.validationUrl": "Geçerli bir URL adresi girmelisiniz", + "components.Settings.validationUrlBaseTrailingSlash": "Temel URL slash (eğik çizgi) ile bitmemelidir", + "components.Settings.validationUrlTrailingSlash": "URL'nizin sonunda slash (eğik çizgi) olmamalıdır", + "components.Settings.webAppUrl": "Web Uygulaması Linki", + "components.Settings.webhook": "Webhook", + "components.Settings.webpush": "Web Bildirimi", + "components.Setup.configuremediaserver": "Medya Sunucusunu Düzenleyin", + "components.Setup.configureservices": "Servisleri Düzenleyin", + "components.Setup.continue": "Devam Et", + "components.Setup.finish": "Kurulumu Bitir", + "components.Setup.finishing": "Son Adımlar…", + "components.Setup.scanbackground": "Tarama arkaplanda çalışacaktır, bu süre zarfında kuruluma devam edebilirsiniz.", + "components.Setup.setup": "Kurulum", + "components.Setup.signin": "Oturum Aç", + "components.Setup.signinMessage": "Oturum açarak devam edin", + "components.Setup.signinWithPlex": "Plex hesabınızı kullanın", + "components.Setup.tip": "İpuçları", + "components.Setup.welcome": "Jellyseer'a Hoşgeldiniz", + "components.StatusBadge.managemedia": "{mediaType} içeriğini yönet", + "components.StatusBadge.openinarr": "{arr}'da Aç", + "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}B{episodeNumber}", + "components.StatusBadge.status": "{status}", + "components.StatusBadge.status4k": "4K {status}", + "components.StatusChecker.appUpdated": "{applicationTitle} Güncellendi", + "components.StatusChecker.reloadApp": "{applicationTitle}'ı Yenile", + "components.StatusChecker.restartRequired": "Sunucuyu Yeniden Başlatmalısınız", + "components.StatusChecker.restartRequiredDescription": "Güncellenen ayarların etkinleşmesi için sunucuyu yeniden başlatın.", + "components.TitleCard.addToWatchList": "İzleme listesine ekle", + "components.TitleCard.cleardata": "Veriyi Temizle", + "components.TitleCard.mediaerror": "{mediaType} Bulunamadı", + "components.TitleCard.tmdbid": "TMDB ID'si", + "components.TitleCard.watchlistCancel": "{title} için olan izleme listesi iptal edildi.", + "components.TitleCard.watchlistError": "Bir şeyler ters gitti, lütfen tekrar deneyiniz.", + "components.TitleCard.watchlistSuccess": "{title} izleme listesine başarıyla eklendi!", + "components.TvDetails.Season.noepisodes": "Bölüm listesi mevcut değil.", + "components.TvDetails.Season.somethingwentwrong": "Sezon verisi alınırken bir şeyler ters gitti.", + "components.TvDetails.TvCast.fullseriescast": "Tüm Dizinin Kadrosu", + "components.TvDetails.TvCrew.fullseriescrew": "Tüm Dizinin Ekibi", + "components.TvDetails.anime": "Anime", + "components.TvDetails.cast": "Kadro", + "components.TvDetails.episodeRuntime": "Bölümün Süresi", + "components.TvDetails.episodeRuntimeMinutes": "{runtime} dakika", + "components.TvDetails.firstAirDate": "İlk Yayın Tarihi", + "components.TvDetails.manageseries": "Diziyi Düzenle", + "components.TvDetails.nextAirDate": "Sonraki Çıkış tarihi", + "components.TvDetails.originallanguage": "Orjinal Dili", + "components.TvDetails.originaltitle": "Ana Dilindeki Başlık", + "components.TvDetails.overview": "Özet", + "components.TvDetails.play": "{mediaServerName} ile Oynat", + "components.TvDetails.play4k": "İçeriği {mediaServerName} içinde 4K'da oynat", + "components.TvDetails.recommendations": "Önerilenler", + "components.TvDetails.reportissue": "Bir Sorun Bildir", + "components.TvDetails.rtaudiencescore": "Rotten Tomatoes İzleyici Puanı", + "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", + "components.TvDetails.seasonnumber": "Sezon {seasonNumber}", + "components.TvDetails.network": "{networkCount} TV Ağı", + "components.TvDetails.seasons": "{seasonCount} # Sezon", + "components.TvDetails.tmdbuserscore": "TMBD Kullanıcı Skoru", + "components.TvDetails.viewfullcrew": "Tüm Ekibi Gör", + "components.TvDetails.watchtrailer": "Fragmanı İzle", + "components.UserList.accounttype": "Türü", + "components.UserList.admin": "Yönetici", + "components.UserList.autogeneratepassword": "Rastgele Bir Parola Oluştur", + "components.UserList.bulkedit": "Toplu Düzenle", + "components.UserList.create": "Oluştur", + "components.UserList.created": "Katıldı", + "components.UserList.createlocaluser": "Yerel Bir Kullanıcı Oluştur", + "components.UserList.creating": "Oluşturuluyor…", + "components.UserList.deleteuser": "Kullanıcıyı Sil", + "components.UserList.displayName": "Görünen İsmi", + "components.UserList.edituser": "Kullanıcının İzinlerini Düzenle", + "components.UserList.email": "E-mail Adresi", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} kullanıcısı başarıyla içeri aktarıldı!", + "components.UserList.importedfromplex": "{userCount} Plex kullanıcısı başarıyla içeri aktarıldı!", + "components.UserList.importfromJellyfin": "{mediaServerName} Kullanıcılarını İçeri Aktar", + "components.UserList.importfrommediaserver": "{mediaServerName} Kullanıcılarını İçeri Aktar", + "components.UserList.importfromplex": "Plex Kullanıcılarını İçeri Aktar", + "components.UserList.importfromplexerror": "Plex kullanıcıları içeri aktarılırken bir şeyler ters gitti.", + "components.UserList.localLoginDisabled": "Yerel Oturum Açma seçeneği şu anda etin değil.", + "components.UserList.localuser": "Yerel Kullanıcı", + "components.UserList.mediaServerUser": "{mediaServerName} Kullanıcısı", + "components.TitleCard.watchlistDeleted": "{title} İzleme listenizden başarıyla çıkarıldı!", + "components.Settings.sonarrsettings": "Sonarr Ayarları", + "components.Settings.syncing": "Eşitleniyor", + "components.Settings.tautulliSettingsDescription": "Tautulli sunucunuz için opsiyonel ayarları düzenleyin. Overseer Plex'te ki izleme geçmişinizi Tautulli'den çeker.", + "components.Settings.validationApiKey": "Bir API anahtarı girmelisiniz", + "components.Settings.validationUrlBaseLeadingSlash": "URL'nizin başında slash (eğik çizgi) olmalıdır", + "components.Settings.webAppUrlTip": "Opsiyonel bazlı olarak, kullanıcıları \"hostlanan\" web uygulaması yerine kendi sunucunuzda ki uygulamaya yönlendirin", + "components.Setup.signinWithJellyfin": "{mediaServerName} hesabınızı kullanın", + "components.StatusBadge.playonplex": "{mediaServerName} ile Oynat", + "components.StatusChecker.appUpdatedDescription": "Lütfen aşağıda ki butona tıklayarak uygulamayı yenileyin.", + "components.TitleCard.tvdbid": "TheTVDB ID'si", + "components.TvDetails.similar": "Benzer Diziler", + "components.TvDetails.overviewunavailable": "Özet mevcut değil.", + "components.TvDetails.seasonstitle": "Sezonlar", + "components.UserList.autogeneratepasswordTip": "Kullanıcıya otomatik oluşturulmuş parolayı e-mail olarak gönder", + "components.TvDetails.episodeCount": "{episodeCount} # Bölüm", + "components.UserList.importfromJellyfinerror": "{mediaServerName} kullanıcıları içeri aktarılırken bir şeyler ters gitti.", + "components.TvDetails.showtype": "Dizinin Türü", + "components.TvDetails.productioncountries": "Yapımcı {plural, one {Ülke} other {Ülkeler}}", + "components.TvDetails.status4k": "4K {status}", + "components.UserList.deleteconfirm": "Bu kullanıcıyı silmek istediğinizden emin misiniz? Tüm istek verisi temelli olarak silinecektir.", + "components.TvDetails.streamingproviders": "İçertiğin Erişilebilir Olduğu Platformlar", + "components.UserList.newplexsigninenabled": "Yeni Plex Kullanıcılarının Oturum Açmasına İzin Ver ayarı etkin durumdadır. Plex kullanıcılarından kütüphane erişimine sahip olanlarının oturum açabilmesi için içeri aktarılması gerekmez.", + "components.UserList.newJellyfinsigninenabled": "Yeni {mediaServerName} Kullanıcılarının Oturum Açmasına İzin Ver ayarı etkin durumdadır. {mediaServerName} kullanıcılarından kütüphane erişimine sahip olanlarının oturum açabilmesi için içeri aktarılması gerekmez.", + "components.UserList.noJellyfinuserstoimport": "İçeri aktarılacak {mediaServerName} kullanıcısı mevcut değil.", + "components.UserList.nouserstoimport": "İçeri aktarılacak Plex kullanıcısı mevcut değil.", + "components.UserList.owner": "Sahip", + "components.UserList.password": "Şifre", + "components.UserList.passwordinfodescription": "Bir uygulama URL'si ayarlayın ve otomatik şifre oluşturulabilmesi için e-mail bildirimlerini aktif edin.", + "components.UserList.plexuser": "Plex Kullanıcısı", + "components.UserList.role": "Rolü", + "components.UserList.sortCreated": "Katılım Tarihi", + "components.UserList.sortDisplayName": "Görünen İsmi", + "components.UserList.sortRequests": "İstek Sayısı", + "components.UserList.totalrequests": "İstekleri", + "components.UserList.user": "Kullanıcı", + "components.UserList.usercreatedsuccess": "Kullanıcı başarıyla oluşturuldu!", + "components.UserList.userdeleted": "Kullanıcı başarıyla silindi!", + "components.UserList.userdeleteerror": "Kullanıcıyı silerken bir şeyler ters gitti.", + "components.UserList.userfail": "Kullanıcının izinleri kaydedilirken bir şeyler ters gitti.", + "components.UserList.userlist": "Kullanıcı Listesi", + "components.UserList.users": "Kullanıcılar", + "components.UserList.userssaved": "Kullanıcının izni başarıyla kaydedildi!", + "components.UserList.validationpasswordminchars": "Girdiğiniz şifre çok kısa, en az 8 karakterden oluşmalıdır", + "components.UserProfile.ProfileHeader.joindate": "{joindate} tarihinde katıldı", + "components.UserProfile.ProfileHeader.profile": "Profili Görüntüle", + "components.UserProfile.ProfileHeader.settings": "Ayarları Düzenle", + "components.UserProfile.ProfileHeader.userid": "Kullanıcı ID'si: {userid}", + "components.UserProfile.UserSettings.UserGeneralSettings.accounttype": "Hesabın Türü", + "components.UserProfile.UserSettings.UserGeneralSettings.admin": "Yönetici", + "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "Discord Kullanıcı ID'si", + "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Görünen İsmi", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "E-mail", + "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Genel Limiti Geçersiz Kıl", + "components.UserProfile.UserSettings.UserGeneralSettings.general": "Genel", + "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "Genel Ayarlar", + "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Varsayılan ({language})", + "components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Yerel Kullanıcı", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} Kullanıcısı", + "components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Film İsteme Limiti", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguage": "Keşfet Dili", + "components.UserProfile.UserSettings.UserGeneralSettings.originallanguageTip": "İçerikleri orjinal dillerine göre filtrele", + "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Sahip", + "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Plex Kullanıcısı", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Filmleri Otomatize İstet", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Dizileri Otomatize İstet", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Plex İzleme Listenizde'ki dizileri otomatik olarak istet", + "components.UserProfile.UserSettings.UserGeneralSettings.region": "Keşfet'in Bölgesi", + "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "İçerikleri bölgesel erişilebilirliklerine göre filtrele", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Değişiklikleri Kaydet", + "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Dizi İsteme Limiti", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Ayarlar kaydedilirken beklenmedik bir hata oluştu.", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Ayarlar başarıyla kaydedildi!", + "components.UserProfile.UserSettings.UserGeneralSettings.user": "Kullanıcı", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Cihaz Varsayılanı", + "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "Kullanıcı ID'si", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Discord bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.discordsettingssaved": "Discord bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserNotificationSettings.email": "E-mail", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": "E-mail bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "E-mail bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Bildirimler", + "components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Bildirim Ayarları", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "Açık (Public) PGP Anahtarı", + "components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKeyTip": "E-mail mesajlarını OpenPGP kullanarak şifrele", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessToken": "Erişim Token'i", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "Hesap Ayarlarınızdan bir token oluşturun", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingsfailed": "Pushbullet bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "Uygulama API'sinin Token'i", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationTokenTip": "{applicationTitle} ile kullanabilmeniz için bir uygulama oluşturun", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKey": "Kullanıcı ya da Grup Kimliği", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Pushover bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingssaved": "Pushover bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Sessizce Gönder", + "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Ses olmadan bildirim gönder", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Bildirim Sesi", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Sohbet ID'si", + "components.UserProfile.UserSettings.menuNotifications": "Bildirimler", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Telegram bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Telegram bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Geçerli bir kullanıcı ID'si sağlamalısın", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPgpPublicKey": "Geçerli bir PGP açık (public) anahtarı sağlamalısın", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "Bir erişim token'i sağlamalısınız", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "Geçerli bir kullanıcı ya da grup kimliği sağlamalısınız", + "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "Geçerli bir sohbet ID'si sağlamalısın", + "components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Bildirimi", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Web bildirim ayarları kaydedilemedi.", + "components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Web bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Şifreyi Doğrula", + "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Kullandığınız Şifre", + "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "Yeni Şifre", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "Hesabın ayarlanmış bir şifreye sahip değil. Aşağıdan e-mail adresinizi kullanarak bir şifre belirlerseniz \"yerel kullanıcı\" olarak oturum açabilirsiniz.", + "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "Bu kullanıcının şifresini değiştirme yetkisine sahip değilsin.", + "components.UserProfile.UserSettings.UserPasswordChange.password": "Şifre", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailureVerifyCurrent": "Şifre kaydedilirken bir hata oluştu, şu anda kullandığınız şifreyi doğru girdiğinizden emin olun.", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsSuccess": "Şifre başarıyla kaydedildi!", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPassword": "Yeni şifreyi doğrulamanız gerekmektedir", + "components.UserProfile.UserSettings.UserPasswordChange.validationConfirmPasswordSame": "Girdiğiniz şifreler uyuşmuyor", + "components.UserProfile.UserSettings.UserPasswordChange.validationCurrentPassword": "Mevcut olarak kullandığınız şifrenizi girmediniz", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPassword": "Yeni bir şifre girmediniz", + "components.UserProfile.UserSettings.UserPermissions.permissions": "İzinler", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsFailure": "Ayarlar kaydedilirken beklenmedik bir hata oluştu.", + "components.UserProfile.UserSettings.UserPermissions.toastSettingsSuccess": "İzinler başarıyla kaydedildi!", + "components.UserProfile.UserSettings.UserPermissions.unauthorizedDescription": "Kendi izinlerini düzenleyemezsin.", + "components.UserProfile.UserSettings.menuChangePass": "Şifre", + "components.UserProfile.UserSettings.menuGeneralSettings": "Genel", + "components.UserProfile.UserSettings.unauthorizedDescription": "Bu kullanıcının ayarlarını düzenleme yetkin yok.", + "components.UserProfile.limit": "{limit} limitinizden {remaining} adet kaldı", + "components.UserProfile.localWatchlist": "{username} kullanıcısının İzleme Listesi", + "components.UserProfile.movierequests": "Film İstekleri", + "components.UserProfile.pastdays": "{type} ({days} gün geçti)", + "components.UserProfile.plexwatchlist": "Plex İzleme Listen", + "components.UserProfile.recentlywatched": "Yakın Zamanda İzlendi", + "components.UserProfile.recentrequests": "Yakın Vakitteki İstekler", + "components.UserProfile.requestsperdays": "{limit} limitin kaldı", + "components.UserProfile.seriesrequest": "Dizi İstekleri", + "components.UserProfile.totalrequests": "Toplam İstek", + "components.UserProfile.unlimited": "Sınırsız", + "i18n.advanced": "Gelişmiş", + "i18n.all": "Hepsi", + "i18n.approve": "Kabul Et", + "i18n.areyousure": "Emin misiniz?", + "i18n.available": "Kullanılabilir", + "i18n.back": "Geri", + "i18n.cancel": "İptal Et", + "i18n.canceling": "İptal Ediliyor…", + "i18n.close": "Kapat", + "i18n.collection": "Koleksiyon", + "i18n.decline": "Reddet", + "i18n.declined": "Reddedildi", + "i18n.delete": "Sil", + "i18n.delimitedlist": "{a}, {b}", + "i18n.edit": "Düzenle", + "i18n.experimental": "Deneysel", + "i18n.failed": "Başarısız", + "i18n.import": "İçe Aktar", + "i18n.importing": "İçe Aktarılıyor…", + "i18n.loading": "Yükleniyor…", + "i18n.movie": "Film", + "i18n.movies": "Filmler", + "i18n.next": "Sonraki", + "i18n.noresults": "Sonuç yok.", + "i18n.open": "Açık", + "i18n.partiallyavailable": "Kısmen Mevcut", + "i18n.pending": "Beklemede", + "i18n.previous": "Önceki", + "i18n.processing": "İşleniyor", + "i18n.request": "İste", + "i18n.request4k": "4K'da İste", + "i18n.requested": "İstenildi", + "i18n.requesting": "İsteniliyor…", + "i18n.resolved": "Çözüldü", + "i18n.resultsperpage": "Sayfa başına {pageSize} sonuç göster", + "i18n.retry": "Yeniden Dene", + "i18n.retrying": "Yeniden Deneniyor…", + "i18n.save": "Değişiklikleri Kaydet", + "i18n.saving": "Kaydediliyor…", + "i18n.settings": "Ayarlar", + "i18n.showingresults": "{from} ile {to} arasından toplam {total} sonuç var", + "i18n.status": "Durum", + "i18n.test": "Test Et", + "i18n.testing": "Tet Ediliyor…", + "i18n.tvshow": "Dizi", + "i18n.tvshows": "Diziler", + "i18n.unavailable": "Mevcut Değil", + "i18n.usersettings": "Kullanıcı Ayarları", + "i18n.view": "Görüntüle", + "pages.errormessagewithcode": "{statusCode} - {error}", + "pages.internalservererror": "Dahili Sunucu Hatası", + "pages.oops": "Eyvah", + "pages.pagenotfound": "Sayfa Bulunamadı", + "pages.returnHome": "Anasayfaya Dön", + "pages.serviceunavailable": "Hizmet Erişilebilir Değil", + "pages.somethingwentwrong": "Bir Şeyler Ters Gitti", + "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "Geçerli bir Discord ID'si sunmalısın", + "components.UserProfile.UserSettings.UserPasswordChange.toastSettingsFailure": "Şifre kaydedilirken bir şeyler ters gitti.", + "components.UserProfile.UserSettings.menuPermissions": "İzinler", + "i18n.approved": "Kabul Edildi", + "i18n.deleting": "Siliniyor…", + "i18n.notrequested": "İstenmedi", + "components.UserList.usercreatedfailed": "Kullanıcı oluşturulurken bir şeyler ters gitti.", + "components.UserList.usercreatedfailedexisting": "Verilen e-mail adresi halihazırda başka bir kulllanıcı tarafından kullanılmaktadır.", + "components.UserList.validationEmail": "Geçerli bir e-mail adresi sağlamalısın", + "components.UserProfile.UserSettings.UserGeneralSettings.applanguage": "Görüntüleme Dili", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Plex İzleme Listenizde'ki filmleri otomatik olarak istet", + "components.UserProfile.UserSettings.UserGeneralSettings.role": "Rolü", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Kaydediliyor…", + "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "Hesabınız ile bağıntılı numaralardan oluşan kullanıcı ID'niz", + "components.UserProfile.UserSettings.UserNotificationSettings.pushoverUserKeyTip": "30 karakterlik kullanıcı ya da grup kimliği", + "components.UserProfile.emptywatchlist": "Plex İzleme Listenize eklenen içerikler burada gözükeceklerdir.", + "i18n.restartRequired": "Yeniden Başlatma Gereklidir", + "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "Discord hesabınız ile bağıntılı numaralardan oluşan kullanıcı ID'niz", + "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Pushbullet bildirim ayarları kaydedildi!", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Bir sohbet başlatın ve @get_id_bot ID'li botunuzu ekleyin. Son olarak /my_id komutunu kullanın", + "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverApplicationToken": "Geçerli bir uygulama token'i sağlamalısınız", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "Bu kullanıcının henüz ayarlanmış bir şifresi yok. Aşağıdan bir şifre ayarlayarak bu hesabın \"yerel kullanıcı\" olarak oturum açabilmesini sağlayın.", + "components.UserProfile.UserSettings.UserPasswordChange.validationNewPasswordLength": "Girdiğiniz şifre çok kısa, en az 8 karakterden oluşmalıdır" +} diff --git a/src/i18n/locale/uk.json b/src/i18n/locale/uk.json index 0b38de3e..03eb1205 100644 --- a/src/i18n/locale/uk.json +++ b/src/i18n/locale/uk.json @@ -10,10 +10,10 @@ "components.Discover.DiscoverMovieLanguage.languageMovies": "Фільми мовою \"{language}\"", "components.Discover.DiscoverNetwork.networkSeries": "Серіали {network}", "components.Discover.DiscoverStudio.studioMovies": "Фільми {studio}", - "components.Discover.DiscoverTvGenre.genreSeries": "Серіали в жанрі \"{genre}\"", - "components.Discover.DiscoverTvLanguage.languageSeries": "Серіали мовою \"{language}\"", - "components.Discover.DiscoverWatchlist.discoverwatchlist": "Your Watchlist", - "components.Discover.DiscoverWatchlist.watchlist": "Список спостереження Plex", + "components.Discover.DiscoverTvGenre.genreSeries": "Серіали в жанрі {genre}", + "components.Discover.DiscoverTvLanguage.languageSeries": "Серіали мовою {language}", + "components.Discover.DiscoverWatchlist.discoverwatchlist": "Ваш список перегляду Plex", + "components.Discover.DiscoverWatchlist.watchlist": "Список перегляду Plex", "components.Discover.MovieGenreList.moviegenres": "Фільми за жанрами", "components.Discover.MovieGenreSlider.moviegenres": "Фільми за жанрами", "components.Discover.NetworkSlider.networks": "Телеканали", @@ -25,7 +25,7 @@ "components.Discover.discovertv": "Популярні серіали", "components.Discover.emptywatchlist": "Тут з’являться медіафайли, додані до вашого списку спостереження Plex.", "components.Discover.noRequests": "Жодних запитів.", - "components.Discover.plexwatchlist": "Ваш список спостереження Plex", + "components.Discover.plexwatchlist": "Ваш список перегляду Plex", "components.Discover.popularmovies": "Популярні фільми", "components.Discover.populartv": "Популярні серіали", "components.Discover.recentlyAdded": "Нещодавно додані", @@ -118,7 +118,7 @@ "components.Layout.Sidebar.settings": "Налаштування", "components.Layout.Sidebar.users": "Користувачі", "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "Запити на фільми", - "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "Запити на серіали", + "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "Запити на сезони", "components.Layout.UserDropdown.myprofile": "Профіль", "components.Layout.UserDropdown.requests": "Запити", "components.Layout.UserDropdown.settings": "Налаштування", @@ -142,24 +142,24 @@ "components.ManageSlideOver.downloadstatus": "Завантаження", "components.ManageSlideOver.manageModalAdvanced": "Просунутий", "components.ManageSlideOver.manageModalClearMedia": "Очистити дані", - "components.ManageSlideOver.manageModalClearMediaWarning": "* Це призведе до незворотного видалення всіх даних для цього {mediaType}а, включаючи будь-які запити. Якщо цей елемент існує у вашій бібліотеці {mediaServerName}, мультимедійна інформація про нього буде відтворена під час наступного сканування. ", + "components.ManageSlideOver.manageModalClearMediaWarning": "* Це призведе до незворотного видалення всіх даних для цього {mediaType}а, включаючи будь-які запити. Якщо цей елемент існує у вашій бібліотеці {mediaServerName}, мультимедійна інформація про нього буде відтворена під час наступного сканування.", "components.ManageSlideOver.manageModalIssues": "Відкриті проблеми", "components.ManageSlideOver.manageModalMedia": "Media", "components.ManageSlideOver.manageModalMedia4k": "4K Media", "components.ManageSlideOver.manageModalNoRequests": "Запитів немає.", "components.ManageSlideOver.manageModalRequests": "Запити", - "components.ManageSlideOver.manageModalTitle": "Управління {mediaType}", + "components.ManageSlideOver.manageModalTitle": "Управління {mediaType}ом", "components.ManageSlideOver.mark4kavailable": "Позначити як доступний у 4К", "components.ManageSlideOver.markallseasons4kavailable": "Позначити всі сезони як доступні в 4K", - "components.ManageSlideOver.markallseasonsavailable": "Mark All Seasons as Available", + "components.ManageSlideOver.markallseasonsavailable": "Позначити всі сезони як доступні", "components.ManageSlideOver.markavailable": "Позначити як доступний", "components.ManageSlideOver.movie": "фільм", "components.ManageSlideOver.openarr": "Відкрити в {arr}", "components.ManageSlideOver.openarr4k": "Відкрити в 4К {arr}", "components.ManageSlideOver.opentautulli": "Відкрити в Tautulli", "components.ManageSlideOver.pastdays": "Останні {days, number} днів", - "components.ManageSlideOver.playedby": "Грає", - "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {грає} other {грають}}", + "components.ManageSlideOver.playedby": "Переглядає", + "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {перегляд} other {переглядів}}", "components.ManageSlideOver.tvshow": "серіал", "components.MediaSlider.ShowMoreCard.seemore": "Подивитися більше", "components.MovieDetails.MovieCast.fullcast": "Повний акторський склад", @@ -210,7 +210,7 @@ "components.NotificationTypeSelector.mediaapproved": "Схвалення медіа-запитів", "components.NotificationTypeSelector.mediaapprovedDescription": "Надсилати повідомлення, коли медіа-запити схвалюються вручну.", "components.NotificationTypeSelector.mediaautorequested": "Запит надіслано автоматично", - "components.NotificationTypeSelector.mediaautorequestedDescription": "Отримуйте сповіщення, коли нові медіа-запити автоматично надсилаються для елементів у вашому списку спостереження Plex.", + "components.NotificationTypeSelector.mediaautorequestedDescription": "Отримуйте сповіщення, коли нові медіа-запити автоматично надсилаються для елементів у вашому списку перегляду Plex.", "components.NotificationTypeSelector.mediaavailable": "Доступні нові медіафайли", "components.NotificationTypeSelector.mediaavailableDescription": "Надсилати повідомлення, коли запитані медіафайли стають доступними.", "components.NotificationTypeSelector.mediadeclined": "Відхилення медіа-запитів", @@ -245,13 +245,13 @@ "components.PermissionEdit.autoapproveMovies": "Автоматичне схвалення фільмів", "components.PermissionEdit.autoapproveMoviesDescription": "Надати дозвіл на автоматичне схвалення всіх фільмів, відмінних від 4К.", "components.PermissionEdit.autoapproveSeries": "Автоматичне схвалення серіалів", - "components.PermissionEdit.autoapproveSeriesDescription": "Надати дозвіл на автоматичне схвалення всіх серіалів, відмінних від 4К.", + "components.PermissionEdit.autoapproveSeriesDescription": "Надати дозвіл на автоматичне схвалення всіх серіалів, відмінних від 4K.", "components.PermissionEdit.autorequest": "Автоматичний запит", - "components.PermissionEdit.autorequestDescription": "Надайте дозвіл на автоматичне надсилання запитів на медіафайли, відмінні від 4K, через Plex Watchlist.", + "components.PermissionEdit.autorequestDescription": "Надайте дозвіл на автоматичне надсилання запитів на медіафайли, відмінні від 4K, через список перегляду Plex.", "components.PermissionEdit.autorequestMovies": "Автоматичний запит фільмів", - "components.PermissionEdit.autorequestMoviesDescription": "Надайте дозвіл на автоматичне надсилання запитів на фільми, відмінні від 4K, через Plex Watchlist.", + "components.PermissionEdit.autorequestMoviesDescription": "Надайте дозвіл на автоматичне надсилання запитів на фільми, відмінні від 4K, через список перегляду Plex.", "components.PermissionEdit.autorequestSeries": "Автоматичний запит Серіалів", - "components.PermissionEdit.autorequestSeriesDescription": "Надайте дозвіл на автоматичне надсилання запитів на серіали, відмінні від 4K, через Plex Watchlist.", + "components.PermissionEdit.autorequestSeriesDescription": "Надайте дозвіл на автоматичне надсилання запитів на серіали, відмінні від 4K, через список перегляду Plex.", "components.PermissionEdit.createissues": "Повідомлення про проблеми", "components.PermissionEdit.createissuesDescription": "Надати дозвіл на повідомлення про проблеми з медіафайлами.", "components.PermissionEdit.manageissues": "Управління проблемами", @@ -266,7 +266,7 @@ "components.PermissionEdit.request4kTv": "Запити серіалів у 4К", "components.PermissionEdit.request4kTvDescription": "Надати дозвіл на надсилання запитів серіалів у 4К.", "components.PermissionEdit.requestDescription": "Надати дозвіл на надсилання запитів усіх медіафайлів, відмінних від 4К.", - "components.PermissionEdit.requestMovies": "Замовити фільми", + "components.PermissionEdit.requestMovies": "Запити фільмів", "components.PermissionEdit.requestMoviesDescription": "Надати дозвіл на надсилання запитів усіх фільмів, відмінних від 4К.", "components.PermissionEdit.requestTv": "Запити серіалів", "components.PermissionEdit.requestTvDescription": "Надати дозвіл на надсилання запитів усіх серіалів, відмінних від 4К.", @@ -280,8 +280,8 @@ "components.PermissionEdit.viewrecentDescription": "Надайте дозвіл на перегляд списку нещодавно доданих медіа.", "components.PermissionEdit.viewrequests": "Перегляд запитів", "components.PermissionEdit.viewrequestsDescription": "Надати дозвіл на перегляд медіа-запитів, надісланих іншими користувачами.", - "components.PermissionEdit.viewwatchlists": "Перегляньте списки спостереження Plex", - "components.PermissionEdit.viewwatchlistsDescription": "Надайте дозвіл на перегляд списків спостереження Plex інших користувачів.", + "components.PermissionEdit.viewwatchlists": "Перегляд списків переглядів Plex", + "components.PermissionEdit.viewwatchlistsDescription": "Надайте дозвіл на перегляд списків перегляду Plex інших користувачів.", "components.PersonDetails.alsoknownas": "Також відомий(а) як: {names}", "components.PersonDetails.appearsin": "Появи у фільмах та серіалах", "components.PersonDetails.ascharacter": "в ролі {character}", @@ -291,10 +291,10 @@ "components.PlexLoginButton.signingin": "Виконується вхід...", "components.PlexLoginButton.signinwithplex": "Увійти", "components.QuotaSelector.days": "{count, plural, one {день} other {днів}}", - "components.QuotaSelector.movieRequests": "{quotaLimit} {фільмів} за {quotaDays} {днів}", + "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} на {quotaDays} {days}", "components.QuotaSelector.movies": "{count, plural, one {фільм} other {фільми}}", "components.QuotaSelector.seasons": "{count, plural, one {сезон} other {сезони}}", - "components.QuotaSelector.tvRequests": "{quotaLimit} {сезонів} за {quotaDays} {днів}", + "components.QuotaSelector.tvRequests": "{quotaLimit} {seasons} на {quotaDays} {days}", "components.QuotaSelector.unlimited": "Необмежено", "components.RegionSelector.regionDefault": "Всі регіони", "components.RegionSelector.regionServerDefault": "За замовчуванням ({region})", @@ -329,7 +329,7 @@ "components.RequestCard.deleterequest": "Видалити запит", "components.RequestCard.editrequest": "Редагувати запит", "components.RequestCard.failedretry": "Щось пішло не так при спробі повторити запит.", - "components.RequestCard.mediaerror": "Назва, пов'язана з цим запитом, більше недоступна.", + "components.RequestCard.mediaerror": "{mediaType} Не знайдено", "components.RequestCard.seasons": "{seasonCount, plural, one {Сезон} other {Сезони}}", "components.RequestCard.tmdbid": "TMDB ID", "components.RequestCard.tvdbid": "TheTVDB ID", @@ -338,7 +338,7 @@ "components.RequestList.RequestItem.deleterequest": "Видалити запит", "components.RequestList.RequestItem.editrequest": "Редагувати запит", "components.RequestList.RequestItem.failedretry": "Щось пішло не так при спробі повторити запит.", - "components.RequestList.RequestItem.mediaerror": "Назва, пов'язана з цим запитом, більше недоступна.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} Не знайдено", "components.RequestList.RequestItem.modified": "Змінено", "components.RequestList.RequestItem.modifieduserdate": "{date} користувачем {user}", "components.RequestList.RequestItem.requested": "Запрошений", @@ -366,17 +366,17 @@ "components.RequestModal.QuotaDisplay.allowedRequests": "Вам дозволено запитувати {limit} {type} кожні {days} днів.", "components.RequestModal.QuotaDisplay.allowedRequestsUser": "Цьому користувачеві дозволено запитувати {limit} {type} кожні {days} днів.", "components.RequestModal.QuotaDisplay.movie": "фільм", - "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {фільм} other {фільми}}", + "components.RequestModal.QuotaDisplay.movielimit": "{limit, plural, one {фільм} other {фільмів}}", "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Залишилося недостатньо запитів на сезони", "components.RequestModal.QuotaDisplay.quotaLink": "Ви можете переглянути зведення ваших обмежень на кількість запитів на сторінці вашого профілю.", "components.RequestModal.QuotaDisplay.quotaLinkUser": "Ви можете переглянути зведення обмежень на кількість запитів цього користувача на сторінці його профілю.", - "components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {запитів {type} не залишилося} other {залишилось # запиту(ів) {type}}}", - "components.RequestModal.QuotaDisplay.requiredquota": "Вам необхідно мати принаймні {seasons} {seasons, plural, one {запит на сезони} other {запиту(ів) на сезони}} для того , щоб надіслати запит на цей серіал.", + "components.RequestModal.QuotaDisplay.requestsremaining": "{remaining, plural, =0 {Запитів на {type} не залишилося} other {Залишилось # запити(ів) на {type}}}", + "components.RequestModal.QuotaDisplay.requiredquota": "Вам необхідно мати принаймні {seasons} {seasons, plural, one {запит на сезони} other {запиту(ів) на сезони}} для того, щоб надіслати запит на цей серіал.", "components.RequestModal.QuotaDisplay.requiredquotaUser": "Цьому користувачеві необхідно мати принаймні {seasons} {seasons, plural, one {запит на сезони} other {запиту(ів) на сезони}} для того, щоб надіслати запит на цей серіал.", "components.RequestModal.QuotaDisplay.season": "сезон", - "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {сезон} other {сезони}}", - "components.RequestModal.SearchByNameModal.nomatches": "Нам не вдалося знайти відповідність для цієї серії.", - "components.RequestModal.SearchByNameModal.notvdbiddescription": "Ми не змогли автоматично виконати ваш запит. Будь ласка, виберіть правильний збіг зі списку нижче.", + "components.RequestModal.QuotaDisplay.seasonlimit": "{limit, plural, one {сезон} other {сезонів}}", + "components.RequestModal.SearchByNameModal.nomatches": "Нам не вдалося знайти відповідність для цього серіалу.", + "components.RequestModal.SearchByNameModal.notvdbiddescription": "Нам не вдалося автоматично знайти цей серіал. Будь ласка, виберіть правильний збіг зі списку нижче.", "components.RequestModal.alreadyrequested": "Вже запрошений", "components.RequestModal.approve": "Схвалити запит", "components.RequestModal.autoapproval": "Автоматичне схвалення", @@ -385,9 +385,9 @@ "components.RequestModal.errorediting": "Щось пішло не так під час редагування запиту.", "components.RequestModal.extras": "Додатково", "components.RequestModal.numberofepisodes": "# епізодів", - "components.RequestModal.pending4krequest": "", + "components.RequestModal.pending4krequest": "Очікуючий запит в 4К", "components.RequestModal.pendingapproval": "Ваш запит чекає схвалення.", - "components.RequestModal.pendingrequest": "", + "components.RequestModal.pendingrequest": "Очікуючий запит", "components.RequestModal.requestApproved": "Запит на {title} схвалений!", "components.RequestModal.requestCancel": "Запит на {title} скасовано.", "components.RequestModal.requestSuccess": "{title} успішно запрошений!", @@ -404,7 +404,7 @@ "components.RequestModal.requestmovietitle": "Запит на фільм", "components.RequestModal.requestseasons": "Запросити {seasonCount} {seasonCount, plural, one {сезон} other {сезону(ів)}}", "components.RequestModal.requestseasons4k": "Запит {seasonCount} {seasonCount, plural, one {сезону} other {сезонів}} у 4К", - "components.RequestModal.requestseries4ktitle": "Надіслати запит на серіал у 4K", + "components.RequestModal.requestseries4ktitle": "Запросити серіал у 4K", "components.RequestModal.requestseriestitle": "Запит на серіал", "components.RequestModal.season": "Сезон", "components.RequestModal.seasonnumber": "Сезон {number}", @@ -452,7 +452,7 @@ "components.Settings.Notifications.NotificationsPushbullet.accessToken": "Токен доступу", "components.Settings.Notifications.NotificationsPushbullet.accessTokenTip": "Створіть токен у налаштуваннях облікового запису", "components.Settings.Notifications.NotificationsPushbullet.agentEnabled": "Активувати службу", - "components.Settings.Notifications.NotificationsPushbullet.channelTag": "Channel Tag", + "components.Settings.Notifications.NotificationsPushbullet.channelTag": "Тег каналу", "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsFailed": "Не вдалося зберегти налаштування сповіщень Pushbullet.", "components.Settings.Notifications.NotificationsPushbullet.pushbulletSettingsSaved": "Налаштування сповіщень Pushbullet успішно збережено!", "components.Settings.Notifications.NotificationsPushbullet.toastPushbulletTestFailed": "Не вдалося надіслати тестове повідомлення до Pushbullet.", @@ -609,7 +609,7 @@ "components.Settings.RadarrModal.validationRootFolderRequired": "Ви повинні вибрати кореневий каталог", "components.Settings.SettingsAbout.Releases.currentversion": "Поточна", "components.Settings.SettingsAbout.Releases.latestversion": "Остання", - "components.Settings.SettingsAbout.Releases.releasedataMissing": "Дані про дозвіл в даний час недоступні.", + "components.Settings.SettingsAbout.Releases.releasedataMissing": "Дані про реліз наразі недоступні.", "components.Settings.SettingsAbout.Releases.releases": "Релізи", "components.Settings.SettingsAbout.Releases.versionChangelog": "Зміни у версії {version}", "components.Settings.SettingsAbout.Releases.viewchangelog": "Переглянути список змін", @@ -626,7 +626,7 @@ "components.Settings.SettingsAbout.preferredmethod": "Переважний спосіб", "components.Settings.SettingsAbout.runningDevelop": "Ви використовуєте гілку develop проекту Jellyseerr, яка рекомендується тільки для тих, хто робить внесок у розробку або допомагає в тестуванні.", "components.Settings.SettingsAbout.supportoverseerr": "Підтримати Jellyseerr", - "components.Settings.SettingsAbout.timezone": "Годинний пояс", + "components.Settings.SettingsAbout.timezone": "Часовий пояс", "components.Settings.SettingsAbout.totalmedia": "Усього мультимедіа", "components.Settings.SettingsAbout.totalrequests": "Усього запитів", "components.Settings.SettingsAbout.uptodate": "Актуальна", @@ -645,7 +645,7 @@ "components.Settings.SettingsJobsCache.download-sync": "Синхронізувати завантаження", "components.Settings.SettingsJobsCache.download-sync-reset": "Скинути синхронізацію завантажень", "components.Settings.SettingsJobsCache.editJobSchedule": "Змінити завдання", - "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Current Frequency", + "components.Settings.SettingsJobsCache.editJobScheduleCurrent": "Поточна частота", "components.Settings.SettingsJobsCache.editJobSchedulePrompt": "Частота", "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Кожен {jobScheduleHours, plural, one {година} other {{jobScheduleHours} години(ів)}}", "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Кожну {jobScheduleMinutes, plural, one {хвилину} other {{jobScheduleMinutes} хвилин(и)}}", @@ -837,7 +837,7 @@ "components.Settings.sonarrsettings": "Налаштування Sonarr", "components.Settings.ssl": "SSL", "components.Settings.startscan": "Почати сканування", - "components.Settings.tautulliApiKey": "API Key", + "components.Settings.tautulliApiKey": "Ключ API", "components.Settings.tautulliSettings": "Tautulli Налаштування", "components.Settings.tautulliSettingsDescription": "За бажанням налаштуйте параметри для вашого сервера Tautulli. Jellyseerr отримує дані історії переглядів для медіафайлів Plex від Tautulli.", "components.Settings.toastApiKeyFailure": "Щось пішло не так при створенні нового ключа API.", @@ -966,8 +966,8 @@ "components.UserList.user": "Користувач", "components.UserList.usercreatedfailed": "Щось пішло не так при створенні користувача.", "components.UserList.usercreatedfailedexisting": "Вказана адреса електронної пошти вже використовується іншим користувачем.", - "components.UserList.usercreatedsuccess": "Користувач успішно створено!", - "components.UserList.userdeleted": "Користувач успішно видалено!", + "components.UserList.usercreatedsuccess": "Користувача успішно створено!", + "components.UserList.userdeleted": "Користувача успішно видалено!", "components.UserList.userdeleteerror": "Щось пішло не так при видаленні користувача.", "components.UserList.userfail": "Щось пішло не так при збереженні дозволів користувача.", "components.UserList.userlist": "Список користувачів", @@ -996,9 +996,9 @@ "components.UserProfile.UserSettings.UserGeneralSettings.owner": "Власник", "components.UserProfile.UserSettings.UserGeneralSettings.plexuser": "Користувач Plex", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmovies": "Автоматичний запит фільмів", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Автоматично надсилайте запит на перегляд фільмів у своєму списку спостереження Plex", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncmoviestip": "Автоматично запитувати фільми з вашого списку перегляду Plex", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseries": "Автоматичний запит Серіалів", - "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Автоматично надсилайте запит на серіал у своєму списку спостереження Plex", + "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "Автоматично запитувати серіали з вашого списку перегляду Plex", "components.UserProfile.UserSettings.UserGeneralSettings.region": "Регіон для пошуку фільмів та серіалів", "components.UserProfile.UserSettings.UserGeneralSettings.regionTip": "Контент фільтрується за доступністю у вибраному регіоні", "components.UserProfile.UserSettings.UserGeneralSettings.role": "Роль", @@ -1031,7 +1031,7 @@ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Надсилати без звуку", "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Надсилати повідомлення без звуку", "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "ID чату", - "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Почніть чат, додайте @get_id_bot і виконайте команду /my_id", + "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Почніть чат, додайте @get_id_bot і виконайте команду /my_id", "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingsfailed": "Не вдалося зберегти налаштування сповіщень Telegram.", "components.UserProfile.UserSettings.UserNotificationSettings.telegramsettingssaved": "Налаштування сповіщень Telegram успішно збережено!", "components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "Ви повинні надати дійсний ID користувача", @@ -1067,15 +1067,15 @@ "components.UserProfile.UserSettings.menuNotifications": "Сповіщення", "components.UserProfile.UserSettings.menuPermissions": "Дозволи", "components.UserProfile.UserSettings.unauthorizedDescription": "У вас немає дозволу на зміну налаштувань цього користувача.", - "components.UserProfile.emptywatchlist": "Тут з’являться медіафайли, додані до вашого списку спостереження Plex.", + "components.UserProfile.emptywatchlist": "Тут з’являться медіафайли, додані до вашого списку перегляду Plex.", "components.UserProfile.limit": "{remaining} з {limit}", "components.UserProfile.movierequests": "Запитів фільмів", - "components.UserProfile.pastdays": "{type} (за {days} день(ів))", - "components.UserProfile.plexwatchlist": "Список спостереження Plex", + "components.UserProfile.pastdays": "{type} (на {days} дні(в)", + "components.UserProfile.plexwatchlist": "Список перегляду Plex", "components.UserProfile.recentlywatched": "Нещодавно переглянуто", "components.UserProfile.recentrequests": "Останні запити", "components.UserProfile.requestsperdays": "залишилось {limit}", - "components.UserProfile.seriesrequest": "Запитів серіалів", + "components.UserProfile.seriesrequest": "Запитів сезонів", "components.UserProfile.totalrequests": "Усього запитів", "components.UserProfile.unlimited": "Необмежено", "i18n.advanced": "Для просунутих користувачів", @@ -1114,7 +1114,7 @@ "i18n.requested": "Запрошений", "i18n.requesting": "Запит…", "i18n.resolved": "Вирішені", - "i18n.restartRequired": "Restart Required", + "i18n.restartRequired": "Потрібне перезавантаження", "i18n.resultsperpage": "Відобразити {pageSize} результатів на сторінці", "i18n.retry": "Повторити", "i18n.retrying": "Повтор…", @@ -1140,144 +1140,211 @@ "Components.PermissionEdit.requestMovies": "Запити фільмів", "Components.PermissionEdit.autoapprove4kMovies": "Автоматичне схвалення 4К фільмів", "Components.PermissionEdit.autoapproveMovies": "Автоматичне схвалення фільмів", - "components.Discover.FilterSlideover.studio": "Студія", - "components.Discover.RecentlyAddedSlider.recentlyAdded": "Нещодавно додані", - "components.Discover.FilterSlideover.keywords": "Ключові слова", - "components.Discover.FilterSlideover.ratingText": "Оцінки від {minValue} до {maxValue}", - "components.Discover.FilterSlideover.tmdbuserscore": "Оцінка користувачів TMDB", - "components.Discover.DiscoverTv.discovertv": "Серіали", - "components.Discover.FilterSlideover.runtime": "Тривалість", - "components.Discover.FilterSlideover.from": "Від", - "components.Discover.studios": "Студії", - "components.Discover.FilterSlideover.to": "До", - "components.Discover.FilterSlideover.filters": "Фільтри", - "components.Discover.CreateSlider.providetmdbsearch": "Введіть пошуковий запит", - "components.Discover.DiscoverMovieKeyword.keywordMovies": "Фільми {keywordTitle}", - "components.Discover.PlexWatchlistSlider.plexwatchlist": "Ваш список перегляду Plex", - "components.Discover.FilterSlideover.genres": "Жанри", - "components.Discover.FilterSlideover.originalLanguage": "Мова оригіналу", "components.Discover.CreateSlider.nooptions": "Немає результатів.", - "components.Discover.FilterSlideover.tmdbuservotecount": "Кількість голосів користувачів TMDB", - "components.Discover.DiscoverMovies.discovermovies": "Фільми", - "components.Discover.FilterSlideover.clearfilters": "Очистити активні фільтри", + "components.Discover.CreateSlider.searchKeywords": "Ключові слова пошуку…", "components.Discover.CreateSlider.searchStudios": "Пошук студій…", + "components.Discover.CreateSlider.starttyping": "Починайте писати для пошуку.", + "components.Discover.CreateSlider.validationTitlerequired": "Ви повинні вказати назву.", + "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", + "components.Discover.DiscoverMovies.discovermovies": "Фільми", + "components.Discover.DiscoverSliderEdit.enable": "Змінити видимість", + "components.Discover.DiscoverSliderEdit.remove": "Видалити", + "components.Discover.DiscoverTv.discovertv": "Серіали", + "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", + "components.Discover.FilterSlideover.filters": "Фільтри", + "components.Discover.FilterSlideover.from": "Від", + "components.Discover.FilterSlideover.genres": "Жанри", + "components.Discover.FilterSlideover.keywords": "Ключові слова", + "components.Discover.FilterSlideover.originalLanguage": "Оригінальна мова", + "components.Discover.FilterSlideover.ratingText": "Оцінки від {minValue} до {maxValue}", "components.Discover.FilterSlideover.releaseDate": "Дата релізу", + "components.Discover.FilterSlideover.runtime": "Тривалість", + "components.Discover.FilterSlideover.studio": "Студія", + "components.Discover.FilterSlideover.tmdbuserscore": "Оцінка користувачів TMDB", + "components.Discover.FilterSlideover.tmdbuservotecount": "Кількість голосів від користувачів TMDB", + "components.Discover.FilterSlideover.to": "До", + "components.Discover.PlexWatchlistSlider.emptywatchlist": "Медіа додано до вашого списку перегляду Plex.", + "components.Discover.PlexWatchlistSlider.plexwatchlist": "Ваш список перегляду Plex", + "components.Discover.RecentlyAddedSlider.recentlyAdded": "Нещодавно додані", + "components.Discover.studios": "Студії", + "components.Discover.tmdbmoviegenre": "Жанр фільму TMDB", + "components.Discover.tmdbmoviekeyword": "Ключове слово фільму TMDB", + "components.Discover.tmdbmoviestreamingservices": "Сервіси потокової передачі фільмів TMDB", + "components.Discover.tmdbnetwork": "Телеканал TMDB", + "components.Discover.tmdbstudio": "Студія TMDB", + "components.Discover.tmdbtvgenre": "Жанр серіалу TMDB", + "components.Discover.tmdbtvkeyword": "Ключове слово серіалу TMDB", + "components.Discover.tvgenres": "Жанри серіалів", + "components.Layout.UserWarnings.passwordRequired": "Потрібно вказати пароль.", + "components.Login.host": "{mediaServerName} URL", + "components.Login.initialsignin": "Підключитися", + "components.Login.initialsigningin": "Підключення…", + "components.Login.save": "Додати", + "components.Login.signinwithjellyfin": "Використовуйте свій {mediaServerName} обліковий запис", + "components.Login.title": "Додати email", + "components.Login.username": "Ім'я користувача", + "components.ManageSlideOver.removearr4k": "Видалити з 4K {arr}", + "components.MovieDetails.downloadstatus": "Статус завантаження", + "components.MovieDetails.imdbuserscore": "Оцінка користувачів IMDB", + "components.MovieDetails.openradarr4k": "Відкрити фільм у 4К Radarr", + "components.Selector.searchKeywords": "Ключові слова пошуку…", + "components.Selector.searchStudios": "Пошук студій…", + "components.Selector.starttyping": "Початок введення для пошуку.", + "components.Settings.Notifications.NotificationsPushover.sound": "Звук сповіщення", + "components.Settings.SettingsMain.general": "Загальні", + "components.Settings.SettingsMain.generalsettings": "Загальні налаштування", + "components.Settings.SettingsMain.applicationTitle": "Назва програми", + "components.Settings.SettingsMain.applicationurl": "URL програми", + "components.Settings.SettingsMain.cacheImages": "Увімкнути кешування зображень", + "components.Settings.SettingsMain.csrfProtection": "Увімкнути CSRF захист", + "components.Settings.SettingsMain.csrfProtectionHoverTip": "НЕ вмикайте цей параметр, якщо ви не розумієте, що робите!", + "components.Settings.SettingsMain.hideAvailable": "Приховати доступні медіа", + "components.Settings.SettingsMain.locale": "Мова програми", + "components.Settings.SettingsMain.originallanguage": "Мови для пошуку фільмів та серіалів", + "components.Settings.SettingsMain.partialRequestsEnabled": "Дозволити запитувати серіали частково", + "components.Settings.SettingsMain.region": "Регіон для пошуку фільмів та серіалів", + "components.Settings.SettingsMain.regionTip": "Фільтрувати вміст за регіональною доступністю", + "components.Settings.SettingsMain.toastApiKeySuccess": "Новий ключ API успішно згенеровано!", + "components.Settings.SettingsMain.toastSettingsFailure": "Під час збереження налаштувань сталася помилка.", + "components.Settings.SettingsMain.toastSettingsSuccess": "Налаштування успішно збережено!", + "components.Settings.SettingsMain.trustProxyTip": "Дозволити Jellyseerr правильно реєструвати IP-адреси клієнтів за проксі-сервером", + "components.Settings.SettingsMain.validationApplicationUrl": "Ви повинні надати дійсну URL-адресу", + "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL-адреса не має закінчуватися косою рискою", + "components.Settings.SonarrModal.animeSeriesType": "Тип аніме", + "components.Settings.jellyfinSettings": "Налаштування {mediaServerName}", + "components.Settings.jellyfinSettingsSuccess": "Налаштування {mediaServerName} успішно збережено!", + "components.Settings.jellyfinlibraries": "Бібліотеки {mediaServerName}", + "components.Settings.jellyfinsettings": "Налаштування {mediaServerName}", + "components.Setup.signinWithPlex": "Використовуйте свій обліковий запис Plex", + "components.TvDetails.play": "Відтворити в {mediaServerName}", + "components.UserList.mediaServerUser": "Користувач {mediaServerName}", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "Електронна пошта", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Збереження…", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук сповіщення", + "i18n.collection": "Колекція", + "components.Discover.CreateSlider.needresults": "Ви повинні мати принаймні 1 результат.", "components.Discover.CreateSlider.searchGenres": "Пошук жанрів…", + "components.Discover.DiscoverMovieKeyword.keywordMovies": "Фільми {keywordTitle}", + "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", + "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Серіали", + "components.Discover.FilterSlideover.clearfilters": "Очистити всі активні фільтри", "components.Discover.FilterSlideover.runtimeText": "тривалість {minValue}-{maxValue} хвилин", "components.Discover.FilterSlideover.voteCount": "Кількість голосів від {minValue} до {maxValue}", - "components.Discover.DiscoverSliderEdit.remove": "Видалити", - "components.Layout.Sidebar.browsemovies": "Фільми", - "components.MovieDetails.imdbuserscore": "Оцінка користувачів IMDB", - "components.Layout.Sidebar.browsetv": "Серіали", - "components.Discover.DiscoverTv.sortPopularityDesc": "Популярність за спаданням", - "components.Discover.moviegenres": "Жанри фільмів", - "components.Discover.resetwarning": "Скинути всі повзунки до стандартних. Це також видалить будь-які спеціальні повзунки!", - "components.Discover.stopediting": "Зупинити редагування", - "components.Discover.DiscoverMovies.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", - "components.Discover.DiscoverTv.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", - "components.Discover.FilterSlideover.streamingservices": "Сервіси потокового передавання", - "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Активний фільтр} other {# Активні фільтри}}", - "components.Discover.CreateSlider.addSlider": "Додати повзунок", - "components.Discover.tvgenres": "Жанри серіалів", - "components.Discover.tmdbmoviekeyword": "Ключове слово фільму TMDB", - "components.Discover.tmdbtvkeyword": "Ключове слово серіала TMDB", - "components.Discover.tmdbnetwork": "Телеканал TMDB", "components.Discover.networks": "Телеканали", - "components.Discover.tmdbtvgenre": "Жанр серіала TMDB", - "components.Discover.tmdbstudio": "Студія TMDB", - "components.Discover.tmdbtvstreamingservices": "Сервіси потокового передавання серіалів TMDB", - "components.Discover.tmdbmoviestreamingservices": "Сервіси потокової передачі фільмів TMDB", - "components.Discover.resetfailed": "Щось пішло не так під час скидання налаштувань Discover.", - "components.Discover.tmdbsearch": "Пошук TMDB", - "components.Discover.CreateSlider.searchKeywords": "Ключові слова пошуку…", - "components.Discover.tmdbmoviegenre": "Жанр фільму TMDB", - "components.Discover.updatesuccess": "Оновлено параметри налаштування Discover.", - "components.Discover.resetsuccess": "Успішно скинуто параметри налаштування.", - "components.Discover.updatefailed": "Під час оновлення налаштувань Discover сталася помилка.", - "components.Selector.showmore": "Показати більше", - "components.Selector.searchGenres": "Виберіть жанри…", - "components.Selector.searchStudios": "Пошук студій…", - "components.Discover.CreateSlider.addcustomslider": "Створити власний повзунок", - "components.Selector.showless": "Показати менше", - "components.Selector.starttyping": "Початок введення для пошуку.", - "components.Selector.searchKeywords": "Пошук за ключовими словами…", - "components.Selector.nooptions": "Немає результатів.", "components.Discover.resettodefault": "Скинути за замовчуванням", - "components.Settings.SettingsJobsCache.availability-sync": "Синхронізація доступності медіа", + "components.Discover.tmdbsearch": "Пошук TMDB", + "components.Discover.tmdbtvstreamingservices": "Сервіси потокового передавання серіалів TMDB", + "components.Layout.Sidebar.browsemovies": "Фільми", + "components.Layout.Sidebar.browsetv": "Серіали", + "components.Layout.UserWarnings.emailInvalid": "Адреса електронної пошти недійсна.", + "components.Login.saving": "Додавання…", + "components.Login.validationhostformat": "Потрібна дійсна URL-адреса", + "components.Login.validationusernamerequired": "Потрібно ім'я користувача", + "components.ManageSlideOver.removearr": "Видалити з {arr}", + "components.MovieDetails.openradarr": "Відкрити фільм у Radarr", + "components.Selector.nooptions": "Немає результатів.", + "components.Selector.searchGenres": "Виберіть жанри…", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Пристрій за замовчуванням", + "components.Settings.Notifications.userEmailRequired": "Потрібен email користувача", "components.Settings.RadarrModal.tagRequestsInfo": "Автоматично додавати додатковий тег з ID та іменем користувача, який запитує", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Кожну {jobScheduleSeconds, plural, one {секунду} other {{jobScheduleSeconds} секунд}}", - "components.Settings.RadarrModal.tagRequests": "Теги запитів", - "components.Settings.SettingsMain.hideAvailable": "Приховати доступні медіа", - "components.Settings.SettingsMain.regionTip": "Фільтрувати вміст за регіональною доступністю", - "components.Settings.SettingsMain.region": "Регіон для пошуку фільмів та серіалів", - "components.Settings.SettingsMain.trustProxy": "Увімкнути підтримку проксі", - "components.Settings.SettingsMain.toastSettingsSuccess": "Налаштування успішно збережено!", - "components.Settings.SettingsMain.locale": "Мова інтерфейсу", - "components.Settings.SettingsMain.applicationTitle": "Назва програми", - "components.Settings.SettingsMain.originallanguage": "Мови для пошуку фільмів та серіалів", - "components.Settings.SettingsMain.csrfProtection": "Увімкнути захист CSRF", - "components.Settings.SettingsMain.toastApiKeyFailure": "Під час створення нового ключа API сталася помилка.", - "components.Settings.SettingsMain.originallanguageTip": "Фільтрувати вміст за мовою оригіналу", - "components.Settings.SettingsMain.cacheImagesTip": "Кешувати зображення із зовнішніх джерел (потрібний значний об'єм дискового простору)", - "components.Settings.SettingsMain.trustProxyTip": "Дозволити Overseerr правильно реєструвати IP-адреси клієнтів за проксі-сервером", - "components.Settings.SettingsMain.generalsettings": "Загальні налаштування", - "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL-адреса не має закінчуватися скісною рискою", - "components.Settings.SettingsMain.apikey": "Ключ API", - "components.Settings.SettingsMain.generalsettingsDescription": "Налаштуйте глобальні параметри і параметри за замовчуванням для Overseerr.", - "components.Settings.SettingsMain.toastApiKeySuccess": "Новий ключ API успішно згенеровано!", - "components.Settings.SettingsMain.cacheImages": "Увімкнути кешування зображень", - "components.Settings.SettingsMain.applicationurl": "URL програми", - "components.Settings.SettingsMain.general": "Загальні", - "components.Settings.SettingsMain.csrfProtectionHoverTip": "НЕ вмикайте цей параметр, якщо ви не розумієте, що робите!", - "components.Settings.SettingsMain.partialRequestsEnabled": "Дозволити запитувати серіали частково", - "components.Settings.SettingsMain.toastSettingsFailure": "Під час збереження налаштувань сталася помилка.", - "components.Settings.SettingsMain.validationApplicationUrl": "Ви повинні вказати дійсну URL-адресу", - "components.Settings.SettingsMain.validationApplicationTitle": "Ви повинні вказати назву програми", + "components.Settings.SettingsAbout.supportjellyseerr": "Підтримайте Jellyseerr", "components.Settings.SettingsMain.csrfProtectionTip": "Встановіть доступ до зовнішнього API лише для читання (потрібний HTTPS)", - "components.Discover.DiscoverTvKeyword.keywordSeries": "{keywordTitle} Серіали", - "components.Discover.CreateSlider.editsuccess": "Відредаговано повзунок і збережено налаштування Discover.", - "components.Discover.CreateSlider.slidernameplaceholder": "Назва повзунка", + "components.Settings.internalUrl": "Внутрішня URL-адреса", + "components.Settings.SettingsJobsCache.availability-sync": "Синхронізація доступності медіа", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Кожну {jobScheduleSeconds, plural, one {секунду} other {{jobScheduleSeconds} секунд}}", + "components.Settings.SettingsMain.apikey": "Ключ API", + "components.Settings.SettingsMain.toastApiKeyFailure": "Під час створення нового ключа API сталася помилка.", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Пристрій за замовчуванням", + "components.UserList.noJellyfinuserstoimport": "Немає користувачів {mediaServerName} для імпорту.", + "components.UserList.importfromJellyfinerror": "Під час імпорту користувачів з {mediaServerName} сталася помилка.", + "components.Settings.SettingsMain.cacheImagesTip": "Кешувати зображення із зовнішніх джерел (потрібний значний об'єм дискового простору)", + "components.Settings.SettingsMain.validationApplicationTitle": "Ви повинні вказати назву програми", + "components.Settings.SonarrModal.seriesType": "Тип серіалу", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "Користувач {mediaServerName}", + "components.TitleCard.watchlistError": "Щось пішло не так, повторіть спробу.", + "components.Settings.SettingsMain.generalsettingsDescription": "Налаштуйте глобальні параметри та параметри за замовчуванням для Jellyseerr.", + "components.Settings.SettingsMain.originallanguageTip": "Фільтрувати вміст за мовою оригіналу", + "components.Settings.SettingsMain.trustProxy": "Увімкнути підтримку проксі", + "components.Settings.jellyfinSettingsFailure": "Під час збереження налаштувань {mediaServerName} сталася помилка.", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "Зберегти зміни", + "components.TvDetails.play4k": "Відтворити 4K в {mediaServerName}", + "components.Discover.CreateSlider.providetmdbsearch": "Введіть пошуковий запит", + "components.Discover.FilterSlideover.streamingservices": "Сервіси потокового передавання", + "components.Discover.moviegenres": "Жанри фільмів", + "components.MovieDetails.play": "Відтворити в {mediaServerName}", + "components.MovieDetails.play4k": "Відтворити в {mediaServerName} у 4К", + "components.Selector.showless": "Згорнути", + "components.Discover.CreateSlider.addSlider": "Додати повзунок", + "components.Discover.CreateSlider.addcustomslider": "Створити власний повзунок", "components.Discover.CreateSlider.addfail": "Не вдалося створити новий повзунок.", - "components.Discover.CreateSlider.needresults": "Ви повинні мати принаймні 1 результат.", + "components.Discover.DiscoverSliderEdit.deletefail": "Не вдалося видалити повзунок.", + "components.Discover.DiscoverSliderEdit.deletesuccess": "Повзунок успішно видалено.", + "components.Discover.CreateSlider.addsuccess": "Створено новий повзунок і збережено параметри налаштування Discover.", "components.Discover.CreateSlider.editSlider": "Редагувати повзунок", "components.Discover.CreateSlider.editfail": "Не вдалося відредагувати повзунок.", - "components.Discover.CreateSlider.addsuccess": "Створено новий повзунок і збережено параметри налаштування Discover.", - "components.Discover.PlexWatchlistSlider.emptywatchlist": "Медіа додано до вашого списку перегляду Plex.", - "components.Settings.Notifications.NotificationsPushover.sound": "Звук сповіщення", - "components.Discover.customizediscover": "Налаштувати Discover", - "components.Discover.createnewslider": "Створити новий повзунок", - "components.Discover.FilterSlideover.firstAirDate": "Дата виходу в ефір", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Пристрій за замовчуванням", - "components.Settings.SonarrModal.tagRequests": "Теги запитів", - "components.Settings.SonarrModal.tagRequestsInfo": "Автоматично додавати додатковий тег з ID та іменем користувача, який запитує", - "components.Settings.SonarrModal.animeSeriesType": "Тип аніме-серіалу", - "components.Settings.SonarrModal.seriesType": "Тип серіалу", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Пристрій за замовчуванням", - "i18n.collection": "Колекція", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "Звук сповіщення", - "components.Discover.DiscoverMovies.sortPopularityDesc": "Популярність за спаданням", - "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням", - "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Дата випуску за спаданням", - "components.Discover.CreateSlider.providetmdbnetwork": "Введіть TMDB ID мережі", - "components.Discover.DiscoverMovies.sortPopularityAsc": "Популярність за зростанням", - "components.Discover.CreateSlider.validationDatarequired": "Ви повинні надати доступний для пошуку вміст.", - "components.Discover.CreateSlider.providetmdbstudio": "Введіть TMDB ID студії", - "components.Discover.DiscoverMovies.sortTitleDesc": "Назва (Я-А) за спаданням", - "components.Discover.CreateSlider.starttyping": "Початок введення для пошуку.", - "components.Discover.CreateSlider.providetmdbkeywordid": "Введіть TMDB ID ключового слова", - "components.Discover.CreateSlider.validationTitlerequired": "Ви повинні вказати назву.", - "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Дата випуску за зростанням", - "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням", - "components.Discover.DiscoverMovies.sortTitleAsc": "Назва (А-Я) за зростанням", + "components.Discover.CreateSlider.editsuccess": "Відредаговано повзунок і збережено параметри налаштування Discover.", "components.Discover.CreateSlider.providetmdbgenreid": "Введіть TMDB ID жанру", - "components.Discover.DiscoverTv.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням", + "components.Discover.CreateSlider.providetmdbkeywordid": "Введіть TMDB ID ключового слова", + "components.Discover.CreateSlider.providetmdbnetwork": "Введіть TMDB ID мережі", + "components.Discover.CreateSlider.providetmdbstudio": "Введіть TMDB ID студії", + "components.Discover.CreateSlider.slidernameplaceholder": "Назва повзунка", + "components.Discover.CreateSlider.validationDatarequired": "Ви повинні надати доступний для пошуку вміст.", + "components.Discover.DiscoverMovies.sortPopularityAsc": "Популярність за зростанням", + "components.Discover.DiscoverMovies.sortPopularityDesc": "Популярність за спаданням", + "components.Discover.DiscoverMovies.sortReleaseDateAsc": "Дата випуску за зростанням", + "components.Discover.DiscoverMovies.sortReleaseDateDesc": "Дата випуску за спаданням", + "components.Discover.DiscoverMovies.sortTitleAsc": "Назва (А-Я) за зростанням", + "components.Discover.DiscoverMovies.sortTitleDesc": "Назва (Я-А) за спаданням", + "components.Discover.DiscoverMovies.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням", + "components.Discover.DiscoverMovies.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням", "components.Discover.DiscoverTv.sortFirstAirDateAsc": "Дата виходу в ефір за зростанням", - "components.Discover.DiscoverTv.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням", - "components.Discover.DiscoverTv.sortPopularityAsc": "Популярність за зростанням", - "components.Discover.DiscoverTv.sortTitleAsc": "Назва (А-Я) за зростанням", "components.Discover.DiscoverTv.sortFirstAirDateDesc": "Дата виходу в ефір за спаданням", - "components.Discover.DiscoverSliderEdit.deletefail": "Не вдалося видалити повзунок.", - "components.Discover.DiscoverSliderEdit.enable": "Перемкнути видимість", - "components.Discover.DiscoverSliderEdit.deletesuccess": "Повзунок успішно видалено.", - "components.Discover.DiscoverTv.sortTitleDesc": "Назва (Я-А) за спаданням" + "components.Discover.DiscoverTv.sortPopularityAsc": "Популярність за зростанням", + "components.Discover.DiscoverTv.sortPopularityDesc": "Популярність за спаданням", + "components.Discover.DiscoverTv.sortTitleAsc": "Назва (А-Я) за зростанням", + "components.Discover.DiscoverTv.sortTitleDesc": "Назва (Я-А) за спаданням", + "components.Discover.DiscoverTv.sortTmdbRatingAsc": "Рейтинг TMDB за зростанням", + "components.Discover.DiscoverTv.sortTmdbRatingDesc": "Рейтинг TMDB за спаданням", + "components.Discover.FilterSlideover.firstAirDate": "Дата виходу в ефір", + "components.Discover.createnewslider": "Створити новий повзунок", + "components.Discover.customizediscover": "Налаштувати Discover", + "components.Discover.resetfailed": "Щось пішло не так під час скидання налаштувань Discover.", + "components.Discover.resetsuccess": "Успішно скинуто параметри налаштування.", + "components.Discover.resetwarning": "Скинути всі повзунки до стандартних. Це також видалить будь-які спеціальні повзунки!", + "components.Discover.stopediting": "Зупинити редагування", + "components.Discover.updatefailed": "Під час оновлення налаштувань Discover сталася помилка.", + "components.Discover.updatesuccess": "Оновлено параметри налаштування Discover.", + "components.Login.credentialerror": "Ім'я користувача або пароль неправильні.", + "components.Login.description": "Оскільки ви вперше входите в {applicationName}, вам потрібно додати дійсну адресу електронної пошти.", + "components.Login.validationEmailFormat": "Невірний email", + "components.Login.validationEmailRequired": "Ви повинні вказати адресу електронної пошти", + "components.Login.validationemailformat": "Потрібен дійсний email", + "components.Layout.UserWarnings.emailRequired": "Потрібно вказати адресу електронної пошти.", + "components.Login.emailtooltip": "Адресу не потрібно пов’язувати з вашим {mediaServerName} сервером.", + "components.Login.validationhostrequired": "Необхідна URL-адреса {mediaServerName}", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* Це безповоротно видалить цей {mediaType} з {arr}, включаючи всі файли.", + "components.Selector.showmore": "Показати більше", + "components.Settings.RadarrModal.tagRequests": "Теги запитів", + "components.Settings.SonarrModal.tagRequests": "Теги запитів", + "components.Setup.configuremediaserver": "Налаштуйте медіасервер", + "components.Settings.jellyfinsettingsDescription": "Налаштуйте свій {mediaServerName} сервер. {mediaServerName} відсканує бібліотеки, щоб побачити, які бібліотеки доступні.", + "components.Settings.manualscanJellyfin": "Сканувати бібліотеки вручну", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.jellyfinlibrariesDescription": "Бібліотеки {mediaServerName} перевіряються на наявність заголовків. Натисніть нижче, якщо в списку не вистачає бібліотек.", + "components.Settings.saving": "Збереження…", + "components.Settings.syncJellyfin": "Синхронізувати бібліотеки", + "components.Settings.syncing": "Синхронізація", + "components.Setup.signin": "Увійти", + "components.Setup.signinWithJellyfin": "Використовуйте свій {mediaServerName} обліковий запис", + "components.TitleCard.addToWatchList": "Додати в список перегляду", + "components.TitleCard.watchlistSuccess": "{title} успішно додано до списку перегляду!", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} успішно імпортовано!", + "components.UserList.importfromJellyfin": "Додати користувачів з {mediaServerName}", + "components.Settings.manualscanDescriptionJellyfin": "Зазвичай це запускається лише раз на 24 години. Jellyseerr перевірятиме нещодавно доданий сервер {mediaServerName} більш агресивно. Якщо ви вперше налаштовуєте Jellyseerr, рекомендується одноразове повне сканування бібліотеки вручну!", + "components.Settings.save": "Зберегти зміни", + "components.TitleCard.watchlistDeleted": "{title} Успішно видалено зі списку перегляду!", + "components.UserList.newJellyfinsigninenabled": "Параметр Увімкнути новий вхід на {mediaServerName} наразі ввімкнено. Користувачам {mediaServerName} із доступом до бібліотеки не потрібно імпортувати, щоб увійти.", + "components.UserProfile.localWatchlist": "Список перегляду {username}", + "components.Settings.jellyfinSettingsDescription": "Додатково налаштуйте внутрішні та зовнішні кінцеві точки для вашого сервера {mediaServerName}. У більшості випадків зовнішня URL-адреса відрізняється від внутрішньої URL-адреси. Для входу в систему {mediaServerName} також можна встановити спеціальну URL-адресу скидання пароля, якщо ви хочете переспрямувати на іншу сторінку скидання пароля.", + "components.Settings.SonarrModal.tagRequestsInfo": "Автоматично додавати додатковий тег з ID та іменем користувача, який запитує" } diff --git a/src/i18n/locale/zh_Hans.json b/src/i18n/locale/zh_Hans.json index 0ea93511..b09b93d3 100644 --- a/src/i18n/locale/zh_Hans.json +++ b/src/i18n/locale/zh_Hans.json @@ -397,7 +397,7 @@ "components.UserProfile.UserSettings.UserPasswordChange.password": "密码设置", "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "你无权设置此用户的密码。", "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSetOwnAccount": "你的帐户目前没有设置密码。在下方配置密码,使你能够作为“本地用户”登录。", - "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "此用户帐户目前没有设置密码。在下方配置密码,使该帐户能够作为“本地用户”登录。", + "components.UserProfile.UserSettings.UserPasswordChange.noPasswordSet": "此用户帐户目前没有设置密码。配置下面的密码以使此帐户能够作为“本地用户”登录。", "components.UserProfile.UserSettings.UserPasswordChange.newpassword": "新密码", "components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "当前的密码", "components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "确认密码", @@ -766,14 +766,14 @@ "components.RequestButton.viewrequest": "查看请求", "components.RequestButton.requestmore4k": "再提交 4K 请求", "components.RequestButton.requestmore": "提交更多季数的请求", - "components.RequestButton.declinerequests": "拒绝{requestCount, plural, one {请求} other {{requestCount} 个请求}}", + "components.RequestButton.declinerequests": "拒绝{requestCount, plural, one {Request} other {{requestCount} Requests}}", "components.RequestButton.declinerequest4k": "拒绝 4K 请求", "components.RequestButton.declinerequest": "拒绝请求", - "components.RequestButton.decline4krequests": "拒绝{requestCount, plural, one { 4K 请求} other { {requestCount} 个 4K 请求}}", - "components.RequestButton.approverequests": "批准{requestCount, plural, one {请求} other {{requestCount} 个请求}}", + "components.RequestButton.decline4krequests": "拒绝 {requestCount, plural, one {4K Request} other {{requestCount} 4K Requests}}", + "components.RequestButton.approverequests": "批准 {requestCount, plural, one {Request} other {{requestCount} Requests}}", "components.RequestButton.approverequest4k": "批准 4K 请求", "components.RequestButton.approverequest": "批准请求", - "components.RequestButton.approve4krequests": "批准{requestCount, plural, one { 4K 请求} other { {requestCount} 个 4K 请求}}", + "components.RequestButton.approve4krequests": "批准 {requestCount, plural, one {4K Request} other {{requestCount} 4K Requests}}", "components.RequestBlock.server": "目標服务器", "components.RequestBlock.seasons": "季数", "components.RequestBlock.rootfolder": "根目录", @@ -782,10 +782,10 @@ "components.RegionSelector.regionServerDefault": "默认设置({region})", "components.RegionSelector.regionDefault": "所有地区", "components.QuotaSelector.unlimited": "无限", - "components.QuotaSelector.tvRequests": "每 {quotaDays} {days} {quotaLimit} {seasons}", + "components.QuotaSelector.tvRequests": "{quotaLimit} {seasons} 每 {quotaDays} {days}", "components.QuotaSelector.seasons": "季", "components.QuotaSelector.movies": "部电影", - "components.QuotaSelector.movieRequests": "每 {quotaDays} {days} {quotaLimit} {movies}", + "components.QuotaSelector.movieRequests": "{quotaLimit} {movies} 每 {quotaDays} {days}", "components.QuotaSelector.days": "天", "components.PlexLoginButton.signinwithplex": "登入", "components.PlexLoginButton.signingin": "登入中…", @@ -900,7 +900,7 @@ "components.Settings.Notifications.NotificationsGotify.toastGotifyTestFailed": "Gotify 测试通知发送失败。", "components.Settings.Notifications.NotificationsGotify.toastGotifyTestSending": "Gotify测试通知发送中…", "components.Settings.Notifications.enableMentions": "允许提及", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "每 {jobScheduleMinutes} 分钟", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "每 {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}", "components.UserProfile.UserSettings.UserNotificationSettings.pushoverApplicationToken": "应用 API 令牌", "components.UserList.newplexsigninenabled": "允许新的 Plex 用户登录 设置目前已启用。还没有导入的Plex用户也能登录。", "components.UserProfile.UserSettings.UserNotificationSettings.pushoversettingsfailed": "Pushover 通知设置保存失败。", @@ -909,7 +909,7 @@ "components.Settings.RadarrModal.inCinemas": "已上映", "components.UserProfile.UserSettings.UserNotificationSettings.pushbulletAccessTokenTip": "从您的账号设置获取API令牌", "components.Settings.SettingsAbout.appDataPath": "数据目录", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "每 {jobScheduleHours} 小时", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "每 {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}", "components.Settings.tautulliSettings": "Tautulli 设置", "components.Settings.tautulliSettingsDescription": "关于 Tautulli 服务器的设置。Jellyseerr 会从 Tautulli 获取 Plex 媒体的观看历史记录。", "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "Discord 用户ID", @@ -1063,8 +1063,8 @@ "components.Settings.restartrequiredTooltip": "必须重新启动 Jellyseerr 才能使更改的设置生效", "components.TvDetails.manageseries": "管理电视节目", "components.UserProfile.UserSettings.UserGeneralSettings.plexwatchlistsyncseriestip": "自动请求您的 Plex 关注列表的媒体", - "components.AirDateBadge.airedrelative": "播出{relativeTime}", - "components.AirDateBadge.airsrelative": "播出{relativeTime}", + "components.AirDateBadge.airedrelative": "{relativeTime}播出", + "components.AirDateBadge.airsrelative": "{relativeTime}播出", "components.Layout.UserDropdown.MiniQuotaDisplay.movierequests": "电影请求", "components.Layout.UserDropdown.MiniQuotaDisplay.seriesrequests": "电视节目请求", "components.NotificationTypeSelector.mediaautorequestedDescription": "当 Plex 关注列表中的项目自动提交新媒体请求时,会收到通知。", @@ -1203,7 +1203,7 @@ "components.Discover.PlexWatchlistSlider.emptywatchlist": "您的 Plex 关注列表中的媒体会显示在这里。", "components.Selector.starttyping": "开始打字以进行搜索。", "components.Discover.CreateSlider.starttyping": "开始打字以进行搜索。", - "components.Discover.CreateSlider.needresults": "需要至少有 1 个结果。", + "components.Discover.CreateSlider.needresults": "你需要至少有 1 个结果。", "components.Selector.showless": "显示更少", "components.Discover.resetfailed": "重置探索媒体设置时出了点问题。", "components.Settings.SettingsMain.validationApplicationTitle": "你必须提供一个应用程序标题", @@ -1253,7 +1253,7 @@ "components.Settings.SettingsJobsCache.availability-sync": "同步媒体可用性", "components.Discover.tmdbmoviestreamingservices": "TMDB 电影流媒体服务", "components.Discover.tmdbtvstreamingservices": "TMDB 电视流媒体服务", - "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "每 {jobScheduleSeconds} 秒", + "components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "每 {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}", "components.Discover.FilterSlideover.voteCount": "在 {minValue} 和 {maxValue} 之间的评分数", "components.Settings.RadarrModal.tagRequests": "标签请求", "components.Settings.RadarrModal.tagRequestsInfo": "自动添加带有请求者的用户 ID 和显示名称的附加标签", @@ -1261,11 +1261,83 @@ "i18n.collection": "合集", "components.Discover.FilterSlideover.tmdbuservotecount": "TMDB 用户评分数", "components.Settings.SonarrModal.tagRequestsInfo": "自动添加带有请求者的用户 ID 和显示名称的附加标签", - "components.MovieDetails.imdbuserscore": "IMDB 用户评分", - "components.Settings.Notifications.NotificationsPushover.sound": "通知提示音", - "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "默认设备", - "components.Settings.SonarrModal.animeSeriesType": "动漫剧集类型", - "components.Settings.SonarrModal.seriesType": "剧集类型", - "components.UserProfile.UserSettings.UserNotificationSettings.sound": "通知提示音", - "components.Settings.Notifications.NotificationsPushover.deviceDefault": "默认设备" + "components.Layout.UserWarnings.passwordRequired": "需要输入密码。", + "components.Login.emailtooltip": "地址不需要与{mediaServerName}实例相关联。", + "components.Login.host": "{mediaServerName} 的 URL", + "components.Login.initialsignin": "连接", + "components.Login.initialsigningin": "连接中……", + "components.Login.save": "添加", + "components.Login.saving": "添加中……", + "components.Login.signinwithjellyfin": "使用您的{mediaServerName}帐户", + "components.Login.title": "添加邮件", + "components.Login.username": "用户名", + "components.Login.validationEmailFormat": "无效的邮件地址", + "components.Login.validationEmailRequired": "你必须提供一个电子邮件", + "components.Login.validationemailformat": "需要有效的电子邮件", + "components.Login.validationhostformat": "需要有效的URL", + "components.Login.validationusernamerequired": "需要用户名", + "components.ManageSlideOver.manageModalRemoveMediaWarning": "* 这将不可逆地从{arr}中删除{mediaType},包括所有文件。", + "components.ManageSlideOver.removearr4k": "移除4K {arr}", + "components.MovieDetails.downloadstatus": "下载状态", + "components.MovieDetails.imdbuserscore": "IMDB用户评分", + "components.MovieDetails.openradarr": "在Radarr中打开电影", + "components.MovieDetails.play": "播放{mediaServerName}", + "components.MovieDetails.play4k": "播放 4K {mediaServerName}", + "components.Settings.Notifications.NotificationsPushover.sound": "通知声音", + "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Jellyfin全库扫描", + "components.Settings.SonarrModal.seriesType": "系列类型", + "components.Settings.jellyfinSettingsFailure": "保存{mediaServerName}设置时出错。", + "components.Settings.jellyfinSettingsSuccess": "{mediaServerName}设置保存成功!", + "components.Settings.jellyfinlibraries": "{mediaServerName}库", + "components.Settings.jellyfinlibrariesDescription": "库{mediaServerName}用于扫描标题。如果没有列出库,请单击下面的按钮。", + "components.Settings.jellyfinsettings": "{mediaServerName}设置", + "components.Settings.manualscanJellyfin": "手动扫描库", + "components.Settings.menuJellyfinSettings": "{mediaServerName}", + "components.Settings.saving": "保存中……", + "components.Settings.syncJellyfin": "同步库", + "components.Settings.syncing": "同步中", + "components.Settings.timeout": "超时", + "components.Setup.signin": "登录", + "components.Setup.signinWithJellyfin": "使用您的{mediaServerName}帐户", + "components.TitleCard.addToWatchList": "添加到监视列表", + "components.TitleCard.watchlistDeleted": "{title}从监视列表中删除成功!", + "components.TitleCard.watchlistError": "出了问题,再试一次。", + "components.TvDetails.play": "在 {mediaServerName} 播放", + "components.TvDetails.play4k": "mediaServerName} 播放 4K", + "components.UserList.importfrommediaserver": "导入{mediaServerName}用户", + "components.UserList.mediaServerUser": "{mediaServerName} 用户", + "components.UserList.noJellyfinuserstoimport": "在{mediaServerName}中没有用户要导入。", + "components.UserList.newJellyfinsigninenabled": "启用 {mediaServerName} 登录 设置当前已启用. {mediaServerName} 具有库访问权限的用户不需要导入即可登录。", + "components.UserProfile.UserSettings.UserGeneralSettings.mediaServerUser": "{mediaServerName} 用户", + "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "设备默认", + "components.Layout.UserWarnings.emailInvalid": "邮件地址无效。", + "components.Layout.UserWarnings.emailRequired": "需要填写电子邮件地址。", + "components.Login.credentialerror": "用户名或密码错误。", + "components.Login.description": "由于这是您第一次登录{applicationName},您需要添加一个有效的电子邮件地址。", + "components.Login.validationhostrequired": "{mediaServerName} URL是必需的", + "components.ManageSlideOver.removearr": "从{arr}中删除", + "components.MovieDetails.openradarr4k": "在 4K Radarr 中打开电影", + "components.Settings.Notifications.NotificationsPushover.deviceDefault": "设备默认", + "components.Settings.Notifications.userEmailRequired": "获取用户邮箱", + "components.Settings.SettingsAbout.supportjellyseerr": "支持Jellyseerr", + "components.Settings.SettingsJobsCache.jellyfin-recently-added-scan": "Jellyfin最近新增扫描", + "components.Settings.SonarrModal.animeSeriesType": "动漫系列", + "components.Settings.internalUrl": "内部URL", + "components.Settings.jellyfinSettings": "{mediaServerName}设置", + "components.Settings.jellyfinSettingsDescription": "可以为您的{mediaServerName}服务器配置内部和外部端点。在大多数情况下,外部URL与内部URL不同。如果你想重定向到不同的密码重置页面,也可以为{mediaServerName}登录设置自定义密码重置URL。", + "components.Settings.jellyfinsettingsDescription": "配置{mediaServerName}服务器的设置。{mediaServerName}扫描{mediaServerName}库以查看可用的内容。", + "components.Settings.manualscanDescriptionJellyfin": "正常情况下,每24小时只会运行一次。Jellyseerr将更积极地检查您的{mediaServerName}服务器最近添加的内容。如果这是您第一次配置Jellyseerr,建议您手动进行一次完整的库扫描!", + "components.Settings.save": "保存更改", + "components.Setup.configuremediaserver": "配置媒体服务器", + "components.Setup.signinWithPlex": "使用您的 Plex 帐户", + "components.TitleCard.watchlistCancel": "{title}的监视列表已取消。", + "components.TitleCard.watchlistSuccess": "{title}添加到监视列表成功!", + "components.UserList.importfromJellyfin": "导入{mediaServerName}用户", + "components.UserProfile.UserSettings.UserGeneralSettings.email": "电子邮件", + "components.UserProfile.UserSettings.UserGeneralSettings.save": "保存更改", + "components.UserList.importfromJellyfinerror": "导入{mediaServerName}用户时出错。", + "components.UserProfile.UserSettings.UserNotificationSettings.sound": "通知声音", + "components.UserProfile.UserSettings.UserGeneralSettings.saving": "保存中……", + "components.UserProfile.localWatchlist": "{username}的监视列表", + "components.UserList.importedfromJellyfin": "{userCount} {mediaServerName} {userCount, plural, one {user} other {users}} 导入成功!" } From 80f63017ac5e9b1720a19c761dbef4dd517f1c2c Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 19 Aug 2024 06:02:52 +0200 Subject: [PATCH 28/53] fix: handle status badge for season packs (#927) * fix: handle status badge for season packs When a series is downloaded with a season pack, the status tooltip displays only the name of the first episode as a title, and displays a list of all episodes as a description, with the same file being repeated for each episode. This PR fixes this, using the season number as the tooltip title and showing only the season pack file currently being downloaded. * fix: add missing i18n translation --- server/lib/downloadtracker.ts | 3 + src/components/StatusBadge/index.tsx | 132 +++++++++++++++++++-------- src/i18n/locale/en.json | 1 + 3 files changed, 96 insertions(+), 40 deletions(-) diff --git a/server/lib/downloadtracker.ts b/server/lib/downloadtracker.ts index cf29313e..e948c580 100644 --- a/server/lib/downloadtracker.ts +++ b/server/lib/downloadtracker.ts @@ -20,6 +20,7 @@ export interface DownloadingItem { timeLeft: string; estimatedCompletionTime: Date; title: string; + downloadId: string; episode?: EpisodeNumberResult; } @@ -95,6 +96,7 @@ class DownloadTracker { status: item.status, timeLeft: item.timeleft, title: item.title, + downloadId: item.downloadId, })); if (queueItems.length > 0) { @@ -172,6 +174,7 @@ class DownloadTracker { timeLeft: item.timeleft, title: item.title, episode: item.episode, + downloadId: item.downloadId, })); if (queueItems.length > 0) { diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index e7cbbbba..0061a903 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -18,6 +18,7 @@ const messages = defineMessages('components.StatusBadge', { playonplex: 'Play on {mediaServerName}', openinarr: 'Open in {arr}', managemedia: 'Manage {mediaType}', + seasonnumber: 'S{seasonNumber}', seasonepisodenumber: 'S{seasonNumber}E{episodeNumber}', }); @@ -107,22 +108,34 @@ const StatusBadge = ({ } } - const tooltipContent = ( -
    - {downloadItem.map((status, index) => ( -
  • - -
  • - ))} -
- ); + const tooltipContent = + mediaType === 'tv' && + downloadItem.length > 1 && + downloadItem.every( + (item) => + item.downloadId && item.downloadId === downloadItem[0].downloadId + ) ? ( + + ) : ( +
    + {downloadItem.map((status, index) => ( +
  • + +
  • + ))} +
+ ); const badgeDownloadProgress = (
{inProgress && ( <> - {mediaType === 'tv' && downloadItem[0].episode && ( - - {intl.formatMessage(messages.seasonepisodenumber, { - seasonNumber: downloadItem[0].episode.seasonNumber, - episodeNumber: downloadItem[0].episode.episodeNumber, - })} - - )} + {mediaType === 'tv' && + downloadItem[0].episode && + (downloadItem.length > 1 && + downloadItem.every( + (item) => + item.downloadId && + item.downloadId === downloadItem[0].downloadId + ) ? ( + + {intl.formatMessage(messages.seasonnumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + })} + + ) : ( + + {intl.formatMessage(messages.seasonepisodenumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + episodeNumber: downloadItem[0].episode.episodeNumber, + })} + + ))} )} @@ -230,14 +256,27 @@ const StatusBadge = ({ {inProgress && ( <> - {mediaType === 'tv' && downloadItem[0].episode && ( - - {intl.formatMessage(messages.seasonepisodenumber, { - seasonNumber: downloadItem[0].episode.seasonNumber, - episodeNumber: downloadItem[0].episode.episodeNumber, - })} - - )} + {mediaType === 'tv' && + downloadItem[0].episode && + (downloadItem.length > 1 && + downloadItem.every( + (item) => + item.downloadId && + item.downloadId === downloadItem[0].downloadId + ) ? ( + + {intl.formatMessage(messages.seasonnumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + })} + + ) : ( + + {intl.formatMessage(messages.seasonepisodenumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + episodeNumber: downloadItem[0].episode.episodeNumber, + })} + + ))} )} @@ -283,14 +322,27 @@ const StatusBadge = ({ {inProgress && ( <> - {mediaType === 'tv' && downloadItem[0].episode && ( - - {intl.formatMessage(messages.seasonepisodenumber, { - seasonNumber: downloadItem[0].episode.seasonNumber, - episodeNumber: downloadItem[0].episode.episodeNumber, - })} - - )} + {mediaType === 'tv' && + downloadItem[0].episode && + (downloadItem.length > 1 && + downloadItem.every( + (item) => + item.downloadId && + item.downloadId === downloadItem[0].downloadId + ) ? ( + + {intl.formatMessage(messages.seasonnumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + })} + + ) : ( + + {intl.formatMessage(messages.seasonepisodenumber, { + seasonNumber: downloadItem[0].episode.seasonNumber, + episodeNumber: downloadItem[0].episode.episodeNumber, + })} + + ))} )} diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index f34aa5b4..5c9aa6fe 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1056,6 +1056,7 @@ "components.StatusBadge.openinarr": "Open in {arr}", "components.StatusBadge.playonplex": "Play on {mediaServerName}", "components.StatusBadge.seasonepisodenumber": "S{seasonNumber}E{episodeNumber}", + "components.StatusBadge.seasonnumber": "S{seasonNumber}", "components.StatusBadge.status": "{status}", "components.StatusBadge.status4k": "4K {status}", "components.StatusChecker.appUpdated": "{applicationTitle} Updated", From cfd1bc253557d6e19725743b8aa9a2fa33bbe760 Mon Sep 17 00:00:00 2001 From: Joaquin Olivero <66050823+JoaquinOlivero@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:37:04 -0300 Subject: [PATCH 29/53] feat: adds status filter for tv shows (#796) re #605 Co-authored-by: JoaquinOlivero --- overseerr-api.yml | 5 ++ server/api/themoviedb/index.ts | 3 + server/routes/discover.ts | 2 + .../Discover/FilterSlideover/index.tsx | 19 +++++ src/components/Discover/constants.ts | 5 ++ src/components/Selector/index.tsx | 76 +++++++++++++++++++ src/i18n/locale/en.json | 8 ++ 7 files changed, 118 insertions(+) diff --git a/overseerr-api.yml b/overseerr-api.yml index 3cb42284..8f916708 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -4862,6 +4862,11 @@ paths: schema: type: string example: 8|9 + - in: query + name: status + schema: + type: string + example: 3|4 responses: '200': description: Results diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index 922ff90f..6f13ec08 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -95,6 +95,7 @@ interface DiscoverTvOptions { sortBy?: SortOptions; watchRegion?: string; watchProviders?: string; + withStatus?: string; // Returning Series: 0 Planned: 1 In Production: 2 Ended: 3 Cancelled: 4 Pilot: 5 } class TheMovieDb extends ExternalAPI { @@ -523,6 +524,7 @@ class TheMovieDb extends ExternalAPI { voteCountLte, watchProviders, watchRegion, + withStatus, }: DiscoverTvOptions = {}): Promise => { try { const defaultFutureDate = new Date( @@ -570,6 +572,7 @@ class TheMovieDb extends ExternalAPI { 'vote_count.lte': voteCountLte || '', with_watch_providers: watchProviders || '', watch_region: watchRegion || '', + with_status: withStatus || '', }); return data; diff --git a/server/routes/discover.ts b/server/routes/discover.ts index 9590d32b..55a844ad 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -71,6 +71,7 @@ const QueryFilterOptions = z.object({ network: z.coerce.string().optional(), watchProviders: z.coerce.string().optional(), watchRegion: z.coerce.string().optional(), + status: z.coerce.string().optional(), }); export type FilterOptions = z.infer; @@ -385,6 +386,7 @@ discoverRoutes.get('/tv', async (req, res, next) => { voteCountLte: query.voteCountLte, watchProviders: query.watchProviders, watchRegion: query.watchRegion, + withStatus: query.status, }); const media = await Media.getRelatedMedia( diff --git a/src/components/Discover/FilterSlideover/index.tsx b/src/components/Discover/FilterSlideover/index.tsx index d7029fb2..7df6d55a 100644 --- a/src/components/Discover/FilterSlideover/index.tsx +++ b/src/components/Discover/FilterSlideover/index.tsx @@ -8,6 +8,7 @@ import { CompanySelector, GenreSelector, KeywordSelector, + StatusSelector, WatchProviderSelector, } from '@app/components/Selector'; import useSettings from '@app/hooks/useSettings'; @@ -40,6 +41,7 @@ const messages = defineMessages('components.Discover.FilterSlideover', { runtime: 'Runtime', streamingservices: 'Streaming Services', voteCount: 'Number of votes between {minValue} and {maxValue}', + status: 'Status', }); type FilterSlideoverProps = { @@ -150,6 +152,23 @@ const FilterSlideover = ({ updateQueryParams('genre', value?.map((v) => v.value).join(',')); }} /> + {type === 'tv' && ( + <> + + {intl.formatMessage(messages.status)} + + { + updateQueryParams( + 'status', + value?.map((v) => v.value).join('|') + ); + }} + /> + + )} {intl.formatMessage(messages.keywords)} diff --git a/src/components/Discover/constants.ts b/src/components/Discover/constants.ts index 42d0dab0..c123c927 100644 --- a/src/components/Discover/constants.ts +++ b/src/components/Discover/constants.ts @@ -108,6 +108,7 @@ export const QueryFilterOptions = z.object({ voteCountGte: z.string().optional(), watchRegion: z.string().optional(), watchProviders: z.string().optional(), + status: z.string().optional(), }); export type FilterOptions = z.infer; @@ -147,6 +148,10 @@ export const prepareFilterValues = ( filterValues.genre = values.genre; } + if (values.status) { + filterValues.status = values.status; + } + if (values.keywords) { filterValues.keywords = values.keywords; } diff --git a/src/components/Selector/index.tsx b/src/components/Selector/index.tsx index ba40c991..a371b7f9 100644 --- a/src/components/Selector/index.tsx +++ b/src/components/Selector/index.tsx @@ -33,6 +33,13 @@ const messages = defineMessages('components.Selector', { nooptions: 'No results.', showmore: 'Show More', showless: 'Show Less', + searchStatus: 'Select status...', + returningSeries: 'Returning Series', + planned: 'Planned', + inProduction: 'In Production', + ended: 'Ended', + canceled: 'Canceled', + pilot: 'Pilot', }); type SingleVal = { @@ -204,6 +211,75 @@ export const GenreSelector = ({ ); }; +export const StatusSelector = ({ + isMulti, + defaultValue, + onChange, +}: BaseSelectorMultiProps | BaseSelectorSingleProps) => { + const intl = useIntl(); + const [defaultDataValue, setDefaultDataValue] = useState< + { label: string; value: number }[] | null + >(null); + + const options = useMemo( + () => [ + { name: intl.formatMessage(messages.returningSeries), id: 0 }, + { name: intl.formatMessage(messages.planned), id: 1 }, + { name: intl.formatMessage(messages.inProduction), id: 2 }, + { name: intl.formatMessage(messages.ended), id: 3 }, + { name: intl.formatMessage(messages.canceled), id: 4 }, + { name: intl.formatMessage(messages.pilot), id: 5 }, + ], + [intl] + ); + + useEffect(() => { + const loadDefaultStatus = async (): Promise => { + if (!defaultValue) { + return; + } + const statuses = defaultValue.split('|'); + + const statusData = options + .filter((opt) => statuses.find((s) => Number(s) === opt.id)) + .map((o) => ({ + label: o.name, + value: o.id, + })); + + setDefaultDataValue(statusData); + }; + + loadDefaultStatus(); + }, [defaultValue, options]); + + const loadStatusOptions = async () => { + return options + .map((result) => ({ + label: result.name, + value: result.id, + })) + .filter(({ label }) => label.toLowerCase()); + }; + + return ( + { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onChange(value as any); + }} + /> + ); +}; + export const KeywordSelector = ({ isMulti, defaultValue, diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 5c9aa6fe..0c07003f 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -73,6 +73,7 @@ "components.Discover.FilterSlideover.releaseDate": "Release Date", "components.Discover.FilterSlideover.runtime": "Runtime", "components.Discover.FilterSlideover.runtimeText": "{minValue}-{maxValue} minute runtime", + "components.Discover.FilterSlideover.status": "Status", "components.Discover.FilterSlideover.streamingservices": "Streaming Services", "components.Discover.FilterSlideover.studio": "Studio", "components.Discover.FilterSlideover.tmdbuserscore": "TMDB User Score", @@ -555,9 +556,16 @@ "components.ResetPassword.validationpasswordrequired": "You must provide a password", "components.Search.search": "Search", "components.Search.searchresults": "Search Results", + "components.Selector.canceled": "Canceled", + "components.Selector.ended": "Ended", + "components.Selector.inProduction": "In Production", "components.Selector.nooptions": "No results.", + "components.Selector.pilot": "Pilot", + "components.Selector.planned": "Planned", + "components.Selector.returningSeries": "Returning Series", "components.Selector.searchGenres": "Select genres…", "components.Selector.searchKeywords": "Search keywords…", + "components.Selector.searchStatus": "Select status...", "components.Selector.searchStudios": "Search studios…", "components.Selector.showless": "Show Less", "components.Selector.showmore": "Show More", From 15cb949f1f2e617853f90ae7bb8ae5d6622f610e Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Wed, 21 Aug 2024 02:35:47 +0500 Subject: [PATCH 30/53] feat: Jellyfin/Emby server type setup (#685) * feat: add Media Server Selection to Setup Page Introduce the ability to select the media server type on the setup page. Users can now choose their preferred media server (e.g., Plex through the Plex sign-in or Emby/Jellyfin sign-in to select either Emby or Jellyfin). The selected media server type is then reflected in the application settings. This enhancement provides users with increased flexibility and customization options during the initial setup process, eliminating the need to rely on environment variables (which cannot be set if using platforms like snaps). Existing Emby users, who use the environment variable, should log out and log back in after updating to set their mediaServerType to Emby. BREAKING CHANGE: This commit deprecates the JELLYFIN_TYPE variable to identify Emby media server and instead rely on the mediaServerType that is set in the `settings.json`. Existing environment variable users can log out and log back in to set the mediaServerType to `3` (Emby). * feat(api): add severType to the api BREAKING CHANGE: This adds a serverType to the `/auth/jellyfin` which requires a serverType to be set (`jellyfin`/`emby`) * refactor: use enums for serverType and rename selectedservice to serverType * refactor(auth): jellyfin/emby authentication to set MediaServerType * fix: issue page formatMessage for 4k media * refactor: cleaner way of handling serverType change using MediaServerType instead of strings instead of using strings now it will use MediaServerType enums for serverType * revert: removed conditional render of the auto-request permission reverts the conditional render toshow the auto-request permission if the mediaServerType was set to Plex as this should be handled in a different PR and Cypress tests should be modified accordingly(currently cypress test would fail if this conditional check is there) * feat: add server type step to setup * feat: migrate existing emby setups to use emby mediaServerType * fix: scan jobs not running when media server type is emby * fix: emby media server type migration * refactor: change emby logo to full logo * style: decrease emby logo size in setup screen * refactor: use title case for servertype i18n message * refactor(i18n): fix a typo * refactor: use enums instead of numbers * fix: remove old references to JELLYFIN_TYPE environment variable * fix: go back to the last step when refresh the setup page * fix: move "scanning in background" tip next to the scanning section * fix: redirect the setup page when Jellyseerr is already setup --------- Co-authored-by: Gauthier --- next.config.js | 4 - overseerr-api.yml | 2 + server/constants/server.ts | 5 + server/constants/user.ts | 1 + server/entity/Media.ts | 5 +- server/lib/scanners/jellyfin/index.ts | 5 +- .../migrations/0002_emby_media_server_type.ts | 17 ++ server/routes/auth.ts | 92 +++++---- src/assets/services/emby-icon-only.svg | 47 +++++ src/assets/services/emby.svg | 177 +++++++++++++----- src/components/ExternalLinkBlock/index.tsx | 5 +- src/components/IssueDetails/index.tsx | 14 +- src/components/Login/JellyfinLogin.tsx | 65 ++++--- src/components/Login/index.tsx | 21 ++- src/components/ManageSlideOver/index.tsx | 5 +- src/components/MovieDetails/index.tsx | 8 +- src/components/Settings/SettingsJellyfin.tsx | 122 ++++++------ .../Settings/SettingsJobsCache/index.tsx | 17 +- src/components/Settings/SettingsLayout.tsx | 8 +- .../Settings/SettingsUsers/index.tsx | 51 +++-- src/components/Setup/SetupLogin.tsx | 128 ++++++------- src/components/Setup/index.tsx | 134 ++++++++++--- src/components/StatusBadge/index.tsx | 4 +- src/components/TvDetails/index.tsx | 10 +- .../UserList/JellyfinImportModal.tsx | 22 ++- src/components/UserList/index.tsx | 7 +- .../UserGeneralSettings/index.tsx | 4 +- src/i18n/locale/en.json | 14 +- src/pages/settings/jellyfin.tsx | 2 +- src/styles/globals.css | 10 + 30 files changed, 663 insertions(+), 343 deletions(-) create mode 100644 server/lib/settings/migrations/0002_emby_media_server_type.ts create mode 100644 src/assets/services/emby-icon-only.svg diff --git a/next.config.js b/next.config.js index 380eb897..35a316c6 100644 --- a/next.config.js +++ b/next.config.js @@ -6,10 +6,6 @@ module.exports = { commitTag: process.env.COMMIT_TAG || 'local', forceIpv4First: process.env.FORCE_IPV4_FIRST === 'true' ? 'true' : 'false', }, - publicRuntimeConfig: { - // Will be available on both server and client - JELLYFIN_TYPE: process.env.JELLYFIN_TYPE, - }, images: { remotePatterns: [ { hostname: 'gravatar.com' }, diff --git a/overseerr-api.yml b/overseerr-api.yml index 8f916708..d2403538 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -3586,6 +3586,8 @@ paths: type: string email: type: string + serverType: + type: number required: - username - password diff --git a/server/constants/server.ts b/server/constants/server.ts index 7b2f9f1f..eed1939f 100644 --- a/server/constants/server.ts +++ b/server/constants/server.ts @@ -4,3 +4,8 @@ export enum MediaServerType { EMBY, NOT_CONFIGURED, } + +export enum ServerType { + JELLYFIN = 'Jellyfin', + EMBY = 'Emby', +} diff --git a/server/constants/user.ts b/server/constants/user.ts index 5a0a4bd5..90b33dc8 100644 --- a/server/constants/user.ts +++ b/server/constants/user.ts @@ -2,4 +2,5 @@ export enum UserType { PLEX = 1, LOCAL = 2, JELLYFIN = 3, + EMBY = 4, } diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 102185be..723eb213 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -211,9 +211,10 @@ class Media { } } else { const pageName = - process.env.JELLYFIN_TYPE === 'emby' ? 'item' : 'details'; + getSettings().main.mediaServerType == MediaServerType.EMBY + ? 'item' + : 'details'; const { serverId, externalHostname } = getSettings().jellyfin; - const jellyfinHost = externalHostname && externalHostname.length > 0 ? externalHostname diff --git a/server/lib/scanners/jellyfin/index.ts b/server/lib/scanners/jellyfin/index.ts index 4ccf5485..f48de70e 100644 --- a/server/lib/scanners/jellyfin/index.ts +++ b/server/lib/scanners/jellyfin/index.ts @@ -567,7 +567,10 @@ class JellyfinScanner { public async run(): Promise { const settings = getSettings(); - if (settings.main.mediaServerType != MediaServerType.JELLYFIN) { + if ( + settings.main.mediaServerType != MediaServerType.JELLYFIN && + settings.main.mediaServerType != MediaServerType.EMBY + ) { return; } diff --git a/server/lib/settings/migrations/0002_emby_media_server_type.ts b/server/lib/settings/migrations/0002_emby_media_server_type.ts new file mode 100644 index 00000000..2bfd2cda --- /dev/null +++ b/server/lib/settings/migrations/0002_emby_media_server_type.ts @@ -0,0 +1,17 @@ +import { MediaServerType } from '@server/constants/server'; +import type { AllSettings } from '@server/lib/settings'; + +const migrateHostname = (settings: any): AllSettings => { + const oldMediaServerType = settings.main.mediaServerType; + console.log('Migrating media server type', oldMediaServerType); + if ( + oldMediaServerType === MediaServerType.JELLYFIN && + process.env.JELLYFIN_TYPE === 'emby' + ) { + settings.main.mediaServerType = MediaServerType.EMBY; + } + + return settings; +}; + +export default migrateHostname; diff --git a/server/routes/auth.ts b/server/routes/auth.ts index 6f01135d..cd931c25 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -1,7 +1,7 @@ import JellyfinAPI from '@server/api/jellyfin'; import PlexTvAPI from '@server/api/plextv'; import { ApiErrorCode } from '@server/constants/error'; -import { MediaServerType } from '@server/constants/server'; +import { MediaServerType, ServerType } from '@server/constants/server'; import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import { User } from '@server/entity/User'; @@ -227,15 +227,20 @@ authRoutes.post('/jellyfin', async (req, res, next) => { urlBase?: string; useSsl?: boolean; email?: string; + serverType?: number; }; - //Make sure jellyfin login is enabled, but only if jellyfin is not already configured + //Make sure jellyfin login is enabled, but only if jellyfin && Emby is not already configured if ( settings.main.mediaServerType !== MediaServerType.JELLYFIN && - settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED + settings.main.mediaServerType !== MediaServerType.EMBY && + settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED && + settings.jellyfin.ip !== '' ) { return res.status(500).json({ error: 'Jellyfin login is disabled' }); - } else if (!body.username) { + } + + if (!body.username) { return res.status(500).json({ error: 'You must provide an username' }); } else if (settings.jellyfin.ip !== '' && body.hostname) { return res @@ -273,7 +278,8 @@ authRoutes.post('/jellyfin', async (req, res, next) => { } // First we need to attempt to log the user in to jellyfin - const jellyfinserver = new JellyfinAPI(hostname, undefined, deviceId); + const jellyfinserver = new JellyfinAPI(hostname ?? '', undefined, deviceId); + const jellyfinHost = externalHostname && externalHostname.length > 0 ? externalHostname @@ -317,22 +323,47 @@ authRoutes.post('/jellyfin', async (req, res, next) => { ); // User doesn't exist, and there are no users in the database, we'll create the user - // with admin permission - settings.main.mediaServerType = MediaServerType.JELLYFIN; - user = new User({ - email: body.email || account.User.Name, - jellyfinUsername: account.User.Name, - jellyfinUserId: account.User.Id, - jellyfinDeviceId: deviceId, - permissions: Permission.ADMIN, - avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` - : gravatarUrl(body.email || account.User.Name, { - default: 'mm', - size: 200, - }), - userType: UserType.JELLYFIN, - }); + // with admin permissions + switch (body.serverType) { + case MediaServerType.EMBY: + settings.main.mediaServerType = MediaServerType.EMBY; + user = new User({ + email: body.email || account.User.Name, + jellyfinUsername: account.User.Name, + jellyfinUserId: account.User.Id, + jellyfinDeviceId: deviceId, + jellyfinAuthToken: account.AccessToken, + permissions: Permission.ADMIN, + avatar: account.User.PrimaryImageTag + ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` + : gravatarUrl(body.email || account.User.Name, { + default: 'mm', + size: 200, + }), + userType: UserType.EMBY, + }); + break; + case MediaServerType.JELLYFIN: + settings.main.mediaServerType = MediaServerType.JELLYFIN; + user = new User({ + email: body.email || account.User.Name, + jellyfinUsername: account.User.Name, + jellyfinUserId: account.User.Id, + jellyfinDeviceId: deviceId, + jellyfinAuthToken: account.AccessToken, + permissions: Permission.ADMIN, + avatar: account.User.PrimaryImageTag + ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` + : gravatarUrl(body.email || account.User.Name, { + default: 'mm', + size: 200, + }), + userType: UserType.JELLYFIN, + }); + break; + default: + throw new Error('select_server_type'); + } // Create an API key on Jellyfin from this admin user const jellyfinClient = new JellyfinAPI( @@ -361,12 +392,12 @@ authRoutes.post('/jellyfin', async (req, res, next) => { logger.info( `Found matching ${ settings.main.mediaServerType === MediaServerType.JELLYFIN - ? 'Jellyfin' - : 'Emby' + ? ServerType.JELLYFIN + : ServerType.EMBY } user; updating user with ${ settings.main.mediaServerType === MediaServerType.JELLYFIN - ? 'Jellyfin' - : 'Emby' + ? ServerType.JELLYFIN + : ServerType.EMBY }`, { label: 'API', @@ -389,12 +420,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { user.username = ''; } - // TODO: If JELLYFIN_TYPE is set to 'emby' then set mediaServerType to EMBY - // if (process.env.JELLYFIN_TYPE === 'emby') { - // settings.main.mediaServerType = MediaServerType.EMBY; - // settings.save(); - // } - await userRepository.save(user); } else if (!settings.main.newPlexLogin) { logger.warn( @@ -432,7 +457,10 @@ authRoutes.post('/jellyfin', async (req, res, next) => { default: 'mm', size: 200, }), - userType: UserType.JELLYFIN, + userType: + settings.main.mediaServerType === MediaServerType.JELLYFIN + ? UserType.JELLYFIN + : UserType.EMBY, }); //initialize Jellyfin/Emby users with local login const passedExplicitPassword = body.password && body.password.length > 0; diff --git a/src/assets/services/emby-icon-only.svg b/src/assets/services/emby-icon-only.svg new file mode 100644 index 00000000..e2f2cf2e --- /dev/null +++ b/src/assets/services/emby-icon-only.svg @@ -0,0 +1,47 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/assets/services/emby.svg b/src/assets/services/emby.svg index eddc540c..2aac8662 100644 --- a/src/assets/services/emby.svg +++ b/src/assets/services/emby.svg @@ -1,46 +1,131 @@ - - - - - - - image/svg+xml - - - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/src/components/ExternalLinkBlock/index.tsx b/src/components/ExternalLinkBlock/index.tsx index 9782d186..9199da7d 100644 --- a/src/components/ExternalLinkBlock/index.tsx +++ b/src/components/ExternalLinkBlock/index.tsx @@ -11,7 +11,6 @@ import useLocale from '@app/hooks/useLocale'; import useSettings from '@app/hooks/useSettings'; import { MediaType } from '@server/constants/media'; import { MediaServerType } from '@server/constants/server'; -import getConfig from 'next/config'; interface ExternalLinkBlockProps { mediaType: 'movie' | 'tv'; @@ -31,7 +30,6 @@ const ExternalLinkBlock = ({ mediaUrl, }: ExternalLinkBlockProps) => { const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); const { locale } = useLocale(); return ( @@ -45,7 +43,8 @@ const ExternalLinkBlock = ({ > {settings.currentSettings.mediaServerType === MediaServerType.PLEX ? ( - ) : publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? ( + ) : settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? ( ) : ( diff --git a/src/components/IssueDetails/index.tsx b/src/components/IssueDetails/index.tsx index a5ec6391..ca0337e5 100644 --- a/src/components/IssueDetails/index.tsx +++ b/src/components/IssueDetails/index.tsx @@ -28,7 +28,6 @@ import type Issue from '@server/entity/Issue'; import type { MovieDetails } from '@server/models/Movie'; import type { TvDetails } from '@server/models/Tv'; import { Field, Form, Formik } from 'formik'; -import getConfig from 'next/config'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/router'; @@ -108,7 +107,6 @@ const IssueDetails = () => { (opt) => opt.issueType === issueData?.issueType ); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); if (!data && !error) { return ; @@ -390,7 +388,8 @@ const IssueDetails = () => { > - {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + {settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? intl.formatMessage(messages.playonplex, { mediaServerName: 'Emby', }) @@ -437,7 +436,8 @@ const IssueDetails = () => { > - {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + {settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? intl.formatMessage(messages.play4konplex, { mediaServerName: 'Emby', }) @@ -662,7 +662,8 @@ const IssueDetails = () => { > - {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + {settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? intl.formatMessage(messages.playonplex, { mediaServerName: 'Emby', }) @@ -708,7 +709,8 @@ const IssueDetails = () => { > - {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + {settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? intl.formatMessage(messages.play4konplex, { mediaServerName: 'Emby', }) diff --git a/src/components/Login/JellyfinLogin.tsx b/src/components/Login/JellyfinLogin.tsx index dd08e53d..ba59d11b 100644 --- a/src/components/Login/JellyfinLogin.tsx +++ b/src/components/Login/JellyfinLogin.tsx @@ -4,9 +4,9 @@ import useSettings from '@app/hooks/useSettings'; import defineMessages from '@app/utils/defineMessages'; import { InformationCircleIcon } from '@heroicons/react/24/solid'; import { ApiErrorCode } from '@server/constants/error'; +import { MediaServerType, ServerType } from '@server/constants/server'; import { Field, Form, Formik } from 'formik'; -import getConfig from 'next/config'; -import { useIntl } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import * as Yup from 'yup'; @@ -26,6 +26,7 @@ const messages = defineMessages('components.Login', { validationemailformat: 'Valid email required', validationusernamerequired: 'Username required', validationpasswordrequired: 'Password required', + validationservertyperequired: 'Please select a server type', validationHostnameRequired: 'You must provide a valid hostname or IP address', validationPortRequired: 'You must provide a valid port number', validationUrlTrailingSlash: 'URL must not end in a trailing slash', @@ -40,42 +41,51 @@ const messages = defineMessages('components.Login', { initialsigningin: 'Connecting…', initialsignin: 'Connect', forgotpassword: 'Forgot Password?', + servertype: 'Server Type', + back: 'Go back', }); interface JellyfinLoginProps { revalidate: () => void; initial?: boolean; + serverType?: MediaServerType; + onCancel?: () => void; } const JellyfinLogin: React.FC = ({ revalidate, initial, + serverType, + onCancel, }) => { const toasts = useToasts(); const intl = useIntl(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); + + const mediaServerFormatValues = { + mediaServerName: + serverType === MediaServerType.JELLYFIN + ? ServerType.JELLYFIN + : serverType === MediaServerType.EMBY + ? ServerType.EMBY + : 'Media Server', + }; if (initial) { const LoginSchema = Yup.object().shape({ hostname: Yup.string().required( - intl.formatMessage(messages.validationhostrequired, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin', - }) + intl.formatMessage( + messages.validationhostrequired, + mediaServerFormatValues + ) ), port: Yup.number().required( intl.formatMessage(messages.validationPortRequired) ), - urlBase: Yup.string() - .matches( - /^(\/[^/].*[^/]$)/, - intl.formatMessage(messages.validationUrlBaseLeadingSlash) - ) - .matches( - /^(.*[^/])$/, - intl.formatMessage(messages.validationUrlBaseTrailingSlash) - ), + urlBase: Yup.string().matches( + /^(.*[^/])$/, + intl.formatMessage(messages.validationUrlBaseTrailingSlash) + ), email: Yup.string() .email(intl.formatMessage(messages.validationemailformat)) .required(intl.formatMessage(messages.validationemailrequired)), @@ -85,11 +95,6 @@ const JellyfinLogin: React.FC = ({ password: Yup.string(), }); - const mediaServerFormatValues = { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin', - }; - return ( = ({ validationSchema={LoginSchema} onSubmit={async (values) => { try { + // Check if serverType is either 'Jellyfin' or 'Emby' + // if (serverType !== 'Jellyfin' && serverType !== 'Emby') { + // throw new Error('Invalid serverType'); // You can customize the error message + // } + const res = await fetch('/api/v1/auth/jellyfin', { method: 'POST', headers: { @@ -117,6 +127,7 @@ const JellyfinLogin: React.FC = ({ useSsl: values.useSsl, urlBase: values.urlBase, email: values.email, + serverType: serverType, }), }); if (!res.ok) throw new Error(res.statusText, { cause: res }); @@ -312,7 +323,7 @@ const JellyfinLogin: React.FC = ({
-
+
+ {onCancel && ( + + + + )}
@@ -429,7 +447,8 @@ const JellyfinLogin: React.FC = ({ jellyfinForgotPasswordUrl ? `${jellyfinForgotPasswordUrl}` : `${baseUrl}/web/index.html#!/${ - process.env.JELLYFIN_TYPE === 'emby' + settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? 'startup/' : '' }forgotpassword.html` diff --git a/src/components/Login/index.tsx b/src/components/Login/index.tsx index eca7b6ac..7b95b9fc 100644 --- a/src/components/Login/index.tsx +++ b/src/components/Login/index.tsx @@ -10,7 +10,6 @@ import defineMessages from '@app/utils/defineMessages'; import { Transition } from '@headlessui/react'; import { XCircleIcon } from '@heroicons/react/24/solid'; import { MediaServerType } from '@server/constants/server'; -import getConfig from 'next/config'; import { useRouter } from 'next/dist/client/router'; import Image from 'next/image'; import { useEffect, useState } from 'react'; @@ -34,7 +33,6 @@ const Login = () => { const { user, revalidate } = useUser(); const router = useRouter(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); // Effect that is triggered when the `authToken` comes back from the Plex OAuth // We take the token and attempt to sign in. If we get a success message, we will @@ -88,6 +86,15 @@ const Login = () => { revalidateOnFocus: false, }); + const mediaServerFormatValues = { + mediaServerName: + settings.currentSettings.mediaServerType === MediaServerType.JELLYFIN + ? 'Jellyfin' + : settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : undefined, + }; + return (
@@ -154,12 +161,10 @@ const Login = () => { {settings.currentSettings.mediaServerType == MediaServerType.PLEX ? intl.formatMessage(messages.signinwithplex) - : intl.formatMessage(messages.signinwithjellyfin, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - })} + : intl.formatMessage( + messages.signinwithjellyfin, + mediaServerFormatValues + )}
diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index 35c8bc1c..b669ebb4 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -26,7 +26,6 @@ import type { MediaWatchDataResponse } from '@server/interfaces/api/mediaInterfa import type { RadarrSettings, SonarrSettings } from '@server/lib/settings'; import type { MovieDetails } from '@server/models/Movie'; import type { TvDetails } from '@server/models/Tv'; -import getConfig from 'next/config'; import Image from 'next/image'; import Link from 'next/link'; import { useIntl } from 'react-intl'; @@ -95,7 +94,6 @@ const ManageSlideOver = ({ const { user: currentUser, hasPermission } = useUser(); const intl = useIntl(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); const { data: watchData } = useSWR( settings.currentSettings.mediaServerType === MediaServerType.PLEX && data.mediaInfo && @@ -661,7 +659,8 @@ const ManageSlideOver = ({ mediaType === 'movie' ? messages.movie : messages.tvshow ), mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? 'Emby' : settings.currentSettings.mediaServerType === MediaServerType.PLEX diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index b18d506c..e4bc991e 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -53,7 +53,6 @@ import type { MovieDetails as MovieDetailsType } from '@server/models/Movie'; import { countries } from 'country-flag-icons'; import 'country-flag-icons/3x2/flags.css'; import { uniqBy } from 'lodash'; -import getConfig from 'next/config'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; @@ -126,7 +125,6 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { const [toggleWatchlist, setToggleWatchlist] = useState( !movie?.onUserWatchlist ); - const { publicRuntimeConfig } = getConfig(); const { addToast } = useToasts(); const { @@ -279,7 +277,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { ?.flatrate ?? []; function getAvalaibleMediaServerName() { - if (publicRuntimeConfig.JELLYFIN_TYPE === 'emby') { + if (settings.currentSettings.mediaServerType === MediaServerType.EMBY) { return intl.formatMessage(messages.play, { mediaServerName: 'Emby' }); } @@ -291,8 +289,8 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { } function getAvalaible4kMediaServerName() { - if (publicRuntimeConfig.JELLYFIN_TYPE === 'emby') { - return intl.formatMessage(messages.play4k, { mediaServerName: 'Emby' }); + if (settings.currentSettings.mediaServerType === MediaServerType.EMBY) { + return intl.formatMessage(messages.play, { mediaServerName: 'Emby' }); } if (settings.currentSettings.mediaServerType === MediaServerType.PLEX) { diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index a627f6d3..316dc48e 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -3,13 +3,14 @@ import Button from '@app/components/Common/Button'; import LoadingSpinner from '@app/components/Common/LoadingSpinner'; import SensitiveInput from '@app/components/Common/SensitiveInput'; import LibraryItem from '@app/components/Settings/LibraryItem'; +import useSettings from '@app/hooks/useSettings'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline'; import { ApiErrorCode } from '@server/constants/error'; +import { MediaServerType } from '@server/constants/server'; import type { JellyfinSettings } from '@server/lib/settings'; import { Field, Formik } from 'formik'; -import getConfig from 'next/config'; import { useState } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; @@ -61,6 +62,9 @@ const messages = defineMessages('components.Settings', { validationUrlTrailingSlash: 'URL must not end in a trailing slash', validationUrlBaseLeadingSlash: 'URL base must have a leading slash', validationUrlBaseTrailingSlash: 'URL base must not end in a trailing slash', + tip: 'Tip', + scanbackground: + 'Scanning will run in the background. You can continue the setup process in the meantime.', }); interface Library { @@ -78,13 +82,13 @@ interface SyncStatus { } interface SettingsJellyfinProps { - showAdvancedSettings?: boolean; + isSetupSettings?: boolean; onComplete?: () => void; } const SettingsJellyfin: React.FC = ({ onComplete, - showAdvancedSettings, + isSetupSettings, }) => { const [isSyncing, setIsSyncing] = useState(false); const toasts = useToasts(); @@ -102,7 +106,7 @@ const SettingsJellyfin: React.FC = ({ ); const intl = useIntl(); const { addToast } = useToasts(); - const { publicRuntimeConfig } = getConfig(); + const settings = useSettings(); const JellyfinSettingsSchema = Yup.object().shape({ hostname: Yup.string() @@ -284,26 +288,29 @@ const SettingsJellyfin: React.FC = ({ return ; } + const mediaServerFormatValues = { + mediaServerName: + settings.currentSettings.mediaServerType === MediaServerType.JELLYFIN + ? 'Jellyfin' + : settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : undefined, + }; + return ( <>

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinlibraries, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinlibraries, { - mediaServerName: 'Jellyfin', - })} + {intl.formatMessage( + messages.jellyfinlibraries, + mediaServerFormatValues + )}

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinlibrariesDescription, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinlibrariesDescription, { - mediaServerName: 'Jellyfin', - })} + {intl.formatMessage( + messages.jellyfinlibrariesDescription, + mediaServerFormatValues + )}

@@ -340,13 +347,10 @@ const SettingsJellyfin: React.FC = ({

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.manualscanDescriptionJellyfin, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.manualscanDescriptionJellyfin, { - mediaServerName: 'Jellyfin', - })} + {intl.formatMessage( + messages.manualscanDescriptionJellyfin, + mediaServerFormatValues + )}

@@ -446,24 +450,26 @@ const SettingsJellyfin: React.FC = ({
+ {isSetupSettings && ( +
+ + {intl.formatMessage(messages.tip)} + + {intl.formatMessage(messages.scanbackground)} +
+ )}

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Jellyfin', - })} + {intl.formatMessage( + messages.jellyfinSettings, + mediaServerFormatValues + )}

- {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Emby', - }) - : intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Jellyfin', - })} + {intl.formatMessage( + messages.jellyfinSettingsDescription, + mediaServerFormatValues + )}

= ({ if (!res.ok) throw new Error(res.statusText, { cause: res }); addToast( - intl.formatMessage(messages.jellyfinSettingsSuccess, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), + intl.formatMessage( + messages.jellyfinSettingsSuccess, + mediaServerFormatValues + ), { autoDismiss: true, appearance: 'success', @@ -518,12 +522,10 @@ const SettingsJellyfin: React.FC = ({ } if (errorData?.message === ApiErrorCode.InvalidUrl) { addToast( - intl.formatMessage(messages.invalidurlerror, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), + intl.formatMessage( + messages.invalidurlerror, + mediaServerFormatValues + ), { autoDismiss: true, appearance: 'error', @@ -531,12 +533,10 @@ const SettingsJellyfin: React.FC = ({ ); } else { addToast( - intl.formatMessage(messages.jellyfinSettingsFailure, { - mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' - ? 'Emby' - : 'Jellyfin', - }), + intl.formatMessage( + messages.jellyfinSettingsFailure, + mediaServerFormatValues + ), { autoDismiss: true, appearance: 'error', @@ -559,7 +559,7 @@ const SettingsJellyfin: React.FC = ({ }) => { return (
- {showAdvancedSettings && ( + {!isSetupSettings && ( <>
- {showAdvancedSettings && ( + {!isSetupSettings && ( <>
-
+
- -
- { - setMediaServerType(MediaServerType.PLEX); - setAuthToken(authToken); - }} - /> -
-
-
- - -
- -
-
-
- + {serverType === MediaServerType.JELLYFIN ? ( + + ) : serverType === MediaServerType.EMBY ? ( + + ) : ( + )} - +
+ {serverType === MediaServerType.PLEX && ( + <> +
+ { + setMediaServerType(MediaServerType.PLEX); + setAuthToken(authToken); + }} + /> +
+
+ +
+ + )} + {serverType === MediaServerType.JELLYFIN && ( + + )} + {serverType === MediaServerType.EMBY && ( + + )}
); }; diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index 6ec227c9..93611467 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -1,5 +1,7 @@ +import EmbyLogo from '@app/assets/services/emby.svg'; +import JellyfinLogo from '@app/assets/services/jellyfin.svg'; +import PlexLogo from '@app/assets/services/plex.svg'; import AppDataWarning from '@app/components/AppDataWarning'; -import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; import ImageFader from '@app/components/Common/ImageFader'; import PageTitle from '@app/components/Common/PageTitle'; @@ -9,26 +11,30 @@ import SettingsPlex from '@app/components/Settings/SettingsPlex'; import SettingsServices from '@app/components/Settings/SettingsServices'; import SetupSteps from '@app/components/Setup/SetupSteps'; import useLocale from '@app/hooks/useLocale'; +import useSettings from '@app/hooks/useSettings'; import defineMessages from '@app/utils/defineMessages'; import { MediaServerType } from '@server/constants/server'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import useSWR, { mutate } from 'swr'; import SetupLogin from './SetupLogin'; const messages = defineMessages('components.Setup', { + welcome: 'Welcome to Jellyseerr', + subtitle: 'Get started by choosing your media server', + configjellyfin: 'Configure Jellyfin', + configplex: 'Configure Plex', + configemby: 'Configure Emby', setup: 'Setup', finish: 'Finish Setup', finishing: 'Finishing…', continue: 'Continue', + servertype: 'Choose Server Type', signin: 'Sign In', configuremediaserver: 'Configure Media Server', configureservices: 'Configure Services', - tip: 'Tip', - scanbackground: - 'Scanning will run in the background. You can continue the setup process in the meantime.', }); const Setup = () => { @@ -42,6 +48,7 @@ const Setup = () => { ); const router = useRouter(); const { locale } = useLocale(); + const settings = useSettings(); const finishSetup = async () => { setIsUpdating(true); @@ -76,6 +83,23 @@ const Setup = () => { revalidateOnFocus: false, }); + useEffect(() => { + if (settings.currentSettings.initialized) { + router.push('/'); + } + if ( + settings.currentSettings.mediaServerType !== + MediaServerType.NOT_CONFIGURED + ) { + setCurrentStep(3); + setMediaServerType(settings.currentSettings.mediaServerType); + } + }, [ + settings.currentSettings.mediaServerType, + settings.currentSettings.initialized, + router, + ]); + return (
@@ -101,58 +125,120 @@ const Setup = () => { > 1} /> 2} /> 3} + /> +
{currentStep === 1 && ( - { - setMediaServerType(mServerType); - setCurrentStep(2); - }} - /> +
+
+ {intl.formatMessage(messages.welcome)} +
+
+ {intl.formatMessage(messages.subtitle)} +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
)} {currentStep === 2 && ( -
+ { + setMediaServerType(MediaServerType.NOT_CONFIGURED); + setCurrentStep(1); + }} + onComplete={() => setCurrentStep(3)} + /> + )} + {currentStep === 3 && ( +
{mediaServerType === MediaServerType.PLEX ? ( setMediaServerSettingsComplete(true)} /> ) : ( setMediaServerSettingsComplete(true)} /> )} -
- - {intl.formatMessage(messages.tip)} - - {intl.formatMessage(messages.scanbackground)} -
@@ -161,7 +247,7 @@ const Setup = () => {
)} - {currentStep === 3 && ( + {currentStep === 4 && (
diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index 0061a903..1d280d28 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -9,7 +9,6 @@ import defineMessages from '@app/utils/defineMessages'; import { MediaStatus } from '@server/constants/media'; import { MediaServerType } from '@server/constants/server'; import type { DownloadingItem } from '@server/lib/downloadtracker'; -import getConfig from 'next/config'; import { useIntl } from 'react-intl'; const messages = defineMessages('components.StatusBadge', { @@ -48,7 +47,6 @@ const StatusBadge = ({ const intl = useIntl(); const { hasPermission } = useUser(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); let mediaLink: string | undefined; let mediaLinkDescription: string | undefined; @@ -86,7 +84,7 @@ const StatusBadge = ({ mediaLink = plexUrl; mediaLinkDescription = intl.formatMessage(messages.playonplex, { mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + settings.currentSettings.mediaServerType === MediaServerType.EMBY ? 'Emby' : settings.currentSettings.mediaServerType === MediaServerType.PLEX ? 'Plex' diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index af253f58..634c72d0 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -59,7 +59,6 @@ import type { Crew } from '@server/models/common'; import type { TvDetails as TvDetailsType } from '@server/models/Tv'; import { countries } from 'country-flag-icons'; import 'country-flag-icons/3x2/flags.css'; -import getConfig from 'next/config'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; @@ -126,7 +125,6 @@ const TvDetails = ({ tv }: TvDetailsProps) => { const [toggleWatchlist, setToggleWatchlist] = useState( !tv?.onUserWatchlist ); - const { publicRuntimeConfig } = getConfig(); const { addToast } = useToasts(); const { @@ -300,7 +298,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => { ?.flatrate ?? []; function getAvalaibleMediaServerName() { - if (publicRuntimeConfig.JELLYFIN_TYPE === 'emby') { + if (settings.currentSettings.mediaServerType === MediaServerType.EMBY) { return intl.formatMessage(messages.play, { mediaServerName: 'Emby' }); } @@ -312,15 +310,15 @@ const TvDetails = ({ tv }: TvDetailsProps) => { } function getAvalaible4kMediaServerName() { - if (publicRuntimeConfig.JELLYFIN_TYPE === 'emby') { - return intl.formatMessage(messages.play4k, { mediaServerName: 'Emby' }); + if (settings.currentSettings.mediaServerType === MediaServerType.EMBY) { + return intl.formatMessage(messages.play, { mediaServerName: 'Emby' }); } if (settings.currentSettings.mediaServerType === MediaServerType.PLEX) { return intl.formatMessage(messages.play4k, { mediaServerName: 'Plex' }); } - return intl.formatMessage(messages.play4k, { mediaServerName: 'Jellyfin' }); + return intl.formatMessage(messages.play, { mediaServerName: 'Jellyfin' }); } const onClickWatchlistBtn = async (): Promise => { diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx index 64ca1861..36dbe0aa 100644 --- a/src/components/UserList/JellyfinImportModal.tsx +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -3,8 +3,8 @@ import Modal from '@app/components/Common/Modal'; import useSettings from '@app/hooks/useSettings'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; +import { MediaServerType } from '@server/constants/server'; import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces'; -import getConfig from 'next/config'; import Image from 'next/image'; import { useState } from 'react'; import { useIntl } from 'react-intl'; @@ -36,7 +36,6 @@ const JellyfinImportModal: React.FC = ({ }) => { const intl = useIntl(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); const { addToast } = useToasts(); const [isImporting, setImporting] = useState(false); const [selectedUsers, setSelectedUsers] = useState([]); @@ -81,7 +80,9 @@ const JellyfinImportModal: React.FC = ({ userCount: createdUsers.length, strong: (msg: React.ReactNode) => {msg}, mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin', + settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : 'Jellyfin', }), { autoDismiss: true, @@ -96,7 +97,9 @@ const JellyfinImportModal: React.FC = ({ addToast( intl.formatMessage(messages.importfromJellyfinerror, { mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin', + settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : 'Jellyfin', }), { autoDismiss: true, @@ -134,7 +137,9 @@ const JellyfinImportModal: React.FC = ({ loading={!data && !error} title={intl.formatMessage(messages.importfromJellyfin, { mediaServerName: - publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin', + settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : 'Jellyfin', })} onOk={() => { importUsers(); @@ -151,7 +156,8 @@ const JellyfinImportModal: React.FC = ({ ( @@ -277,7 +283,9 @@ const JellyfinImportModal: React.FC = ({ diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 66df469b..7a91a103 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -28,7 +28,6 @@ import { MediaServerType } from '@server/constants/server'; import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces'; import { hasPermission } from '@server/lib/permissions'; import { Field, Form, Formik } from 'formik'; -import getConfig from 'next/config'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/router'; @@ -90,7 +89,6 @@ const UserList = () => { const intl = useIntl(); const router = useRouter(); const settings = useSettings(); - const { publicRuntimeConfig } = getConfig(); const { addToast } = useToasts(); const { user: currentUser, hasPermission: currentHasPermission } = useUser(); const [currentSort, setCurrentSort] = useState('displayname'); @@ -535,7 +533,8 @@ const UserList = () => { > - {publicRuntimeConfig.JELLYFIN_TYPE == 'emby' + {settings.currentSettings.mediaServerType === + MediaServerType.EMBY ? intl.formatMessage(messages.importfrommediaserver, { mediaServerName: 'Emby', }) @@ -690,7 +689,7 @@ const UserList = () => { {intl.formatMessage(messages.localuser)} - ) : publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? ( + ) : user.userType === UserType.EMBY ? ( {intl.formatMessage(messages.mediaServerUser, { mediaServerName: 'Emby', diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 3bcf1a04..15d96071 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -16,7 +16,6 @@ import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline'; import type { UserSettingsGeneralResponse } from '@server/interfaces/api/userSettingsInterfaces'; import { Field, Form, Formik } from 'formik'; -import getConfig from 'next/config'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; @@ -69,7 +68,6 @@ const messages = defineMessages( const UserGeneralSettings = () => { const intl = useIntl(); - const { publicRuntimeConfig } = getConfig(); const { addToast } = useToasts(); const { locale, setLocale } = useLocale(); const [movieQuotaEnabled, setMovieQuotaEnabled] = useState(false); @@ -229,7 +227,7 @@ const UserGeneralSettings = () => { {intl.formatMessage(messages.localuser)} - ) : publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? ( + ) : user?.userType === UserType.EMBY ? ( {intl.formatMessage(messages.mediaServerUser, { mediaServerName: 'Emby', diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 0c07003f..cf66b67e 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -221,6 +221,7 @@ "components.Layout.VersionStatus.streamdevelop": "Jellyseerr Develop", "components.Layout.VersionStatus.streamstable": "Jellyseerr Stable", "components.Login.adminerror": "You must use an admin account to sign in.", + "components.Login.back": "Go back", "components.Login.credentialerror": "The username or password is incorrect.", "components.Login.description": "Since this is your first time logging into {applicationName}, you are required to add a valid email address.", "components.Login.email": "Email Address", @@ -236,6 +237,7 @@ "components.Login.port": "Port", "components.Login.save": "Add", "components.Login.saving": "Adding…", + "components.Login.servertype": "Server Type", "components.Login.signin": "Sign In", "components.Login.signingin": "Signing In…", "components.Login.signinheader": "Sign in to continue", @@ -257,6 +259,7 @@ "components.Login.validationhostformat": "Valid URL required", "components.Login.validationhostrequired": "{mediaServerName} URL required", "components.Login.validationpasswordrequired": "You must provide a password", + "components.Login.validationservertyperequired": "Please select a server type", "components.Login.validationusernamerequired": "Username required", "components.ManageSlideOver.alltime": "All Time", "components.ManageSlideOver.downloadstatus": "Downloads", @@ -1047,17 +1050,24 @@ "components.Settings.webAppUrlTip": "Optionally direct users to the web app on your server instead of the \"hosted\" web app", "components.Settings.webhook": "Webhook", "components.Settings.webpush": "Web Push", + "components.Setup.back": "Go back", + "components.Setup.configemby": "Configure Emby", + "components.Setup.configjellyfin": "Configure Jellyfin", + "components.Setup.configplex": "Configure Plex", "components.Setup.configuremediaserver": "Configure Media Server", "components.Setup.configureservices": "Configure Services", "components.Setup.continue": "Continue", "components.Setup.finish": "Finish Setup", "components.Setup.finishing": "Finishing…", "components.Setup.scanbackground": "Scanning will run in the background. You can continue the setup process in the meantime.", + "components.Setup.servertype": "Choose Server Type", "components.Setup.setup": "Setup", "components.Setup.signin": "Sign In", "components.Setup.signinMessage": "Get started by signing in", - "components.Setup.signinWithJellyfin": "Use your {mediaServerName} account", - "components.Setup.signinWithPlex": "Use your Plex account", + "components.Setup.signinWithEmby": "Enter your Emby details", + "components.Setup.signinWithJellyfin": "Enter your Jellyfin details", + "components.Setup.signinWithPlex": "Enter your Plex details", + "components.Setup.subtitle": "Get started by choosing your media server", "components.Setup.tip": "Tip", "components.Setup.welcome": "Welcome to Jellyseerr", "components.StatusBadge.managemedia": "Manage {mediaType}", diff --git a/src/pages/settings/jellyfin.tsx b/src/pages/settings/jellyfin.tsx index 8f1377da..2490c863 100644 --- a/src/pages/settings/jellyfin.tsx +++ b/src/pages/settings/jellyfin.tsx @@ -8,7 +8,7 @@ const JellyfinSettingsPage: NextPage = () => { useRouteGuard(Permission.MANAGE_SETTINGS); return ( - + ); }; diff --git a/src/styles/globals.css b/src/styles/globals.css index 66c023d9..1e99d53d 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -83,6 +83,16 @@ background: #f19a30; } + .server-type-button { + @apply rounded-md border border-gray-500 bg-gray-700 px-4 py-2 text-white transition duration-150 ease-in-out hover:bg-gray-500; + } + .jellyfin-server svg { + @apply h-6 w-6; + } + .emby-server svg { + @apply h-7 w-7; + } + ul.cards-vertical, ul.cards-horizontal { @apply grid gap-4; From 32343f23a38b2ec1d23c612bf8b2f2101f8b2531 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:37:41 +0500 Subject: [PATCH 31/53] chore(migrations): proper rename & clean up of media server type migration (#939) --- ..._emby_media_server_type.ts => 0003_emby_media_server_type.ts} | 1 - 1 file changed, 1 deletion(-) rename server/lib/settings/migrations/{0002_emby_media_server_type.ts => 0003_emby_media_server_type.ts} (87%) diff --git a/server/lib/settings/migrations/0002_emby_media_server_type.ts b/server/lib/settings/migrations/0003_emby_media_server_type.ts similarity index 87% rename from server/lib/settings/migrations/0002_emby_media_server_type.ts rename to server/lib/settings/migrations/0003_emby_media_server_type.ts index 2bfd2cda..1671e7a9 100644 --- a/server/lib/settings/migrations/0002_emby_media_server_type.ts +++ b/server/lib/settings/migrations/0003_emby_media_server_type.ts @@ -3,7 +3,6 @@ import type { AllSettings } from '@server/lib/settings'; const migrateHostname = (settings: any): AllSettings => { const oldMediaServerType = settings.main.mediaServerType; - console.log('Migrating media server type', oldMediaServerType); if ( oldMediaServerType === MediaServerType.JELLYFIN && process.env.JELLYFIN_TYPE === 'emby' From 7423bbbffc5bee2e52e3348254f035dc8527d973 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 22 Aug 2024 14:46:06 +0200 Subject: [PATCH 32/53] fix(setup): page display when homepage is loading (#940) * fix: remove an unwanted display of the setup page while the homepage is loading * fix: edit incorrect return type of setup page --- src/components/Setup/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index 93611467..684b01ad 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -100,6 +100,8 @@ const Setup = () => { router, ]); + if (settings.currentSettings.initialized) return <>; + return (
From a02a0dd1765a6f9333fa105578c858ae6eb16bfc Mon Sep 17 00:00:00 2001 From: Gauthier Date: Mon, 26 Aug 2024 14:27:06 +0200 Subject: [PATCH 33/53] docs: fix broken anchors (#946) --- docs/getting-started/nixpkg.mdx | 8 ++++---- docs/using-jellyseerr/users/editing-users.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/getting-started/nixpkg.mdx b/docs/getting-started/nixpkg.mdx index b7d955e9..9afb2757 100644 --- a/docs/getting-started/nixpkg.mdx +++ b/docs/getting-started/nixpkg.mdx @@ -22,7 +22,7 @@ export const VersionMismatchWarning = () => { <> {!isUpToDate ? ( - The upstream Jellyseerr Nix Package (v{nixpkgVersion}) is not up-to-date. If you want to use Jellyseerr v{jellyseerrVersion}, you will need to override the package derivation. + The upstream Jellyseerr Nix Package (v{nixpkgVersion}) is not up-to-date. If you want to use Jellyseerr v{jellyseerrVersion}, you will need to override the package derivation. ) : ( @@ -95,12 +95,12 @@ export const VersionMatch = () => { }; offlineCache = pkgs.fetchYarnDeps { - sha256 = pkgs.lib.fakeSha256; + sha256 = pkgs.lib.fakeSha256; }; - }); + }); }; }`; - + const module = `{ config, pkgs, lib, ... }: with lib; diff --git a/docs/using-jellyseerr/users/editing-users.md b/docs/using-jellyseerr/users/editing-users.md index 13ddb760..fb2b80ab 100644 --- a/docs/using-jellyseerr/users/editing-users.md +++ b/docs/using-jellyseerr/users/editing-users.md @@ -35,7 +35,7 @@ Users can override the [global display language](/using-jellyseerr/settings/gene ### Discover Region & Discover Language -Users can override the [global filter settings](/using-jellyseerr/settings/general#discover-region-and-discover-language) to suit their own preferences. +Users can override the [global filter settings](/using-jellyseerr/settings/general#discover-region--discover-language) to suit their own preferences. ### Movie Request Limit & Series Request Limit From e57d2654d1c634a91649722d3a2bf4d73c4a02ca Mon Sep 17 00:00:00 2001 From: Joaquin Olivero <66050823+JoaquinOlivero@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:14:46 -0300 Subject: [PATCH 34/53] fix: set correct user type when importing from emby (#949) fix #948 Co-authored-by: JoaquinOlivero --- server/routes/user/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index da9b649c..f8a0d41a 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -2,6 +2,7 @@ import JellyfinAPI from '@server/api/jellyfin'; import PlexTvAPI from '@server/api/plextv'; import TautulliAPI from '@server/api/tautulli'; import { MediaType } from '@server/constants/media'; +import { MediaServerType } from '@server/constants/server'; import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; @@ -550,7 +551,10 @@ router.post( default: 'mm', size: 200, }), - userType: UserType.JELLYFIN, + userType: + settings.main.mediaServerType === MediaServerType.JELLYFIN + ? UserType.JELLYFIN + : UserType.EMBY, }); await userRepository.save(newUser); From 89e0a831ec85a6905f539f59b7523bb1feb90bcf Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 27 Aug 2024 12:54:56 +0200 Subject: [PATCH 35/53] fix: add an error message to say when an email is already taken (#947) When the email is modified in the user settings and it is already taken by someone else, a generic message saying that something wrong happened, without saying that it is because the email is already taken by another user. This PR adds this error message for the email. --- server/constants/error.ts | 1 + server/routes/user/usersettings.ts | 19 +++++++++++++- .../UserGeneralSettings/index.tsx | 26 +++++++++++++++---- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/server/constants/error.ts b/server/constants/error.ts index ac18c3ec..96fafdc9 100644 --- a/server/constants/error.ts +++ b/server/constants/error.ts @@ -2,6 +2,7 @@ export enum ApiErrorCode { InvalidUrl = 'INVALID_URL', InvalidCredentials = 'INVALID_CREDENTIALS', InvalidAuthToken = 'INVALID_AUTH_TOKEN', + InvalidEmail = 'INVALID_EMAIL', NotAdmin = 'NOT_ADMIN', SyncErrorGroupedFolders = 'SYNC_ERROR_GROUPED_FOLDERS', SyncErrorNoLibraries = 'SYNC_ERROR_NO_LIBRARIES', diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts index 9669cb18..11cbd666 100644 --- a/server/routes/user/usersettings.ts +++ b/server/routes/user/usersettings.ts @@ -1,3 +1,4 @@ +import { ApiErrorCode } from '@server/constants/error'; import { getRepository } from '@server/datasource'; import { User } from '@server/entity/User'; import { UserSettings } from '@server/entity/UserSettings'; @@ -9,6 +10,7 @@ import { Permission } from '@server/lib/permissions'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { isAuthenticated } from '@server/middleware/auth'; +import { ApiError } from '@server/types/error'; import { Router } from 'express'; import { canMakePermissionsChange } from '.'; @@ -98,10 +100,18 @@ userSettingsRoutes.post< } user.username = req.body.username; + const oldEmail = user.email; if (user.jellyfinUsername) { user.email = req.body.email || user.jellyfinUsername || user.email; } + const existingUser = await userRepository.findOne({ + where: { email: user.email }, + }); + if (oldEmail !== user.email && existingUser) { + throw new ApiError(400, ApiErrorCode.InvalidEmail); + } + // Update quota values only if the user has the correct permissions if ( !user.hasPermission(Permission.MANAGE_USERS) && @@ -145,7 +155,14 @@ userSettingsRoutes.post< email: savedUser.email, }); } catch (e) { - next({ status: 500, message: e.message }); + if (e.errorCode) { + return next({ + status: e.statusCode, + message: e.errorCode, + }); + } else { + return next({ status: 500, message: e.message }); + } } }); diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 15d96071..502a9d84 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -14,6 +14,7 @@ import globalMessages from '@app/i18n/globalMessages'; import ErrorPage from '@app/pages/_error'; import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline'; +import { ApiErrorCode } from '@server/constants/error'; import type { UserSettingsGeneralResponse } from '@server/interfaces/api/userSettingsInterfaces'; import { Field, Form, Formik } from 'formik'; import { useRouter } from 'next/router'; @@ -42,6 +43,7 @@ const messages = defineMessages( user: 'User', toastSettingsSuccess: 'Settings saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', + toastSettingsFailureEmail: 'This email is already taken!', region: 'Discover Region', regionTip: 'Filter content by regional availability', originallanguage: 'Discover Language', @@ -178,7 +180,7 @@ const UserGeneralSettings = () => { watchlistSyncTv: values.watchlistSyncTv, }), }); - if (!res.ok) throw new Error(); + if (!res.ok) throw new Error(res.statusText, { cause: res }); if (currentUser?.id === user?.id && setLocale) { setLocale( @@ -193,10 +195,24 @@ const UserGeneralSettings = () => { appearance: 'success', }); } catch (e) { - addToast(intl.formatMessage(messages.toastSettingsFailure), { - autoDismiss: true, - appearance: 'error', - }); + let errorData; + try { + errorData = await e.cause?.text(); + errorData = JSON.parse(errorData); + } catch { + /* empty */ + } + if (errorData?.message === ApiErrorCode.InvalidEmail) { + addToast(intl.formatMessage(messages.toastSettingsFailureEmail), { + autoDismiss: true, + appearance: 'error', + }); + } else { + addToast(intl.formatMessage(messages.toastSettingsFailure), { + autoDismiss: true, + appearance: 'error', + }); + } } finally { revalidate(); revalidateUser(); From 54cfeefe74de2c7df97491aa6cb954b94759ac5d Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:58:04 +0500 Subject: [PATCH 36/53] docs(readme): update the translate badge (#951) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f0fa356..f91fe384 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

Discord Docker pulls -Translation status +Translation status GitHub All Contributors From 45ef150e36944d456cc9440574b5ac75f2e4bbc1 Mon Sep 17 00:00:00 2001 From: Aidan Hilt <11202897+AidanHilt@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:05:44 -0400 Subject: [PATCH 37/53] feat: add environment variable for API key (#831) * Added the ability to set the API key with the env var API_KEY * Adding debug statements * Updating * feat: adding env var for API key * feat: update * fix(settings/index.ts): remove a print statement that logs the API key to the console * Update en.json * docs: added documentation about API_KEY environment variable * feat: add a check to ensure API key always uses env var if provided * feat: always check the API_KEY env var at startup * chore: add back the gitkeeps under ./config, accidentally deleted in prev commit * chore: revert change made to docker-compose that was accidentally committed --- docs/using-jellyseerr/settings/general.md | 2 ++ server/lib/settings/index.ts | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/using-jellyseerr/settings/general.md b/docs/using-jellyseerr/settings/general.md index 9cec9b9f..61991ed0 100644 --- a/docs/using-jellyseerr/settings/general.md +++ b/docs/using-jellyseerr/settings/general.md @@ -12,6 +12,8 @@ This is your Jellyseerr API key, which can be used to integrate Jellyseerr with If you need to generate a new API key for any reason, simply click the button to the right of the text box. +If you want to set the API key, rather than letting it be randomly generated, you can use the API_KEY environment variable. Whatever that variable is set to will be your API key. + ## Application Title If you aren't a huge fan of the name "Jellyseerr" and would like to display something different to your users, you can customize the application title! diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 8c55d6c3..074a4fcd 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -611,7 +611,11 @@ class Settings { } private generateApiKey(): string { - return Buffer.from(`${Date.now()}${randomUUID()}`).toString('base64'); + if (process.env.API_KEY) { + return process.env.API_KEY; + } else { + return Buffer.from(`${Date.now()}${randomUUID()}`).toString('base64'); + } } private generateVapidKeys(force = false): void { @@ -648,6 +652,12 @@ class Settings { this.data = merge(this.data, parsedJson); + if (process.env.API_KEY) { + if (this.main.apiKey != process.env.API_KEY) { + this.main.apiKey = process.env.API_KEY; + } + } + this.save(); } return this; From ee7e91c7c948b17b556a625919eb1252a721bb6e Mon Sep 17 00:00:00 2001 From: Jonas F Date: Mon, 16 Sep 2024 19:07:43 +0200 Subject: [PATCH 38/53] fix: change SeriesSearch to MissingEpisodeSearch for season requests (#711) This fix changes the behavior of how Overseerr requests series data from Sonarr. Previously, when adding new seasons to a partially available series, Overseerr would initiate a SeriesSearch, causing Sonarr to search for all monitored seasons of the series, including those already available. This behavior is now corrected by executing a MissingEpisodeSearchCommand for the specific seriesId, which aligns with the intended behavior of only searching for and adding the newly requested seasons that are not already available. Resolves: https://github.com/Fallenbagel/jellyseerr/issues/710 --- server/api/servarr/sonarr.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/api/servarr/sonarr.ts b/server/api/servarr/sonarr.ts index 67c9dd2a..8ae054ed 100644 --- a/server/api/servarr/sonarr.ts +++ b/server/api/servarr/sonarr.ts @@ -303,10 +303,10 @@ class SonarrAPI extends ServarrBase<{ }); try { - await this.runCommand('SeriesSearch', { seriesId }); + await this.runCommand('MissingEpisodeSearch', { seriesId }); } catch (e) { logger.error( - 'Something went wrong while executing Sonarr series search.', + 'Something went wrong while executing Sonarr missing episode search.', { label: 'Sonarr API', errorMessage: e.message, From 818aa60aac185da07bfb71b08e0448939b63a736 Mon Sep 17 00:00:00 2001 From: Joaquin Olivero <66050823+JoaquinOlivero@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:08:12 -0300 Subject: [PATCH 39/53] feat: blacklist items from Discover page (#632) * feat: blacklist media items re #490 * feat: blacklist media items * feat: blacklist media items * style: formatting * refactor: close the manage slide-over when the media item is removed from the blacklist * fix: fix media data in the db when blacklisting an item * refactor: refactor component to accept show boolean * refactor: hide watchlist button in the media page when it's blacklisted. Also add a blacklist button * style: formatting --------- Co-authored-by: JoaquinOlivero --- overseerr-api.yml | 103 +++++ server/constants/media.ts | 1 + server/entity/Blacklist.ts | 95 ++++ server/entity/Media.ts | 9 +- server/entity/MediaRequest.ts | 11 + server/interfaces/api/blacklistInterfaces.ts | 14 + server/lib/permissions.ts | 2 + .../migration/1699901142442-AddBlacklist.ts | 20 + server/routes/blacklist.ts | 148 +++++++ server/routes/index.ts | 2 + server/routes/request.ts | 3 + src/components/Blacklist/index.tsx | 417 ++++++++++++++++++ src/components/BlacklistBlock/index.tsx | 129 ++++++ src/components/BlacklistModal/index.tsx | 79 ++++ src/components/CollectionDetails/index.tsx | 39 +- src/components/Common/ListView/index.tsx | 158 ++++--- .../Common/StatusBadgeMini/index.tsx | 11 +- src/components/Layout/Sidebar/index.tsx | 13 + src/components/ManageSlideOver/index.tsx | 125 +++--- src/components/MediaSlider/index.tsx | 102 +++-- src/components/MovieDetails/index.tsx | 147 ++++-- src/components/PermissionEdit/index.tsx | 23 + src/components/RequestButton/index.tsx | 2 + .../RequestModal/CollectionRequestModal.tsx | 253 ++++++----- src/components/StatusBadge/index.tsx | 11 + src/components/TitleCard/index.tsx | 181 +++++++- src/components/TvDetails/index.tsx | 147 ++++-- src/i18n/globalMessages.ts | 10 + src/i18n/locale/en.json | 25 ++ src/pages/blacklist/index.tsx | 13 + 30 files changed, 1941 insertions(+), 352 deletions(-) create mode 100644 server/entity/Blacklist.ts create mode 100644 server/interfaces/api/blacklistInterfaces.ts create mode 100644 server/migration/1699901142442-AddBlacklist.ts create mode 100644 server/routes/blacklist.ts create mode 100644 src/components/Blacklist/index.tsx create mode 100644 src/components/BlacklistBlock/index.tsx create mode 100644 src/components/BlacklistModal/index.tsx create mode 100644 src/pages/blacklist/index.tsx diff --git a/overseerr-api.yml b/overseerr-api.yml index d2403538..f5e1d162 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -38,6 +38,8 @@ tags: description: Endpoints related to getting service (Radarr/Sonarr) details. - name: watchlist description: Collection of media to watch later + - name: blacklist + description: Blacklisted media from discovery page. servers: - url: '{server}/api/v1' variables: @@ -46,6 +48,19 @@ servers: components: schemas: + Blacklist: + type: object + properties: + tmdbId: + type: number + example: 1 + title: + type: string + media: + $ref: '#/components/schemas/MediaInfo' + userId: + type: number + example: 1 Watchlist: type: object properties: @@ -4042,6 +4057,94 @@ paths: restricted: type: boolean example: false + /blacklist: + get: + summary: Returns blacklisted items + description: Returns list of all blacklisted media + tags: + - settings + parameters: + - in: query + name: take + schema: + type: number + nullable: true + example: 25 + - in: query + name: skip + schema: + type: number + nullable: true + example: 0 + - in: query + name: search + schema: + type: string + nullable: true + example: dune + responses: + '200': + description: Blacklisted items returned + content: + application/json: + schema: + type: object + properties: + pageInfo: + $ref: '#/components/schemas/PageInfo' + results: + type: array + items: + type: object + properties: + user: + $ref: '#/components/schemas/User' + createdAt: + type: string + example: 2024-04-21T01:55:44.000Z + id: + type: number + example: 1 + mediaType: + type: string + example: movie + title: + type: string + example: Dune + tmdbId: + type: number + example: 438631 + post: + summary: Add media to blacklist + tags: + - blacklist + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Blacklist' + responses: + '201': + description: Item succesfully blacklisted + '412': + description: Item has already been blacklisted + /blacklist/{tmdbId}: + delete: + summary: Remove media from blacklist + tags: + - blacklist + parameters: + - in: path + name: tmdbId + description: tmdbId ID + required: true + example: '1' + schema: + type: string + responses: + '204': + description: Succesfully removed media item /watchlist: post: summary: Add media to watchlist diff --git a/server/constants/media.ts b/server/constants/media.ts index de2bf834..dbcfbd34 100644 --- a/server/constants/media.ts +++ b/server/constants/media.ts @@ -16,4 +16,5 @@ export enum MediaStatus { PROCESSING, PARTIALLY_AVAILABLE, AVAILABLE, + BLACKLISTED, } diff --git a/server/entity/Blacklist.ts b/server/entity/Blacklist.ts new file mode 100644 index 00000000..5e24419d --- /dev/null +++ b/server/entity/Blacklist.ts @@ -0,0 +1,95 @@ +import { MediaStatus, type MediaType } from '@server/constants/media'; +import { getRepository } from '@server/datasource'; +import Media from '@server/entity/Media'; +import { User } from '@server/entity/User'; +import type { BlacklistItem } from '@server/interfaces/api/blacklistInterfaces'; +import { + Column, + CreateDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + OneToOne, + PrimaryGeneratedColumn, + Unique, +} from 'typeorm'; +import type { ZodNumber, ZodOptional, ZodString } from 'zod'; + +@Entity() +@Unique(['tmdbId']) +export class Blacklist implements BlacklistItem { + @PrimaryGeneratedColumn() + public id: number; + + @Column({ type: 'varchar' }) + public mediaType: MediaType; + + @Column({ nullable: true, type: 'varchar' }) + title?: string; + + @Column() + @Index() + public tmdbId: number; + + @ManyToOne(() => User, (user) => user.id, { + eager: true, + }) + user: User; + + @OneToOne(() => Media, (media) => media.blacklist, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public media: Media; + + @CreateDateColumn() + public createdAt: Date; + + constructor(init?: Partial) { + Object.assign(this, init); + } + + public static async addToBlacklist({ + blacklistRequest, + }: { + blacklistRequest: { + mediaType: MediaType; + title?: ZodOptional['_output']; + tmdbId: ZodNumber['_output']; + }; + }): Promise { + const blacklist = new this({ + ...blacklistRequest, + }); + + const mediaRepository = getRepository(Media); + let media = await mediaRepository.findOne({ + where: { + tmdbId: blacklistRequest.tmdbId, + }, + }); + + const blacklistRepository = getRepository(this); + + await blacklistRepository.save(blacklist); + + if (!media) { + media = new Media({ + tmdbId: blacklistRequest.tmdbId, + status: MediaStatus.BLACKLISTED, + status4k: MediaStatus.BLACKLISTED, + mediaType: blacklistRequest.mediaType, + blacklist: blacklist, + }); + + await mediaRepository.save(media); + } else { + media.blacklist = blacklist; + media.status = MediaStatus.BLACKLISTED; + media.status4k = MediaStatus.BLACKLISTED; + + await mediaRepository.save(media); + } + } +} diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 723eb213..4f64178a 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -3,6 +3,7 @@ import SonarrAPI from '@server/api/servarr/sonarr'; import { MediaStatus, MediaType } from '@server/constants/media'; import { MediaServerType } from '@server/constants/server'; import { getRepository } from '@server/datasource'; +import { Blacklist } from '@server/entity/Blacklist'; import type { User } from '@server/entity/User'; import { Watchlist } from '@server/entity/Watchlist'; import type { DownloadingItem } from '@server/lib/downloadtracker'; @@ -17,6 +18,7 @@ import { Entity, Index, OneToMany, + OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -66,7 +68,7 @@ class Media { try { const media = await mediaRepository.findOne({ - where: { tmdbId: id, mediaType }, + where: { tmdbId: id, mediaType: mediaType }, relations: { requests: true, issues: true }, }); @@ -116,6 +118,11 @@ class Media { @OneToMany(() => Issue, (issue) => issue.media, { cascade: true }) public issues: Issue[]; + @OneToOne(() => Blacklist, (blacklist) => blacklist.media, { + eager: true, + }) + public blacklist: Blacklist; + @CreateDateColumn() public createdAt: Date; diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index ba67ab7b..6b2c7b56 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -40,6 +40,7 @@ export class RequestPermissionError extends Error {} export class QuotaRestrictedError extends Error {} export class DuplicateMediaRequestError extends Error {} export class NoSeasonsAvailableError extends Error {} +export class BlacklistedMediaError extends Error {} type MediaRequestOptions = { isAutoRequest?: boolean; @@ -143,6 +144,16 @@ export class MediaRequest { mediaType: requestBody.mediaType, }); } else { + if (media.status === MediaStatus.BLACKLISTED) { + logger.warn('Request for media blocked due to being blacklisted', { + tmdbId: tmdbMedia.id, + mediaType: requestBody.mediaType, + label: 'Media Request', + }); + + throw new BlacklistedMediaError('This media is blacklisted.'); + } + if (media.status === MediaStatus.UNKNOWN && !requestBody.is4k) { media.status = MediaStatus.PENDING; } diff --git a/server/interfaces/api/blacklistInterfaces.ts b/server/interfaces/api/blacklistInterfaces.ts new file mode 100644 index 00000000..99e56585 --- /dev/null +++ b/server/interfaces/api/blacklistInterfaces.ts @@ -0,0 +1,14 @@ +import type { User } from '@server/entity/User'; +import type { PaginatedResponse } from '@server/interfaces/api/common'; + +export interface BlacklistItem { + tmdbId: number; + mediaType: 'movie' | 'tv'; + title?: string; + createdAt?: Date; + user: User; +} + +export interface BlacklistResultsResponse extends PaginatedResponse { + results: BlacklistItem[]; +} diff --git a/server/lib/permissions.ts b/server/lib/permissions.ts index 4a4a90d8..bc477169 100644 --- a/server/lib/permissions.ts +++ b/server/lib/permissions.ts @@ -27,6 +27,8 @@ export enum Permission { AUTO_REQUEST_TV = 33554432, RECENT_VIEW = 67108864, WATCHLIST_VIEW = 134217728, + MANAGE_BLACKLIST = 268435456, + VIEW_BLACKLIST = 1073741824, } export interface PermissionCheckOptions { diff --git a/server/migration/1699901142442-AddBlacklist.ts b/server/migration/1699901142442-AddBlacklist.ts new file mode 100644 index 00000000..eb096270 --- /dev/null +++ b/server/migration/1699901142442-AddBlacklist.ts @@ -0,0 +1,20 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddBlacklist1699901142442 implements MigrationInterface { + name = 'AddBlacklist1699901142442'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "blacklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')),"userId" integer, "mediaId" integer,CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId", "userId"))` + ); + + await queryRunner.query( + `CREATE INDEX "IDX_6bbafa28411e6046421991ea21" ON "blacklist" ("tmdbId") ` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "blacklist"`); + await queryRunner.query(`DROP INDEX "IDX_6bbafa28411e6046421991ea21"`); + } +} diff --git a/server/routes/blacklist.ts b/server/routes/blacklist.ts new file mode 100644 index 00000000..4a07a499 --- /dev/null +++ b/server/routes/blacklist.ts @@ -0,0 +1,148 @@ +import { MediaType } from '@server/constants/media'; +import { getRepository } from '@server/datasource'; +import { Blacklist } from '@server/entity/Blacklist'; +import Media from '@server/entity/Media'; +import { NotFoundError } from '@server/entity/Watchlist'; +import type { BlacklistResultsResponse } from '@server/interfaces/api/blacklistInterfaces'; +import { Permission } from '@server/lib/permissions'; +import logger from '@server/logger'; +import { isAuthenticated } from '@server/middleware/auth'; +import { Router } from 'express'; +import rateLimit from 'express-rate-limit'; +import { QueryFailedError } from 'typeorm'; +import { z } from 'zod'; + +const blacklistRoutes = Router(); + +export const blacklistAdd = z.object({ + tmdbId: z.coerce.number(), + mediaType: z.nativeEnum(MediaType), + title: z.coerce.string().optional(), + user: z.coerce.number(), +}); + +blacklistRoutes.get( + '/', + isAuthenticated([Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], { + type: 'or', + }), + rateLimit({ windowMs: 60 * 1000, max: 50 }), + async (req, res, next) => { + const pageSize = req.query.take ? Number(req.query.take) : 25; + const skip = req.query.skip ? Number(req.query.skip) : 0; + const search = (req.query.search as string) ?? ''; + + try { + let query = getRepository(Blacklist) + .createQueryBuilder('blacklist') + .leftJoinAndSelect('blacklist.user', 'user'); + + if (search.length > 0) { + query = query.where('blacklist.title like :title', { + title: `%${search}%`, + }); + } + + const [blacklistedItems, itemsCount] = await query + .orderBy('blacklist.createdAt', 'DESC') + .take(pageSize) + .skip(skip) + .getManyAndCount(); + + return res.status(200).json({ + pageInfo: { + pages: Math.ceil(itemsCount / pageSize), + pageSize, + results: itemsCount, + page: Math.ceil(skip / pageSize) + 1, + }, + results: blacklistedItems, + } as BlacklistResultsResponse); + } catch (error) { + logger.error('Something went wrong while retrieving blacklisted items', { + label: 'Blacklist', + errorMessage: error.message, + }); + return next({ + status: 500, + message: 'Unable to retrieve blacklisted items.', + }); + } + } +); + +blacklistRoutes.post( + '/', + isAuthenticated([Permission.MANAGE_BLACKLIST], { + type: 'or', + }), + async (req, res, next) => { + try { + const values = blacklistAdd.parse(req.body); + + await Blacklist.addToBlacklist({ + blacklistRequest: values, + }); + + return res.status(201).send(); + } catch (error) { + if (!(error instanceof Error)) { + return; + } + + if (error instanceof QueryFailedError) { + switch (error.driverError.errno) { + case 19: + return next({ status: 412, message: 'Item already blacklisted' }); + default: + logger.warn('Something wrong with data blacklist', { + tmdbId: req.body.tmdbId, + mediaType: req.body.mediaType, + label: 'Blacklist', + }); + return next({ status: 409, message: 'Something wrong' }); + } + } + + return next({ status: 500, message: error.message }); + } + } +); + +blacklistRoutes.delete( + '/:id', + isAuthenticated([Permission.MANAGE_BLACKLIST], { + type: 'or', + }), + async (req, res, next) => { + try { + const blacklisteRepository = getRepository(Blacklist); + + const blacklistItem = await blacklisteRepository.findOneOrFail({ + where: { tmdbId: Number(req.params.id) }, + }); + + await blacklisteRepository.remove(blacklistItem); + + const mediaRepository = getRepository(Media); + + const mediaItem = await mediaRepository.findOneOrFail({ + where: { tmdbId: Number(req.params.id) }, + }); + + await mediaRepository.remove(mediaItem); + + return res.status(204).send(); + } catch (e) { + if (e instanceof NotFoundError) { + return next({ + status: 401, + message: e.message, + }); + } + return next({ status: 500, message: e.message }); + } + } +); + +export default blacklistRoutes; diff --git a/server/routes/index.ts b/server/routes/index.ts index 12434256..c7c8389e 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -23,6 +23,7 @@ import restartFlag from '@server/utils/restartFlag'; import { isPerson } from '@server/utils/typeHelpers'; import { Router } from 'express'; import authRoutes from './auth'; +import blacklistRoutes from './blacklist'; import collectionRoutes from './collection'; import discoverRoutes, { createTmdbWithRegionLanguage } from './discover'; import issueRoutes from './issue'; @@ -144,6 +145,7 @@ router.use('/search', isAuthenticated(), searchRoutes); router.use('/discover', isAuthenticated(), discoverRoutes); router.use('/request', isAuthenticated(), requestRoutes); router.use('/watchlist', isAuthenticated(), watchlistRoutes); +router.use('/blacklist', isAuthenticated(), blacklistRoutes); router.use('/movie', isAuthenticated(), movieRoutes); router.use('/tv', isAuthenticated(), tvRoutes); router.use('/media', isAuthenticated(), mediaRoutes); diff --git a/server/routes/request.ts b/server/routes/request.ts index 94ae8384..320f149b 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -8,6 +8,7 @@ import { import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; import { + BlacklistedMediaError, DuplicateMediaRequestError, MediaRequest, NoSeasonsAvailableError, @@ -243,6 +244,8 @@ requestRoutes.post( return next({ status: 409, message: error.message }); case NoSeasonsAvailableError: return next({ status: 202, message: error.message }); + case BlacklistedMediaError: + return next({ status: 403, message: error.message }); default: return next({ status: 500, message: error.message }); } diff --git a/src/components/Blacklist/index.tsx b/src/components/Blacklist/index.tsx new file mode 100644 index 00000000..217f4cef --- /dev/null +++ b/src/components/Blacklist/index.tsx @@ -0,0 +1,417 @@ +import Badge from '@app/components/Common/Badge'; +import Button from '@app/components/Common/Button'; +import CachedImage from '@app/components/Common/CachedImage'; +import ConfirmButton from '@app/components/Common/ConfirmButton'; +import Header from '@app/components/Common/Header'; +import LoadingSpinner from '@app/components/Common/LoadingSpinner'; +import PageTitle from '@app/components/Common/PageTitle'; +import useDebouncedState from '@app/hooks/useDebouncedState'; +import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams'; +import { Permission, useUser } from '@app/hooks/useUser'; +import globalMessages from '@app/i18n/globalMessages'; +import Error from '@app/pages/_error'; +import defineMessages from '@app/utils/defineMessages'; +import { + ChevronLeftIcon, + ChevronRightIcon, + MagnifyingGlassIcon, + TrashIcon, +} from '@heroicons/react/24/solid'; +import type { + BlacklistItem, + BlacklistResultsResponse, +} from '@server/interfaces/api/blacklistInterfaces'; +import type { MovieDetails } from '@server/models/Movie'; +import type { TvDetails } from '@server/models/Tv'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import type { ChangeEvent } from 'react'; +import { useState } from 'react'; +import { useInView } from 'react-intersection-observer'; +import { FormattedRelativeTime, useIntl } from 'react-intl'; +import { useToasts } from 'react-toast-notifications'; +import useSWR from 'swr'; + +const messages = defineMessages('components.Blacklist', { + blacklistsettings: 'Blacklist Settings', + blacklistSettingsDescription: 'Manage blacklisted media.', + mediaName: 'Name', + mediaType: 'Type', + mediaTmdbId: 'tmdb Id', + blacklistdate: 'date', + blacklistedby: '{date} by {user}', + blacklistNotFoundError: '{title} is not blacklisted.', +}); + +const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { + return (movie as MovieDetails).title !== undefined; +}; + +const Blacklist = () => { + const [currentPageSize, setCurrentPageSize] = useState(10); + const [searchFilter, debouncedSearchFilter, setSearchFilter] = + useDebouncedState(''); + const router = useRouter(); + const intl = useIntl(); + + const page = router.query.page ? Number(router.query.page) : 1; + const pageIndex = page - 1; + const updateQueryParams = useUpdateQueryParams({ page: page.toString() }); + + const { + data, + error, + mutate: revalidate, + } = useSWR( + `/api/v1/blacklist/?take=${currentPageSize} + &skip=${pageIndex * currentPageSize} + ${debouncedSearchFilter ? `&search=${debouncedSearchFilter}` : ''}`, + { + refreshInterval: 0, + revalidateOnFocus: false, + } + ); + + // check if there's no data and no errors in the table + // so as to show a spinner inside the table and not refresh the whole component + if (!data && error) { + return ; + } + + const searchItem = (e: ChangeEvent) => { + // Remove the "page" query param from the URL + // so that the "skip" query param on line 62 is empty + // and the search returns results without skipping items + if (router.query.page) router.replace(router.basePath); + + setSearchFilter(e.target.value as string); + }; + + const hasNextPage = data && data.pageInfo.pages > pageIndex + 1; + const hasPrevPage = pageIndex > 0; + + return ( + <> + +

{intl.formatMessage(globalMessages.blacklist)}
+ +
+
+ + + + searchItem(e)} + /> +
+
+ + {!data ? ( + + ) : data.results.length === 0 ? ( +
+ + {intl.formatMessage(globalMessages.noresults)} + +
+ ) : ( + data.results.map((item: BlacklistItem) => { + return ( +
+ +
+ ); + }) + )} + +
+ +
+ + ); +}; + +export default Blacklist; + +interface BlacklistedItemProps { + item: BlacklistItem; + revalidateList: () => void; +} + +const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => { + const [isUpdating, setIsUpdating] = useState(false); + const { addToast } = useToasts(); + const { ref, inView } = useInView({ + triggerOnce: true, + }); + const intl = useIntl(); + const { hasPermission } = useUser(); + + const url = + item.mediaType === 'movie' + ? `/api/v1/movie/${item.tmdbId}` + : `/api/v1/tv/${item.tmdbId}`; + const { data: title, error } = useSWR( + inView ? url : null + ); + + if (!title && !error) { + return ( +
+ ); + } + + const removeFromBlacklist = async (tmdbId: number, title?: string) => { + setIsUpdating(true); + + const res = await fetch('/api/v1/blacklist/' + tmdbId, { + method: 'DELETE', + }); + + if (res.status === 204) { + addToast( + + {intl.formatMessage(globalMessages.removeFromBlacklistSuccess, { + title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + revalidateList(); + setIsUpdating(false); + }; + + return ( +
+ {title && title.backdropPath && ( +
+ +
+
+ )} +
+
+ + + +
+
+ {title && + (isMovie(title) + ? title.releaseDate + : title.firstAirDate + )?.slice(0, 4)} +
+ + + {title && (isMovie(title) ? title.title : title.name)} + + +
+
+ +
+
+ Status + + {intl.formatMessage(globalMessages.blacklisted)} + +
+ + {item.createdAt && ( +
+ + {intl.formatMessage(globalMessages.blacklisted)} + + + {intl.formatMessage(messages.blacklistedby, { + date: ( + + ), + user: ( + + + + + {item.user.displayName} + + + + ), + })} + +
+ )} +
+ {item.mediaType === 'movie' ? ( +
+
+ {intl.formatMessage(globalMessages.movie)} +
+
+ ) : ( +
+
+ {intl.formatMessage(globalMessages.tvshow)} +
+
+ )} +
+
+
+
+ {hasPermission(Permission.MANAGE_BLACKLIST) && ( + + removeFromBlacklist( + item.tmdbId, + title && (isMovie(title) ? title.title : title.name) + ) + } + confirmText={intl.formatMessage( + isUpdating ? globalMessages.deleting : globalMessages.areyousure + )} + className={`w-full ${ + isUpdating ? 'pointer-events-none opacity-50' : '' + }`} + > + + + {intl.formatMessage(globalMessages.removefromBlacklist)} + + + )} +
+
+ ); +}; diff --git a/src/components/BlacklistBlock/index.tsx b/src/components/BlacklistBlock/index.tsx new file mode 100644 index 00000000..0908d373 --- /dev/null +++ b/src/components/BlacklistBlock/index.tsx @@ -0,0 +1,129 @@ +import Badge from '@app/components/Common/Badge'; +import Button from '@app/components/Common/Button'; +import Tooltip from '@app/components/Common/Tooltip'; +import { useUser } from '@app/hooks/useUser'; +import globalMessages from '@app/i18n/globalMessages'; +import defineMessages from '@app/utils/defineMessages'; +import { CalendarIcon, TrashIcon, UserIcon } from '@heroicons/react/24/solid'; +import type { Blacklist } from '@server/entity/Blacklist'; +import Link from 'next/link'; +import { useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useToasts } from 'react-toast-notifications'; + +const messages = defineMessages('component.BlacklistBlock', { + blacklistedby: 'Blacklisted By', + blacklistdate: 'Blacklisted date', +}); + +interface BlacklistBlockProps { + blacklistItem: Blacklist; + onUpdate?: () => void; + onDelete?: () => void; +} + +const BlacklistBlock = ({ + blacklistItem, + onUpdate, + onDelete, +}: BlacklistBlockProps) => { + const { user } = useUser(); + const intl = useIntl(); + const [isUpdating, setIsUpdating] = useState(false); + const { addToast } = useToasts(); + + const removeFromBlacklist = async (tmdbId: number, title?: string) => { + setIsUpdating(true); + + const res = await fetch('/api/v1/blacklist/' + tmdbId, { + method: 'DELETE', + }); + + if (res.status === 204) { + addToast( + + {intl.formatMessage(globalMessages.removeFromBlacklistSuccess, { + title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + onUpdate && onUpdate(); + onDelete && onDelete(); + + setIsUpdating(false); + }; + + return ( +
+
+
+
+ + + + + + + {blacklistItem.user.displayName} + + + +
+
+
+ + + +
+
+
+
+
+ + {intl.formatMessage(globalMessages.blacklisted)} + +
+
+
+ + + + + {intl.formatDate(blacklistItem.createdAt, { + year: 'numeric', + month: 'long', + day: 'numeric', + })} + +
+
+
+ ); +}; + +export default BlacklistBlock; diff --git a/src/components/BlacklistModal/index.tsx b/src/components/BlacklistModal/index.tsx new file mode 100644 index 00000000..aeca8d41 --- /dev/null +++ b/src/components/BlacklistModal/index.tsx @@ -0,0 +1,79 @@ +import Modal from '@app/components/Common/Modal'; +import globalMessages from '@app/i18n/globalMessages'; +import defineMessages from '@app/utils/defineMessages'; +import { Transition } from '@headlessui/react'; +import type { MovieDetails } from '@server/models/Movie'; +import type { TvDetails } from '@server/models/Tv'; +import { useIntl } from 'react-intl'; +import useSWR from 'swr'; + +interface BlacklistModalProps { + tmdbId: number; + type: 'movie' | 'tv' | 'collection'; + show: boolean; + onComplete?: () => void; + onCancel?: () => void; + isUpdating?: boolean; +} + +const messages = defineMessages('component.BlacklistModal', { + blacklisting: 'Blacklisting', +}); + +const isMovie = ( + movie: MovieDetails | TvDetails | undefined +): movie is MovieDetails => { + if (!movie) return false; + return (movie as MovieDetails).title !== undefined; +}; + +const BlacklistModal = ({ + tmdbId, + type, + show, + onComplete, + onCancel, + isUpdating, +}: BlacklistModalProps) => { + const intl = useIntl(); + + const { data, error } = useSWR( + `/api/v1/${type}/${tmdbId}` + ); + + return ( + + + + ); +}; + +export default BlacklistModal; diff --git a/src/components/CollectionDetails/index.tsx b/src/components/CollectionDetails/index.tsx index 7afa28e4..9e8ab32a 100644 --- a/src/components/CollectionDetails/index.tsx +++ b/src/components/CollectionDetails/index.tsx @@ -183,6 +183,11 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => { ); } + const blacklistVisibility = hasPermission( + [Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], + { type: 'or' } + ); + return (
{ sliderKey="collection-movies" isLoading={false} isEmpty={data.parts.length === 0} - items={data.parts.map((title) => ( - - ))} + items={data.parts + .filter((title) => { + if (!blacklistVisibility) + return title.mediaInfo?.status !== MediaStatus.BLACKLISTED; + return title; + }) + .map((title) => ( + + ))} />
diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index 46c946ae..f1c3bf66 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -1,8 +1,10 @@ import PersonCard from '@app/components/PersonCard'; import TitleCard from '@app/components/TitleCard'; import TmdbTitleCard from '@app/components/TitleCard/TmdbTitleCard'; +import { Permission, useUser } from '@app/hooks/useUser'; import useVerticalScroll from '@app/hooks/useVerticalScroll'; import globalMessages from '@app/i18n/globalMessages'; +import { MediaStatus } from '@server/constants/media'; import type { WatchlistItem } from '@server/interfaces/api/discoverInterfaces'; import type { CollectionResult, @@ -32,7 +34,14 @@ const ListView = ({ mutateParent, }: ListViewProps) => { const intl = useIntl(); + const { hasPermission } = useUser(); useVerticalScroll(onScrollBottom, !isLoading && !isEmpty && !isReachingEnd); + + const blacklistVisibility = hasPermission( + [Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], + { type: 'or' } + ); + return ( <> {isEmpty && ( @@ -55,76 +64,89 @@ const ListView = ({ ); })} - {items?.map((title, index) => { - let titleCard: React.ReactNode; + {items + ?.filter((title) => { + if (!blacklistVisibility) + return ( + (title as TvResult | MovieResult).mediaInfo?.status !== + MediaStatus.BLACKLISTED + ); + return title; + }) + .map((title, index) => { + let titleCard: React.ReactNode; - switch (title.mediaType) { - case 'movie': - titleCard = ( - 0 - } - canExpand - /> - ); - break; - case 'tv': - titleCard = ( - 0 - } - canExpand - /> - ); - break; - case 'collection': - titleCard = ( - - ); - break; - case 'person': - titleCard = ( - - ); - break; - } + switch (title.mediaType) { + case 'movie': + titleCard = ( + 0 + } + canExpand + /> + ); + break; + case 'tv': + titleCard = ( + 0 + } + canExpand + /> + ); + break; + case 'collection': + titleCard = ( + + ); + break; + case 'person': + titleCard = ( + + ); + break; + } - return
  • {titleCard}
  • ; - })} + return
  • {titleCard}
  • ; + })} {isLoading && !isReachingEnd && [...Array(20)].map((_item, i) => ( diff --git a/src/components/Common/StatusBadgeMini/index.tsx b/src/components/Common/StatusBadgeMini/index.tsx index a7e24a37..afcd72bf 100644 --- a/src/components/Common/StatusBadgeMini/index.tsx +++ b/src/components/Common/StatusBadgeMini/index.tsx @@ -1,6 +1,11 @@ import Spinner from '@app/assets/spinner.svg'; import { CheckCircleIcon } from '@heroicons/react/20/solid'; -import { BellIcon, ClockIcon, MinusSmallIcon } from '@heroicons/react/24/solid'; +import { + BellIcon, + ClockIcon, + EyeSlashIcon, + MinusSmallIcon, +} from '@heroicons/react/24/solid'; import { MediaStatus } from '@server/constants/media'; interface StatusBadgeMiniProps { @@ -44,6 +49,10 @@ const StatusBadgeMini = ({ ); indicatorIcon = ; break; + case MediaStatus.BLACKLISTED: + badgeStyle.push('bg-red-500 border-white-400 ring-white-400 text-white'); + indicatorIcon = ; + break; case MediaStatus.PARTIALLY_AVAILABLE: badgeStyle.push( 'bg-green-500 border-green-400 ring-green-400 text-green-100' diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx index d9b7d3fb..a947e262 100644 --- a/src/components/Layout/Sidebar/index.tsx +++ b/src/components/Layout/Sidebar/index.tsx @@ -8,6 +8,7 @@ import { ClockIcon, CogIcon, ExclamationTriangleIcon, + EyeSlashIcon, FilmIcon, SparklesIcon, TvIcon, @@ -25,6 +26,7 @@ export const menuMessages = defineMessages('components.Layout.Sidebar', { browsemovies: 'Movies', browsetv: 'Series', requests: 'Requests', + blacklist: 'Blacklist', issues: 'Issues', users: 'Users', settings: 'Settings', @@ -71,6 +73,17 @@ const SidebarLinks: SidebarLinkProps[] = [ svgIcon: , activeRegExp: /^\/requests/, }, + { + href: '/blacklist', + messagesKey: 'blacklist', + svgIcon: , + activeRegExp: /^\/blacklist/, + requiredPermission: [ + Permission.MANAGE_BLACKLIST, + Permission.VIEW_BLACKLIST, + ], + permissionType: 'or', + }, { href: '/issues', messagesKey: 'issues', diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index b669ebb4..0f96aa20 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -1,3 +1,4 @@ +import BlacklistBlock from '@app/components/BlacklistBlock'; import Button from '@app/components/Common/Button'; import ConfirmButton from '@app/components/Common/ConfirmButton'; import SlideOver from '@app/components/Common/SlideOver'; @@ -284,6 +285,20 @@ const ManageSlideOver = ({
    )} + {data.mediaInfo?.status === MediaStatus.BLACKLISTED && ( +
    +

    + {intl.formatMessage(globalMessages.blacklist)} +

    +
    + revalidate()} + onDelete={() => onClose()} + /> +
    +
    + )} {hasPermission(Permission.ADMIN) && (data.mediaInfo?.serviceUrl || data.mediaInfo?.tautulliUrl || @@ -603,32 +618,17 @@ const ManageSlideOver = ({
    )} - {hasPermission(Permission.ADMIN) && data?.mediaInfo && ( -
    -

    - {intl.formatMessage(messages.manageModalAdvanced)} -

    -
    - {data?.mediaInfo.status !== MediaStatus.AVAILABLE && ( - - )} - {data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && - settings.currentSettings.series4kEnabled && ( + {hasPermission(Permission.ADMIN) && + data?.mediaInfo && + data.mediaInfo.status !== MediaStatus.BLACKLISTED && ( +
    +

    + {intl.formatMessage(messages.manageModalAdvanced)} +

    +
    + {data?.mediaInfo.status !== MediaStatus.AVAILABLE && ( )} -
    - deleteMedia()} - confirmText={intl.formatMessage(globalMessages.areyousure)} - className="w-full" - > - - - {intl.formatMessage(messages.manageModalClearMedia)} - - -
    - {intl.formatMessage(messages.manageModalClearMediaWarning, { - mediaType: intl.formatMessage( - mediaType === 'movie' ? messages.movie : messages.tvshow - ), - mediaServerName: - settings.currentSettings.mediaServerType === - MediaServerType.EMBY - ? 'Emby' - : settings.currentSettings.mediaServerType === - MediaServerType.PLEX - ? 'Plex' - : 'Jellyfin', - })} + {data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && + settings.currentSettings.series4kEnabled && ( + + )} +
    + deleteMedia()} + confirmText={intl.formatMessage(globalMessages.areyousure)} + className="w-full" + > + + + {intl.formatMessage(messages.manageModalClearMedia)} + + +
    + {intl.formatMessage(messages.manageModalClearMediaWarning, { + mediaType: intl.formatMessage( + mediaType === 'movie' ? messages.movie : messages.tvshow + ), + mediaServerName: + settings.currentSettings.mediaServerType === + MediaServerType.EMBY + ? 'Emby' + : settings.currentSettings.mediaServerType === + MediaServerType.PLEX + ? 'Plex' + : 'Jellyfin', + })} +
    -
    - )} + )}
    ); diff --git a/src/components/MediaSlider/index.tsx b/src/components/MediaSlider/index.tsx index 56e0afc8..006f0df9 100644 --- a/src/components/MediaSlider/index.tsx +++ b/src/components/MediaSlider/index.tsx @@ -3,8 +3,10 @@ import PersonCard from '@app/components/PersonCard'; import Slider from '@app/components/Slider'; import TitleCard from '@app/components/TitleCard'; import useSettings from '@app/hooks/useSettings'; +import { useUser } from '@app/hooks/useUser'; import { ArrowRightCircleIcon } from '@heroicons/react/24/outline'; import { MediaStatus } from '@server/constants/media'; +import { Permission } from '@server/lib/permissions'; import type { MovieResult, PersonResult, @@ -41,6 +43,7 @@ const MediaSlider = ({ onNewTitles, }: MediaSliderProps) => { const settings = useSettings(); + const { hasPermission } = useUser(); const { data, error, setSize, size } = useSWRInfinite( (pageIndex: number, previousPageData: MixedResult | null) => { if (previousPageData && pageIndex + 1 > previousPageData.totalPages) { @@ -90,50 +93,65 @@ const MediaSlider = ({ return null; } - const finalTitles = titles.slice(0, 20).map((title) => { - switch (title.mediaType) { - case 'movie': + const blacklistVisibility = hasPermission( + [Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], + { type: 'or' } + ); + + const finalTitles = titles + .slice(0, 20) + .filter((title) => { + if (!blacklistVisibility) return ( - 0} - /> + (title as TvResult | MovieResult).mediaInfo?.status !== + MediaStatus.BLACKLISTED ); - case 'tv': - return ( - 0} - /> - ); - case 'person': - return ( - - ); - } - }); + return title; + }) + .map((title) => { + switch (title.mediaType) { + case 'movie': + return ( + 0} + /> + ); + case 'tv': + return ( + 0} + /> + ); + case 'person': + return ( + + ); + } + }); if (linkUrl && titles.length > 20) { finalTitles.push( diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index e4bc991e..c6583e3d 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -5,6 +5,7 @@ import RTRotten from '@app/assets/rt_rotten.svg'; import ImdbLogo from '@app/assets/services/imdb.svg'; import Spinner from '@app/assets/spinner.svg'; import TmdbLogo from '@app/assets/tmdb_logo.svg'; +import BlacklistModal from '@app/components/BlacklistModal'; import Button from '@app/components/Common/Button'; import CachedImage from '@app/components/Common/CachedImage'; import LoadingSpinner from '@app/components/Common/LoadingSpinner'; @@ -35,6 +36,7 @@ import { CloudIcon, CogIcon, ExclamationTriangleIcon, + EyeSlashIcon, FilmIcon, PlayIcon, TicketIcon, @@ -55,7 +57,7 @@ import 'country-flag-icons/3x2/flags.css'; import { uniqBy } from 'lodash'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; @@ -125,6 +127,9 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { const [toggleWatchlist, setToggleWatchlist] = useState( !movie?.onUserWatchlist ); + const [isBlacklistUpdating, setIsBlacklistUpdating] = + useState(false); + const [showBlacklistModal, setShowBlacklistModal] = useState(false); const { addToast } = useToasts(); const { @@ -155,6 +160,11 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { setShowManager(router.query.manage == '1' ? true : false); }, [router.query.manage]); + const closeBlacklistModal = useCallback( + () => setShowBlacklistModal(false), + [] + ); + const { mediaUrl: plexUrl, mediaUrl4k: plexUrl4k } = useDeepLinks({ mediaUrl: data?.mediaInfo?.mediaUrl, mediaUrl4k: data?.mediaInfo?.mediaUrl4k, @@ -374,6 +384,60 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { } }; + const onClickHideItemBtn = async (): Promise => { + setIsBlacklistUpdating(true); + + const res = await fetch('/api/v1/blacklist', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + tmdbId: movie?.id, + mediaType: 'movie', + title: movie?.title, + user: user?.id, + }), + }); + + if (res.status === 201) { + addToast( + + {intl.formatMessage(globalMessages.blacklistSuccess, { + title: movie?.title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + + revalidate(); + } else if (res.status === 412) { + addToast( + + {intl.formatMessage(globalMessages.blacklistDuplicateError, { + title: movie?.title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'info', autoDismiss: true } + ); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + setIsBlacklistUpdating(false); + closeBlacklistModal(); + }; + + const showHideButton = hasPermission([Permission.MANAGE_BLACKLIST], { + type: 'or', + }); + return (
    { revalidate={() => revalidate()} show={showManager} /> +
    {
    - <> - {toggleWatchlist ? ( - + {showHideButton && + data?.mediaInfo?.status !== MediaStatus.PROCESSING && + data?.mediaInfo?.status !== MediaStatus.AVAILABLE && + data?.mediaInfo?.status !== MediaStatus.PARTIALLY_AVAILABLE && + data?.mediaInfo?.status !== MediaStatus.PENDING && + data?.mediaInfo?.status !== MediaStatus.BLACKLISTED && ( + - - ) : ( - - )} - + {data?.mediaInfo?.status !== MediaStatus.BLACKLISTED && ( + <> + {toggleWatchlist ? ( + + + + ) : ( + + + + )} + + )} { - return (data?.parts ?? []).map((part) => part.id); + return (data?.parts ?? []) + .filter((part) => part.mediaInfo?.status !== MediaStatus.BLACKLISTED) + .map((part) => part.id); }; const getAllRequestedParts = (): number[] => { @@ -248,6 +250,11 @@ const CollectionRequestModal = ({ { type: 'or' } ); + const blacklistVisibility = hasPermission( + [Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], + { type: 'or' } + ); + return ( - {data?.parts.map((part) => { - const partRequest = getPartRequest(part.id); - const partMedia = - part.mediaInfo && - part.mediaInfo[is4k ? 'status4k' : 'status'] !== - MediaStatus.UNKNOWN - ? part.mediaInfo - : undefined; + {data?.parts + .filter((part) => { + if (!blacklistVisibility) + return ( + part.mediaInfo?.status !== MediaStatus.BLACKLISTED + ); + return part; + }) + .map((part) => { + const partRequest = getPartRequest(part.id); + const partMedia = + part.mediaInfo && + part.mediaInfo[is4k ? 'status4k' : 'status'] !== + MediaStatus.UNKNOWN + ? part.mediaInfo + : undefined; - return ( - - - togglePart(part.id)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === 'Space') { - togglePart(part.id); - } - }} - className={`relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none ${ - !!partMedia || - partRequest || - (quota?.movie.limit && - currentlyRemaining <= 0 && - !isSelectedPart(part.id)) - ? 'opacity-50' - : '' + return ( + + - - - - -
    - togglePart(part.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === 'Space') { + togglePart(part.id); + } }} - width={600} - height={900} - /> -
    -
    -
    - {part.releaseDate?.slice(0, 4)} + className={`relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center pt-2 focus:outline-none ${ + (!!partMedia && + partMedia.status !== + MediaStatus.BLACKLISTED) || + partRequest || + (quota?.movie.limit && + currentlyRemaining <= 0 && + !isSelectedPart(part.id)) + ? 'opacity-50' + : '' + }`} + > + + + + + +
    +
    -
    - {part.title} +
    +
    + {part.releaseDate?.slice(0, 4)} +
    +
    + {part.title} +
    -
    - - - {!partMedia && !partRequest && ( - - {intl.formatMessage(globalMessages.notrequested)} - - )} - {!partMedia && - partRequest?.status === - MediaRequestStatus.PENDING && ( - - {intl.formatMessage(globalMessages.pending)} + + + {!partMedia && !partRequest && ( + + {intl.formatMessage( + globalMessages.notrequested + )} )} - {((!partMedia && - partRequest?.status === - MediaRequestStatus.APPROVED) || - partMedia?.[is4k ? 'status4k' : 'status'] === - MediaStatus.PROCESSING) && ( - - {intl.formatMessage(globalMessages.requested)} - - )} - {partMedia?.[is4k ? 'status4k' : 'status'] === - MediaStatus.AVAILABLE && ( - - {intl.formatMessage(globalMessages.available)} - - )} - - - ); - })} + {!partMedia && + partRequest?.status === + MediaRequestStatus.PENDING && ( + + {intl.formatMessage(globalMessages.pending)} + + )} + {((!partMedia && + partRequest?.status === + MediaRequestStatus.APPROVED) || + partMedia?.[is4k ? 'status4k' : 'status'] === + MediaStatus.PROCESSING) && ( + + {intl.formatMessage(globalMessages.requested)} + + )} + {partMedia?.[is4k ? 'status4k' : 'status'] === + MediaStatus.AVAILABLE && ( + + {intl.formatMessage(globalMessages.available)} + + )} + {partMedia?.status === MediaStatus.BLACKLISTED && ( + + {intl.formatMessage(globalMessages.blacklisted)} + + )} + + + ); + })}
    diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index 1d280d28..0821c017 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -360,6 +360,17 @@ const StatusBadge = ({ ); + case MediaStatus.BLACKLISTED: + return ( + + + {intl.formatMessage(is4k ? messages.status4k : messages.status, { + status: intl.formatMessage(globalMessages.blacklisted), + })} + + + ); + default: return null; } diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index b6c88796..2d10fdf1 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -1,7 +1,9 @@ import Spinner from '@app/assets/spinner.svg'; +import BlacklistModal from '@app/components/BlacklistModal'; import Button from '@app/components/Common/Button'; import CachedImage from '@app/components/Common/CachedImage'; import StatusBadgeMini from '@app/components/Common/StatusBadgeMini'; +import Tooltip from '@app/components/Common/Tooltip'; import RequestModal from '@app/components/RequestModal'; import ErrorCard from '@app/components/TitleCard/ErrorCard'; import Placeholder from '@app/components/TitleCard/Placeholder'; @@ -13,6 +15,8 @@ import { withProperties } from '@app/utils/typeHelpers'; import { Transition } from '@headlessui/react'; import { ArrowDownTrayIcon, + EyeIcon, + EyeSlashIcon, MinusCircleIcon, StarIcon, } from '@heroicons/react/24/outline'; @@ -20,7 +24,7 @@ import { MediaStatus } from '@server/constants/media'; import type { Watchlist } from '@server/entity/Watchlist'; import type { MediaType } from '@server/models/Search'; import Link from 'next/link'; -import { Fragment, useCallback, useEffect, useState } from 'react'; +import { Fragment, useCallback, useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import { mutate } from 'swr'; @@ -65,7 +69,7 @@ const TitleCard = ({ }: TitleCardProps) => { const isTouch = useIsTouch(); const intl = useIntl(); - const { hasPermission } = useUser(); + const { user, hasPermission } = useUser(); const [isUpdating, setIsUpdating] = useState(false); const [currentStatus, setCurrentStatus] = useState(status); const [showDetail, setShowDetail] = useState(false); @@ -74,6 +78,8 @@ const TitleCard = ({ const [toggleWatchlist, setToggleWatchlist] = useState( !isAddedToWatchlist ); + const [showBlacklistModal, setShowBlacklistModal] = useState(false); + const cardRef = useRef(null); // Just to get the year from the date if (year) { @@ -94,6 +100,11 @@ const TitleCard = ({ [] ); + const closeBlacklistModal = useCallback( + () => setShowBlacklistModal(false), + [] + ); + const onClickWatchlistBtn = async (): Promise => { setIsUpdating(true); try { @@ -166,6 +177,99 @@ const TitleCard = ({ } }; + const onClickHideItemBtn = async (): Promise => { + setIsUpdating(true); + const topNode = cardRef.current; + + if (topNode) { + const res = await fetch('/api/v1/blacklist', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + tmdbId: id, + mediaType, + title, + user: user?.id, + }), + }); + + if (res.status === 201) { + addToast( + + {intl.formatMessage(globalMessages.blacklistSuccess, { + title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + setCurrentStatus(MediaStatus.BLACKLISTED); + } else if (res.status === 412) { + addToast( + + {intl.formatMessage(globalMessages.blacklistDuplicateError, { + title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'info', autoDismiss: true } + ); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + setIsUpdating(false); + closeBlacklistModal(); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + }; + + const onClickShowBlacklistBtn = async (): Promise => { + setIsUpdating(true); + const topNode = cardRef.current; + + if (topNode) { + const res = await fetch('/api/v1/blacklist/' + id, { + method: 'DELETE', + }); + + if (res.status === 204) { + addToast( + + {intl.formatMessage(globalMessages.removeFromBlacklistSuccess, { + title, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + setCurrentStatus(MediaStatus.UNKNOWN); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + setIsUpdating(false); + }; + const closeModal = useCallback(() => setShowRequestModal(false), []); const showRequestButton = hasPermission( @@ -178,10 +282,15 @@ const TitleCard = ({ { type: 'or' } ); + const showHideButton = hasPermission([Permission.MANAGE_BLACKLIST], { + type: 'or', + }); + return (
    +
    - {showDetail && ( - <> + {showDetail && currentStatus !== MediaStatus.BLACKLISTED && ( +
    {toggleWatchlist ? ( )} - + {showHideButton && + currentStatus !== MediaStatus.PROCESSING && + currentStatus !== MediaStatus.AVAILABLE && + currentStatus !== MediaStatus.PARTIALLY_AVAILABLE && + currentStatus !== MediaStatus.PENDING && ( + + )} +
    )} + {showDetail && + showHideButton && + currentStatus == MediaStatus.BLACKLISTED && ( + + + + )} {currentStatus && currentStatus !== MediaStatus.UNKNOWN && ( -
    - +
    +
    + +
    )}
    diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 634c72d0..cf788237 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -4,6 +4,7 @@ import RTFresh from '@app/assets/rt_fresh.svg'; import RTRotten from '@app/assets/rt_rotten.svg'; import Spinner from '@app/assets/spinner.svg'; import TmdbLogo from '@app/assets/tmdb_logo.svg'; +import BlacklistModal from '@app/components/BlacklistModal'; import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; import CachedImage from '@app/components/Common/CachedImage'; @@ -38,6 +39,7 @@ import { ArrowRightCircleIcon, CogIcon, ExclamationTriangleIcon, + EyeSlashIcon, FilmIcon, PlayIcon, } from '@heroicons/react/24/outline'; @@ -61,7 +63,7 @@ import { countries } from 'country-flag-icons'; import 'country-flag-icons/3x2/flags.css'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; @@ -125,6 +127,9 @@ const TvDetails = ({ tv }: TvDetailsProps) => { const [toggleWatchlist, setToggleWatchlist] = useState( !tv?.onUserWatchlist ); + const [isBlacklistUpdating, setIsBlacklistUpdating] = + useState(false); + const [showBlacklistModal, setShowBlacklistModal] = useState(false); const { addToast } = useToasts(); const { @@ -155,6 +160,11 @@ const TvDetails = ({ tv }: TvDetailsProps) => { setShowManager(router.query.manage == '1' ? true : false); }, [router.query.manage]); + const closeBlacklistModal = useCallback( + () => setShowBlacklistModal(false), + [] + ); + const { mediaUrl: plexUrl, mediaUrl4k: plexUrl4k } = useDeepLinks({ mediaUrl: data?.mediaInfo?.mediaUrl, mediaUrl4k: data?.mediaInfo?.mediaUrl4k, @@ -397,6 +407,60 @@ const TvDetails = ({ tv }: TvDetailsProps) => { } }; + const onClickHideItemBtn = async (): Promise => { + setIsBlacklistUpdating(true); + + const res = await fetch('/api/v1/blacklist', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + tmdbId: tv?.id, + mediaType: 'tv', + title: tv?.name, + user: user?.id, + }), + }); + + if (res.status === 201) { + addToast( + + {intl.formatMessage(globalMessages.blacklistSuccess, { + title: tv?.name, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'success', autoDismiss: true } + ); + + revalidate(); + } else if (res.status === 412) { + addToast( + + {intl.formatMessage(globalMessages.blacklistDuplicateError, { + title: tv?.name, + strong: (msg: React.ReactNode) => {msg}, + })} + , + { appearance: 'info', autoDismiss: true } + ); + } else { + addToast(intl.formatMessage(globalMessages.blacklistError), { + appearance: 'error', + autoDismiss: true, + }); + } + + setIsBlacklistUpdating(false); + closeBlacklistModal(); + }; + + const showHideButton = hasPermission([Permission.MANAGE_BLACKLIST], { + type: 'or', + }); + return (
    {
    )} + setShowIssueModal(false)} show={showIssueModal} @@ -528,40 +600,61 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
    - <> - {toggleWatchlist ? ( - + {showHideButton && + data?.mediaInfo?.status !== MediaStatus.PROCESSING && + data?.mediaInfo?.status !== MediaStatus.AVAILABLE && + data?.mediaInfo?.status !== MediaStatus.PARTIALLY_AVAILABLE && + data?.mediaInfo?.status !== MediaStatus.PENDING && + data?.mediaInfo?.status !== MediaStatus.BLACKLISTED && ( + - - ) : ( - - )} - + {data?.mediaInfo?.status !== MediaStatus.BLACKLISTED && ( + <> + {toggleWatchlist ? ( + + + + ) : ( + + + + )} + + )} {title} was successfully blacklisted.', + blacklistError: 'Something went wrong try again.', + blacklistDuplicateError: + '{title} has already been blacklisted.', + removeFromBlacklistSuccess: + '{title} was successfully removed from the Blacklist.', + addToBlacklist: 'Add to Blacklist', + removefromBlacklist: 'Remove from Blacklist', }); export default globalMessages; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index cf66b67e..42e8e6f5 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1,7 +1,18 @@ { + "component.BlacklistBlock.blacklistdate": "Blacklisted date", + "component.BlacklistBlock.blacklistedby": "Blacklisted By", + "component.BlacklistModal.blacklisting": "Blacklisting", "components.AirDateBadge.airedrelative": "Aired {relativeTime}", "components.AirDateBadge.airsrelative": "Airing {relativeTime}", "components.AppDataWarning.dockerVolumeMissingDescription": "The {appDataPath} volume mount was not configured properly. All data will be cleared when the container is stopped or restarted.", + "components.Blacklist.blacklistNotFoundError": "{title} is not blacklisted.", + "components.Blacklist.blacklistSettingsDescription": "Manage blacklisted media.", + "components.Blacklist.blacklistdate": "date", + "components.Blacklist.blacklistedby": "{date} by {user}", + "components.Blacklist.blacklistsettings": "Blacklist Settings", + "components.Blacklist.mediaName": "Name", + "components.Blacklist.mediaTmdbId": "tmdb Id", + "components.Blacklist.mediaType": "Type", "components.CollectionDetails.numberofmovies": "{count} Movies", "components.CollectionDetails.overview": "Overview", "components.CollectionDetails.requestcollection": "Request Collection", @@ -200,6 +211,7 @@ "components.LanguageSelector.originalLanguageDefault": "All Languages", "components.Layout.LanguagePicker.displaylanguage": "Display Language", "components.Layout.SearchInput.searchPlaceholder": "Search Movies & TV", + "components.Layout.Sidebar.blacklist": "Blacklist", "components.Layout.Sidebar.browsemovies": "Movies", "components.Layout.Sidebar.browsetv": "Series", "components.Layout.Sidebar.dashboard": "Discover", @@ -387,8 +399,12 @@ "components.PermissionEdit.autorequestMoviesDescription": "Grant permission to automatically submit requests for non-4K movies via Plex Watchlist.", "components.PermissionEdit.autorequestSeries": "Auto-Request Series", "components.PermissionEdit.autorequestSeriesDescription": "Grant permission to automatically submit requests for non-4K series via Plex Watchlist.", + "components.PermissionEdit.blacklistedItems": "Blacklist media.", + "components.PermissionEdit.blacklistedItemsDescription": "Grant permission to blacklist media.", "components.PermissionEdit.createissues": "Report Issues", "components.PermissionEdit.createissuesDescription": "Grant permission to report media issues.", + "components.PermissionEdit.manageblacklist": "Manage Blacklist", + "components.PermissionEdit.manageblacklistDescription": "Grant permission to manage blacklisted media.", "components.PermissionEdit.manageissues": "Manage Issues", "components.PermissionEdit.manageissuesDescription": "Grant permission to manage media issues.", "components.PermissionEdit.managerequests": "Manage Requests", @@ -407,6 +423,8 @@ "components.PermissionEdit.requestTvDescription": "Grant permission to submit requests for non-4K series.", "components.PermissionEdit.users": "Manage Users", "components.PermissionEdit.usersDescription": "Grant permission to manage users. Users with this permission cannot modify users with or grant the Admin privilege.", + "components.PermissionEdit.viewblacklistedItems": "View blacklisted media.", + "components.PermissionEdit.viewblacklistedItemsDescription": "Grant permission to view blacklisted media.", "components.PermissionEdit.viewissues": "View Issues", "components.PermissionEdit.viewissuesDescription": "Grant permission to view media issues reported by other users.", "components.PermissionEdit.viewrecent": "View Recently Added", @@ -1299,6 +1317,11 @@ "i18n.areyousure": "Are you sure?", "i18n.available": "Available", "i18n.back": "Back", + "i18n.blacklist": "Blacklist", + "i18n.blacklistDuplicateError": "{title} has already been blacklisted.", + "i18n.blacklistError": "Something went wrong try again.", + "i18n.blacklistSuccess": "{title} was successfully blacklisted.", + "i18n.blacklisted": "Blacklisted", "i18n.cancel": "Cancel", "i18n.canceling": "Canceling…", "i18n.close": "Close", @@ -1324,6 +1347,8 @@ "i18n.pending": "Pending", "i18n.previous": "Previous", "i18n.processing": "Processing", + "i18n.removeFromBlacklistSuccess": "{title} was successfully removed from the Blacklist.", + "i18n.removefromBlacklist": "Remove from Blacklist", "i18n.request": "Request", "i18n.request4k": "Request in 4K", "i18n.requested": "Requested", diff --git a/src/pages/blacklist/index.tsx b/src/pages/blacklist/index.tsx new file mode 100644 index 00000000..e7e3903b --- /dev/null +++ b/src/pages/blacklist/index.tsx @@ -0,0 +1,13 @@ +import Blacklist from '@app/components/Blacklist'; +import useRouteGuard from '@app/hooks/useRouteGuard'; +import { Permission } from '@server/lib/permissions'; +import type { NextPage } from 'next'; + +const BlacklistPage: NextPage = () => { + useRouteGuard([Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], { + type: 'or', + }); + return ; +}; + +export default BlacklistPage; From 2b05fffaceea49b51edfacd14ccb4675dc0959a1 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Tue, 17 Sep 2024 08:12:00 +0200 Subject: [PATCH 40/53] chore(issuetemplate): update defaults labels of GitHub issues (#968) --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- .github/ISSUE_TEMPLATE/enhancement.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a98da750..3f760018 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,6 @@ name: 🐛 Bug Report description: Report a problem -labels: ['type:bug', 'awaiting-triage'] +labels: ['bug', 'awaiting triage'] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 35a7adbd..4327a8f6 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -1,6 +1,6 @@ name: ✨ Feature Request description: Suggest an idea -labels: ['type:enhancement', 'awaiting-triage'] +labels: ['enhancement', 'awaiting triage'] body: - type: markdown attributes: From edfd80444c3d8b9d82998f8a7bbb0b3496701602 Mon Sep 17 00:00:00 2001 From: Joaquin Olivero <66050823+JoaquinOlivero@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:38:14 -0300 Subject: [PATCH 41/53] refactor: Proxy and cache avatar images (#907) * refactor: proxy and cache user avatar images * fix: extract keys * fix: set avatar image URL * fix: show the correct avatar in the list of available users in advanced request * fix(s): set correct src URL for cached image * fix: remove unexpired unused image when a user changes their avatar * fix: requested changes * refactor: use 'mime' package to detmerine file extension * style: grammar * refactor: checks if the default avatar is cached to avoid creating duplicates for different users * fix: fix vulnerability * fix: fix incomplete URL substring sanitization * refactor: only cache avatar with http url protocol * fix: remove log and correctly set the if statement for the cached image component * fix: avatar images not showing on issues page * style: formatting --------- Co-authored-by: JoaquinOlivero --- next.config.js | 1 - overseerr-api.yml | 9 ++ package.json | 2 + pnpm-lock.yaml | 25 +++- server/index.ts | 2 + server/interfaces/api/settingsInterfaces.ts | 2 +- server/job/schedule.ts | 3 + server/lib/imageproxy.ts | 135 +++++++++++++----- server/routes/auth.ts | 20 ++- server/routes/avatarproxy.ts | 32 +++++ server/routes/settings/index.ts | 2 + src/components/Common/CachedImage/index.tsx | 7 +- .../IssueDetails/IssueComment/index.tsx | 6 +- src/components/IssueDetails/index.tsx | 7 +- src/components/IssueList/IssueItem/index.tsx | 5 +- src/components/Layout/UserDropdown/index.tsx | 10 +- src/components/ManageSlideOver/index.tsx | 6 +- src/components/RequestCard/index.tsx | 5 +- .../RequestList/RequestItem/index.tsx | 9 +- .../RequestModal/AdvancedRequester/index.tsx | 6 +- .../Settings/SettingsJobsCache/index.tsx | 14 ++ .../UserList/JellyfinImportModal.tsx | 4 +- src/components/UserList/index.tsx | 4 +- .../UserProfile/ProfileHeader/index.tsx | 4 +- src/i18n/locale/en.json | 2 + 25 files changed, 238 insertions(+), 84 deletions(-) create mode 100644 server/routes/avatarproxy.ts diff --git a/next.config.js b/next.config.js index 35a316c6..43aa421d 100644 --- a/next.config.js +++ b/next.config.js @@ -10,7 +10,6 @@ module.exports = { remotePatterns: [ { hostname: 'gravatar.com' }, { hostname: 'image.tmdb.org' }, - { hostname: '*', protocol: 'https' }, ], }, webpack(config) { diff --git a/overseerr-api.yml b/overseerr-api.yml index f5e1d162..96a4520a 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -2790,6 +2790,15 @@ paths: imageCount: type: number example: 123 + avatar: + type: object + properties: + size: + type: number + example: 123456 + imageCount: + type: number + example: 123 apiCaches: type: array items: diff --git a/package.json b/package.json index 426d774d..9ce9330f 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "formik": "^2.4.6", "gravatar-url": "3.1.0", "lodash": "4.17.21", + "mime": "3", "next": "^14.2.4", "node-cache": "5.1.2", "node-gyp": "9.3.1", @@ -119,6 +120,7 @@ "@types/express": "4.17.17", "@types/express-session": "1.17.6", "@types/lodash": "4.14.191", + "@types/mime": "3", "@types/node": "20.14.8", "@types/node-schedule": "2.1.0", "@types/nodemailer": "6.4.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea593fea..7391a775 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: lodash: specifier: 4.17.21 version: 4.17.21 + mime: + specifier: '3' + version: 3.0.0 next: specifier: ^14.2.4 version: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -264,6 +267,9 @@ importers: '@types/lodash': specifier: 4.14.191 version: 4.14.191 + '@types/mime': + specifier: '3' + version: 3.0.4 '@types/node': specifier: 20.14.8 version: 20.14.8 @@ -2848,6 +2854,9 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/mime@3.0.4': + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} @@ -10836,7 +10845,7 @@ snapshots: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.6.2 + semver: 7.3.8 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -10911,13 +10920,13 @@ snapshots: '@npmcli/fs@1.1.1': dependencies: '@gar/promisify': 1.1.3 - semver: 7.6.2 + semver: 7.3.8 optional: true '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 - semver: 7.6.2 + semver: 7.3.8 '@npmcli/move-file@1.1.2': dependencies: @@ -12326,7 +12335,7 @@ snapshots: read-pkg: 5.2.0 registry-auth-token: 5.0.2 semantic-release: 19.0.5(encoding@0.1.13) - semver: 7.6.2 + semver: 7.3.8 tempy: 1.0.1 '@semantic-release/release-notes-generator@10.0.3(semantic-release@19.0.5(encoding@0.1.13))': @@ -12670,6 +12679,8 @@ snapshots: '@types/mime@1.3.5': {} + '@types/mime@3.0.4': {} + '@types/minimatch@3.0.5': {} '@types/minimist@1.2.5': {} @@ -12887,7 +12898,7 @@ snapshots: debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.2 + semver: 7.3.8 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: typescript: 4.9.5 @@ -17269,7 +17280,7 @@ snapshots: nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.6.2 + semver: 7.3.8 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -17348,7 +17359,7 @@ snapshots: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.14.0 - semver: 7.6.2 + semver: 7.3.8 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} diff --git a/server/index.ts b/server/index.ts index ef20674d..4ccc6fed 100644 --- a/server/index.ts +++ b/server/index.ts @@ -19,6 +19,7 @@ import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import clearCookies from '@server/middleware/clearcookies'; import routes from '@server/routes'; +import avatarproxy from '@server/routes/avatarproxy'; import imageproxy from '@server/routes/imageproxy'; import { getAppVersion } from '@server/utils/appVersion'; import restartFlag from '@server/utils/restartFlag'; @@ -202,6 +203,7 @@ app // Do not set cookies so CDNs can cache them server.use('/imageproxy', clearCookies, imageproxy); + server.use('/avatarproxy', clearCookies, avatarproxy); server.get('*', (req, res) => handle(req, res)); server.use( diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 1bf40cdb..579f1109 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -58,7 +58,7 @@ export interface CacheItem { export interface CacheResponse { apiCaches: CacheItem[]; - imageCache: Record<'tmdb', { size: number; imageCount: number }>; + imageCache: Record<'tmdb' | 'avatar', { size: number; imageCount: number }>; } export interface StatusResponse { diff --git a/server/job/schedule.ts b/server/job/schedule.ts index b358130c..a210988e 100644 --- a/server/job/schedule.ts +++ b/server/job/schedule.ts @@ -227,6 +227,9 @@ export const startJobs = (): void => { }); // Clean TMDB image cache ImageProxy.clearCache('tmdb'); + + // Clean users avatar image cache + ImageProxy.clearCache('avatar'); }), }); diff --git a/server/lib/imageproxy.ts b/server/lib/imageproxy.ts index 195e96b9..badfe94f 100644 --- a/server/lib/imageproxy.ts +++ b/server/lib/imageproxy.ts @@ -3,6 +3,7 @@ import type { RateLimitOptions } from '@server/utils/rateLimit'; import rateLimit from '@server/utils/rateLimit'; import { createHash } from 'crypto'; import { promises } from 'fs'; +import mime from 'mime/lite'; import path, { join } from 'path'; type ImageResponse = { @@ -11,7 +12,7 @@ type ImageResponse = { curRevalidate: number; isStale: boolean; etag: string; - extension: string; + extension: string | null; cacheKey: string; cacheMiss: boolean; }; @@ -27,29 +28,45 @@ class ImageProxy { let deletedImages = 0; const cacheDirectory = path.join(baseCacheDirectory, key); - const files = await promises.readdir(cacheDirectory); + try { + const files = await promises.readdir(cacheDirectory); - for (const file of files) { - const filePath = path.join(cacheDirectory, file); - const stat = await promises.lstat(filePath); + for (const file of files) { + const filePath = path.join(cacheDirectory, file); + const stat = await promises.lstat(filePath); - if (stat.isDirectory()) { - const imageFiles = await promises.readdir(filePath); + if (stat.isDirectory()) { + const imageFiles = await promises.readdir(filePath); - for (const imageFile of imageFiles) { - const [, expireAtSt] = imageFile.split('.'); - const expireAt = Number(expireAtSt); - const now = Date.now(); + for (const imageFile of imageFiles) { + const [, expireAtSt] = imageFile.split('.'); + const expireAt = Number(expireAtSt); + const now = Date.now(); - if (now > expireAt) { - await promises.rm(path.join(filePath, imageFile)); - deletedImages += 1; + if (now > expireAt) { + await promises.rm(path.join(filePath), { + recursive: true, + }); + deletedImages += 1; + } } } } + } catch (e) { + if (e.code === 'ENOENT') { + logger.error('Directory not found', { + label: 'Image Cache', + message: e.message, + }); + } else { + logger.error('Failed to read directory', { + label: 'Image Cache', + message: e.message, + }); + } } - logger.info(`Cleared ${deletedImages} stale image(s) from cache`, { + logger.info(`Cleared ${deletedImages} stale image(s) from cache '${key}'`, { label: 'Image Cache', }); } @@ -69,33 +86,49 @@ class ImageProxy { } private static async getDirectorySize(dir: string): Promise { - const files = await promises.readdir(dir, { - withFileTypes: true, - }); + try { + const files = await promises.readdir(dir, { + withFileTypes: true, + }); - const paths = files.map(async (file) => { - const path = join(dir, file.name); + const paths = files.map(async (file) => { + const path = join(dir, file.name); - if (file.isDirectory()) return await ImageProxy.getDirectorySize(path); + if (file.isDirectory()) return await ImageProxy.getDirectorySize(path); - if (file.isFile()) { - const { size } = await promises.stat(path); + if (file.isFile()) { + const { size } = await promises.stat(path); - return size; + return size; + } + + return 0; + }); + + return (await Promise.all(paths)) + .flat(Infinity) + .reduce((i, size) => i + size, 0); + } catch (e) { + if (e.code === 'ENOENT') { + return 0; } + } - return 0; - }); - - return (await Promise.all(paths)) - .flat(Infinity) - .reduce((i, size) => i + size, 0); + return 0; } private static async getImageCount(dir: string) { - const files = await promises.readdir(dir); + try { + const files = await promises.readdir(dir); - return files.length; + return files.length; + } catch (e) { + if (e.code === 'ENOENT') { + return 0; + } + } + + return 0; } private fetch: typeof fetch; @@ -147,6 +180,27 @@ class ImageProxy { return imageResponse; } + public async clearCachedImage(path: string) { + // find cacheKey + const cacheKey = this.getCacheKey(path); + + try { + const directory = join(this.getCacheDirectory(), cacheKey); + const files = await promises.readdir(directory); + + await promises.rm(directory, { recursive: true }); + + logger.info(`Cleared ${files[0]} from cache 'avatar'`, { + label: 'Image Cache', + }); + } catch (e) { + logger.error('Failed to clear cached image', { + label: 'Image Cache', + message: e.message, + }); + } + } + private async get(cacheKey: string): Promise { try { const directory = join(this.getCacheDirectory(), cacheKey); @@ -187,16 +241,25 @@ class ImageProxy { const directory = join(this.getCacheDirectory(), cacheKey); const href = this.baseUrl + - (this.baseUrl.endsWith('/') ? '' : '/') + + (this.baseUrl.length > 0 + ? this.baseUrl.endsWith('/') + ? '' + : '/' + : '') + (path.startsWith('/') ? path.slice(1) : path); const response = await this.fetch(href); const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); - const extension = path.split('.').pop() ?? ''; - const maxAge = Number( + const extension = mime.getExtension( + response.headers.get('content-type') ?? '' + ); + + let maxAge = Number( (response.headers.get('cache-control') ?? '0').split('=')[1] ); + + if (!maxAge) maxAge = 86400; const expireAt = Date.now() + maxAge * 1000; const etag = (response.headers.get('etag') ?? '').replace(/"/g, ''); @@ -232,7 +295,7 @@ class ImageProxy { private async writeToCacheDir( dir: string, - extension: string, + extension: string | null, maxAge: number, expireAt: number, buffer: Buffer, diff --git a/server/routes/auth.ts b/server/routes/auth.ts index cd931c25..4e7f7727 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -6,6 +6,7 @@ import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import { User } from '@server/entity/User'; import { startJobs } from '@server/job/schedule'; +import ImageProxy from '@server/lib/imageproxy'; import { Permission } from '@server/lib/permissions'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; @@ -342,6 +343,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { }), userType: UserType.EMBY, }); + break; case MediaServerType.JELLYFIN: settings.main.mediaServerType = MediaServerType.JELLYFIN; @@ -360,6 +362,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { }), userType: UserType.JELLYFIN, }); + break; default: throw new Error('select_server_type'); @@ -407,12 +410,24 @@ authRoutes.post('/jellyfin', async (req, res, next) => { ); // Update the users avatar with their jellyfin profile pic (incase it changed) if (account.User.PrimaryImageTag) { - user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; + const avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; + if (avatar !== user.avatar) { + const avatarProxy = new ImageProxy('avatar', ''); + avatarProxy.clearCachedImage(user.avatar); + } + user.avatar = avatar; } else { - user.avatar = gravatarUrl(user.email || account.User.Name, { + const avatar = gravatarUrl(user.email || account.User.Name, { default: 'mm', size: 200, }); + + if (avatar !== user.avatar) { + const avatarProxy = new ImageProxy('avatar', ''); + avatarProxy.clearCachedImage(user.avatar); + } + + user.avatar = avatar; } user.jellyfinUsername = account.User.Name; @@ -462,6 +477,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { ? UserType.JELLYFIN : UserType.EMBY, }); + //initialize Jellyfin/Emby users with local login const passedExplicitPassword = body.password && body.password.length > 0; if (passedExplicitPassword) { diff --git a/server/routes/avatarproxy.ts b/server/routes/avatarproxy.ts new file mode 100644 index 00000000..65638df2 --- /dev/null +++ b/server/routes/avatarproxy.ts @@ -0,0 +1,32 @@ +import ImageProxy from '@server/lib/imageproxy'; +import logger from '@server/logger'; +import { Router } from 'express'; + +const router = Router(); + +const avatarImageProxy = new ImageProxy('avatar', ''); +// Proxy avatar images +router.get('/*', async (req, res) => { + const imagePath = req.url.startsWith('/') ? req.url.slice(1) : req.url; + + try { + const imageData = await avatarImageProxy.getImage(imagePath); + + res.writeHead(200, { + 'Content-Type': `image/${imageData.meta.extension}`, + 'Content-Length': imageData.imageBuffer.length, + 'Cache-Control': `public, max-age=${imageData.meta.curRevalidate}`, + 'OS-Cache-Key': imageData.meta.cacheKey, + 'OS-Cache-Status': imageData.meta.cacheMiss ? 'MISS' : 'HIT', + }); + + res.end(imageData.imageBuffer); + } catch (e) { + logger.error('Failed to proxy avatar image', { + imagePath, + errorMessage: e.message, + }); + } +}); + +export default router; diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 30898d2a..30c854af 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -746,11 +746,13 @@ settingsRoutes.get('/cache', async (_req, res) => { })); const tmdbImageCache = await ImageProxy.getImageStats('tmdb'); + const avatarImageCache = await ImageProxy.getImageStats('avatar'); return res.status(200).json({ apiCaches, imageCache: { tmdb: tmdbImageCache, + avatar: avatarImageCache, }, }); }); diff --git a/src/components/Common/CachedImage/index.tsx b/src/components/Common/CachedImage/index.tsx index 6dfb8ee7..7c0d52c2 100644 --- a/src/components/Common/CachedImage/index.tsx +++ b/src/components/Common/CachedImage/index.tsx @@ -16,8 +16,11 @@ const CachedImage = ({ src, ...props }: ImageProps) => { if (typeof imageUrl === 'string' && imageUrl.startsWith('http')) { const parsedUrl = new URL(imageUrl); - if (parsedUrl.host === 'image.tmdb.org' && currentSettings.cacheImages) { - imageUrl = imageUrl.replace('https://image.tmdb.org', '/imageproxy'); + if (parsedUrl.host === 'image.tmdb.org') { + if (currentSettings.cacheImages) + imageUrl = imageUrl.replace('https://image.tmdb.org', '/imageproxy'); + } else if (parsedUrl.host !== 'gravatar.com') { + imageUrl = '/avatarproxy/' + imageUrl; } } diff --git a/src/components/IssueDetails/IssueComment/index.tsx b/src/components/IssueDetails/IssueComment/index.tsx index 0c36ca66..ab3f59ad 100644 --- a/src/components/IssueDetails/IssueComment/index.tsx +++ b/src/components/IssueDetails/IssueComment/index.tsx @@ -1,4 +1,5 @@ import Button from '@app/components/Common/Button'; +import CachedImage from '@app/components/Common/CachedImage'; import Modal from '@app/components/Common/Modal'; import { Permission, useUser } from '@app/hooks/useUser'; import defineMessages from '@app/utils/defineMessages'; @@ -6,7 +7,6 @@ import { Menu, Transition } from '@headlessui/react'; import { EllipsisVerticalIcon } from '@heroicons/react/24/solid'; import type { default as IssueCommentType } from '@server/entity/IssueComment'; import { Field, Form, Formik } from 'formik'; -import Image from 'next/image'; import Link from 'next/link'; import { Fragment, useState } from 'react'; import { FormattedRelativeTime, useIntl } from 'react-intl'; @@ -88,8 +88,8 @@ const IssueComment = ({ - { } className="group ml-1 inline-flex h-full items-center xl:ml-1.5" > - diff --git a/src/components/IssueList/IssueItem/index.tsx b/src/components/IssueList/IssueItem/index.tsx index 1b52be3e..c49a57d4 100644 --- a/src/components/IssueList/IssueItem/index.tsx +++ b/src/components/IssueList/IssueItem/index.tsx @@ -11,7 +11,6 @@ import { MediaType } from '@server/constants/media'; import type Issue from '@server/entity/Issue'; import type { MovieDetails } from '@server/models/Movie'; import type { TvDetails } from '@server/models/Tv'; -import Image from 'next/image'; import Link from 'next/link'; import { useInView } from 'react-intersection-observer'; import { FormattedRelativeTime, useIntl } from 'react-intl'; @@ -226,8 +225,8 @@ const IssueItem = ({ issue }: IssueItemProps) => { href={`/users/${issue.createdBy.id}`} className="group flex items-center truncate" > - { className="flex max-w-xs items-center rounded-full text-sm ring-1 ring-gray-700 hover:ring-gray-500 focus:outline-none focus:ring-gray-500" data-testid="user-menu" > - {
    - - {user.displayName} - {user.displayName} { className="group flex items-center" > - { className="group flex items-center" > - - - { className="group flex items-center truncate" > - { className="group flex items-center truncate" > - - - {appDataPath}/cache/images.', imagecachecount: 'Images Cached', imagecachesize: 'Total Cache Size', + usersavatars: "Users' Avatars", } ); @@ -573,6 +574,19 @@ const SettingsJobs = () => { {formatBytes(cacheData?.imageCache.tmdb.size ?? 0)} + + + {intl.formatMessage(messages.usersavatars)} (avatar) + + + {intl.formatNumber( + cacheData?.imageCache.avatar.imageCount ?? 0 + )} + + + {formatBytes(cacheData?.imageCache.avatar.size ?? 0)} + +
    diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx index 36dbe0aa..e95a0a7d 100644 --- a/src/components/UserList/JellyfinImportModal.tsx +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -1,11 +1,11 @@ import Alert from '@app/components/Common/Alert'; +import CachedImage from '@app/components/Common/CachedImage'; import Modal from '@app/components/Common/Modal'; import useSettings from '@app/hooks/useSettings'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { MediaServerType } from '@server/constants/server'; import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces'; -import Image from 'next/image'; import { useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; @@ -249,7 +249,7 @@ const JellyfinImportModal: React.FC = ({
    - { href={`/users/${user.id}`} className="h-10 w-10 flex-shrink-0" > - {
    - Date: Wed, 25 Sep 2024 21:25:44 +0200 Subject: [PATCH 42/53] fix(blacklist): add blacklist to mobile menu (#980) * fix(blacklist): add blacklist to mobile menu The "Blacklist" menu was only available in the desktop sidebar, not in the mobile menu. fix #979 * fix: export translations --- src/components/Layout/MobileMenu/index.tsx | 14 ++++++++++++++ src/i18n/locale/en.json | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Layout/MobileMenu/index.tsx b/src/components/Layout/MobileMenu/index.tsx index 4338810e..fe1e2e40 100644 --- a/src/components/Layout/MobileMenu/index.tsx +++ b/src/components/Layout/MobileMenu/index.tsx @@ -7,6 +7,7 @@ import { CogIcon, EllipsisHorizontalIcon, ExclamationTriangleIcon, + EyeSlashIcon, FilmIcon, SparklesIcon, TvIcon, @@ -16,6 +17,7 @@ import { ClockIcon as FilledClockIcon, CogIcon as FilledCogIcon, ExclamationTriangleIcon as FilledExclamationTriangleIcon, + EyeSlashIcon as FilledEyeSlashIcon, FilmIcon as FilledFilmIcon, SparklesIcon as FilledSparklesIcon, TvIcon as FilledTvIcon, @@ -84,6 +86,18 @@ const MobileMenu = () => { svgIconSelected: , activeRegExp: /^\/requests/, }, + { + href: '/blacklist', + content: intl.formatMessage(menuMessages.blacklist), + svgIcon: , + svgIconSelected: , + activeRegExp: /^\/blacklist/, + requiredPermission: [ + Permission.MANAGE_BLACKLIST, + Permission.VIEW_BLACKLIST, + ], + permissionType: 'or', + }, { href: '/issues', content: intl.formatMessage(menuMessages.issues), diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 689e2315..c86c5d3e 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1030,6 +1030,7 @@ "components.Settings.save": "Save Changes", "components.Settings.saving": "Saving…", "components.Settings.scan": "Sync Libraries", + "components.Settings.scanbackground": "Scanning will run in the background. You can continue the setup process in the meantime.", "components.Settings.scanning": "Syncing…", "components.Settings.serverLocal": "local", "components.Settings.serverRemote": "remote", @@ -1050,6 +1051,7 @@ "components.Settings.tautulliSettings": "Tautulli Settings", "components.Settings.tautulliSettingsDescription": "Optionally configure the settings for your Tautulli server. Jellyseerr fetches watch history data for your Plex media from Tautulli.", "components.Settings.timeout": "Timeout", + "components.Settings.tip": "Tip", "components.Settings.toastPlexConnecting": "Attempting to connect to Plex…", "components.Settings.toastPlexConnectingFailure": "Failed to connect to Plex.", "components.Settings.toastPlexConnectingSuccess": "Plex connection established successfully!", @@ -1079,16 +1081,14 @@ "components.Setup.continue": "Continue", "components.Setup.finish": "Finish Setup", "components.Setup.finishing": "Finishing…", - "components.Setup.scanbackground": "Scanning will run in the background. You can continue the setup process in the meantime.", "components.Setup.servertype": "Choose Server Type", "components.Setup.setup": "Setup", - "components.Setup.signin": "Sign In", + "components.Setup.signin": "Sign in to your account", "components.Setup.signinMessage": "Get started by signing in", "components.Setup.signinWithEmby": "Enter your Emby details", "components.Setup.signinWithJellyfin": "Enter your Jellyfin details", "components.Setup.signinWithPlex": "Enter your Plex details", "components.Setup.subtitle": "Get started by choosing your media server", - "components.Setup.tip": "Tip", "components.Setup.welcome": "Welcome to Jellyseerr", "components.StatusBadge.managemedia": "Manage {mediaType}", "components.StatusBadge.openinarr": "Open in {arr}", @@ -1233,6 +1233,7 @@ "components.UserProfile.UserSettings.UserGeneralSettings.saving": "Saving…", "components.UserProfile.UserSettings.UserGeneralSettings.seriesrequestlimit": "Series Request Limit", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailure": "Something went wrong while saving settings.", + "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsFailureEmail": "This email is already taken!", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!", "components.UserProfile.UserSettings.UserGeneralSettings.user": "User", "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "You must provide a valid Discord user ID", @@ -1312,6 +1313,7 @@ "components.UserProfile.seriesrequest": "Series Requests", "components.UserProfile.totalrequests": "Total Requests", "components.UserProfile.unlimited": "Unlimited", + "i18n.addToBlacklist": "Add to Blacklist", "i18n.advanced": "Advanced", "i18n.all": "All", "i18n.approve": "Approve", From a5d22ba5b83dd0e812b16f06476d993b5d59cb2a Mon Sep 17 00:00:00 2001 From: Thomas Loubiou Date: Mon, 30 Sep 2024 18:56:25 +0200 Subject: [PATCH 43/53] feat: allow request managers to delete data from sonarr/radarr (#644) * feat: allow requests managers to delete media files * fix(i18n): add missing translations * fix(i18n): remove french translation * refactor: use fetch API --- .../RequestList/RequestItem/index.tsx | 43 +++++++++++++++---- src/i18n/locale/en.json | 1 + 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 22a988cc..e989f2f6 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -42,6 +42,7 @@ const messages = defineMessages('components.RequestList.RequestItem', { tmdbid: 'TMDB ID', tvdbid: 'TheTVDB ID', unknowntitle: 'Unknown Title', + removearr: 'Remove from {arr}', profileName: 'Profile', }); @@ -341,6 +342,18 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { revalidateList(); }; + const deleteMediaFile = async () => { + if (request.media) { + await fetch(`/api/v1/media/${request.media.id}/file`, { + method: 'DELETE', + }); + await fetch(`/api/v1/media/${request.media.id}`, { + method: 'DELETE', + }); + revalidateList(); + } + }; + const retryRequest = async () => { setRetrying(true); @@ -666,14 +679,28 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { )} {requestData.status !== MediaRequestStatus.PENDING && hasPermission(Permission.MANAGE_REQUESTS) && ( - deleteRequest()} - confirmText={intl.formatMessage(globalMessages.areyousure)} - className="w-full" - > - - {intl.formatMessage(messages.deleterequest)} - + <> + deleteRequest()} + confirmText={intl.formatMessage(globalMessages.areyousure)} + className="w-full" + > + + {intl.formatMessage(messages.deleterequest)} + + deleteMediaFile()} + confirmText={intl.formatMessage(globalMessages.areyousure)} + className="w-full" + > + + + {intl.formatMessage(messages.removearr, { + arr: request.type === 'movie' ? 'Radarr' : 'Sonarr', + })} + + + )} {requestData.status === MediaRequestStatus.PENDING && hasPermission(Permission.MANAGE_REQUESTS) && ( diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index c86c5d3e..1642872b 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -298,6 +298,7 @@ "components.ManageSlideOver.plays": "{playCount, number} {playCount, plural, one {play} other {plays}}", "components.ManageSlideOver.removearr": "Remove from {arr}", "components.ManageSlideOver.removearr4k": "Remove from 4K {arr}", + "components.RequestList.RequestItem.removearr": "Remove from {arr}", "components.ManageSlideOver.tvshow": "series", "components.MediaSlider.ShowMoreCard.seemore": "See More", "components.MovieDetails.MovieCast.fullcast": "Full Cast", From 96e1d40304749ce00d2ff7359efc39a1d9724358 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Wed, 2 Oct 2024 20:59:35 +0200 Subject: [PATCH 44/53] fix(session): set the correct TTL for the cookie store (#992) The time-to-live (TTL) of cookies stored in the database was incorrect because the connect-typeorm library takes a TTL in seconds and not milliseconds, making cookies valid for ~82 years instead of 30 days. fix #991 --- server/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.ts b/server/index.ts index 4ccc6fed..96590361 100644 --- a/server/index.ts +++ b/server/index.ts @@ -175,7 +175,7 @@ app }, store: new TypeormStore({ cleanupLimit: 2, - ttl: 1000 * 60 * 60 * 24 * 30, + ttl: 60 * 60 * 24 * 30, }).connect(sessionRespository) as Store, }) ); From 92ba26207dcb1ddd696e0f01931d2609c521ae45 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 10 Oct 2024 11:37:08 +0200 Subject: [PATCH 45/53] feat: refresh monitored downloads before getting queue items (#994) Currently, we sync with sonarr/radarr with whatever value those return. Radarr/Sonarr syncs the activity from the download clients every few minutes. This leads to inaccurate estimated download times, because of the refresh delay with Jellyseerr and the *arrs. This PR fixes this by making a request to the *arrs to refresh the monitored downloads just before we get these downloads information. re #866 --- server/api/externalapi.ts | 6 +++--- server/api/servarr/base.ts | 27 ++++++++++++++++++++------- server/lib/downloadtracker.ts | 2 ++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 4f0ded02..0dfddefc 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -76,7 +76,7 @@ class ExternalAPI { } const data = await this.getDataFromResponse(response); - if (this.cache) { + if (this.cache && ttl !== 0) { this.cache.set(cacheKey, data, ttl ?? DEFAULT_TTL); } @@ -120,7 +120,7 @@ class ExternalAPI { } const resData = await this.getDataFromResponse(response); - if (this.cache) { + if (this.cache && ttl !== 0) { this.cache.set(cacheKey, resData, ttl ?? DEFAULT_TTL); } @@ -164,7 +164,7 @@ class ExternalAPI { } const resData = await this.getDataFromResponse(response); - if (this.cache) { + if (this.cache && ttl !== 0) { this.cache.set(cacheKey, resData, ttl ?? DEFAULT_TTL); } diff --git a/server/api/servarr/base.ts b/server/api/servarr/base.ts index ae024b6e..8b0d5ca0 100644 --- a/server/api/servarr/base.ts +++ b/server/api/servarr/base.ts @@ -157,9 +157,13 @@ class ServarrBase extends ExternalAPI { public getQueue = async (): Promise<(QueueItem & QueueItemAppendT)[]> => { try { - const data = await this.get>(`/queue`, { - includeEpisode: 'true', - }); + const data = await this.get>( + `/queue`, + { + includeEpisode: 'true', + }, + 0 + ); return data.records; } catch (e) { @@ -193,15 +197,24 @@ class ServarrBase extends ExternalAPI { } }; + async refreshMonitoredDownloads(): Promise { + await this.runCommand('RefreshMonitoredDownloads', {}); + } + protected async runCommand( commandName: string, options: Record ): Promise { try { - await this.post(`/command`, { - name: commandName, - ...options, - }); + await this.post( + `/command`, + { + name: commandName, + ...options, + }, + {}, + 0 + ); } catch (e) { throw new Error(`[${this.apiName}] Failed to run command: ${e.message}`); } diff --git a/server/lib/downloadtracker.ts b/server/lib/downloadtracker.ts index e948c580..b96bfca8 100644 --- a/server/lib/downloadtracker.ts +++ b/server/lib/downloadtracker.ts @@ -85,6 +85,7 @@ class DownloadTracker { }); try { + await radarr.refreshMonitoredDownloads(); const queueItems = await radarr.getQueue(); this.radarrServers[server.id] = queueItems.map((item) => ({ @@ -162,6 +163,7 @@ class DownloadTracker { }); try { + await sonarr.refreshMonitoredDownloads(); const queueItems = await sonarr.getQueue(); this.sonarrServers[server.id] = queueItems.map((item) => ({ From a0f80fe7647ef4a9025ca93407cd21ddc640fed1 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Wed, 16 Oct 2024 03:50:21 +0800 Subject: [PATCH 46/53] fix: use jellyfinMediaId4k for mediaUrl4k (#1006) Fixes the issue where mediaUrl4K was still using the non-4k mediaId despite having the correct 4k Id stored. fix #520 --- server/entity/Media.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 4f64178a..de10cebc 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -231,7 +231,7 @@ class Media { this.mediaUrl = `${jellyfinHost}/web/index.html#!/${pageName}?id=${this.jellyfinMediaId}&context=home&serverId=${serverId}`; } if (this.jellyfinMediaId4k) { - this.mediaUrl4k = `${jellyfinHost}/web/index.html#!/${pageName}?id=${this.jellyfinMediaId}&context=home&serverId=${serverId}`; + this.mediaUrl4k = `${jellyfinHost}/web/index.html#!/${pageName}?id=${this.jellyfinMediaId4k}&context=home&serverId=${serverId}`; } } } From 4945b5429848b36fc0ee41cf0277ed79f53d8286 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Thu, 17 Oct 2024 07:25:06 +0800 Subject: [PATCH 47/53] fix: fetch override to attach XSRF token to fix csrfProtection issue (#1014) During the migration from Axios to fetch, we overlooked the fact that Axios automatically handled CSRF tokens, while fetch does not. When CSRF protection was turned on, requests were failing with an "invalid CSRF token" error for users accessing the app even via HTTPS. This commit overrides fetch to ensure that the CSRF token is included in all requests. fix #1011 --- src/pages/_app.tsx | 1 + src/utils/fetchOverride.ts | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/utils/fetchOverride.ts diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ba0677c6..e5704052 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -12,6 +12,7 @@ import { SettingsProvider } from '@app/context/SettingsContext'; import { UserContext } from '@app/context/UserContext'; import type { User } from '@app/hooks/useUser'; import '@app/styles/globals.css'; +import '@app/utils/fetchOverride'; import { polyfillIntl } from '@app/utils/polyfillIntl'; import { MediaServerType } from '@server/constants/server'; import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces'; diff --git a/src/utils/fetchOverride.ts b/src/utils/fetchOverride.ts new file mode 100644 index 00000000..e0a90012 --- /dev/null +++ b/src/utils/fetchOverride.ts @@ -0,0 +1,46 @@ +const getCsrfToken = (): string | null => { + if (typeof window !== 'undefined') { + const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/); + return match ? decodeURIComponent(match[1]) : null; + } + return null; +}; + +const isSameOrigin = (url: RequestInfo | URL): boolean => { + const parsedUrl = new URL( + url instanceof Request ? url.url : url.toString(), + window.location.origin + ); + return parsedUrl.origin === window.location.origin; +}; + +// We are using a custom fetch implementation to add the X-XSRF-TOKEN heade +// to all requests. This is required when CSRF protection is enabled. +if (typeof window !== 'undefined') { + const originalFetch: typeof fetch = window.fetch; + + (window as typeof globalThis).fetch = async ( + input: RequestInfo | URL, + init?: RequestInit + ): Promise => { + if (!isSameOrigin(input)) { + return originalFetch(input, init); + } + + const csrfToken = getCsrfToken(); + + const headers = { + ...(init?.headers || {}), + ...(csrfToken ? { 'XSRF-TOKEN': csrfToken } : {}), + }; + + const newInit: RequestInit = { + ...init, + headers, + }; + + return originalFetch(input, newInit); + }; +} + +export {}; From 9de304d17a222c6b48294b0b83f606d378949a63 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 07:25:36 +0800 Subject: [PATCH 48/53] docs: add M0NsTeRRR as a contributor for security (#1015) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 91017932..3614dbd1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -439,6 +439,15 @@ "contributions": [ "code" ] + }, + { + "login": "M0NsTeRRR", + "name": "Ludovic Ortega", + "avatar_url": "https://avatars.githubusercontent.com/u/37785089?v=4", + "profile": "https://github.com/M0NsTeRRR", + "contributions": [ + "security" + ] } ] } diff --git a/README.md b/README.md index f91fe384..fb6c8790 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Translation status GitHub -All Contributors +All Contributors **Jellyseerr** is a free and open source software application for managing requests for your media library. @@ -146,6 +146,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon Baraa
    Baraa

    💻 Francisco Sales
    Francisco Sales

    💻 Oliver Laing
    Oliver Laing

    💻 + Ludovic Ortega
    Ludovic Ortega

    🛡️ From a351264b878b2660ae7a6415f26d38b52015c591 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 17 Oct 2024 12:37:19 +0200 Subject: [PATCH 49/53] fix: handle non-existent rottentomatoes rating (#1018) This fixes a bug where some media don't have any rottentomatoes ratings. --- server/api/rating/rottentomatoes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api/rating/rottentomatoes.ts b/server/api/rating/rottentomatoes.ts index e86c2488..f4fbe12b 100644 --- a/server/api/rating/rottentomatoes.ts +++ b/server/api/rating/rottentomatoes.ts @@ -182,7 +182,7 @@ class RottenTomatoes extends ExternalAPI { ); } - if (!tvshow) { + if (!tvshow || !tvshow.rottenTomatoes) { return null; } From 4e48fdf2cb9f76ae5c25073b585718650abd3288 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 17 Oct 2024 15:24:15 +0200 Subject: [PATCH 50/53] fix: rewrite avatarproxy and CachedImage (#1016) * fix: rewrite avatarproxy and CachedImage Avatar proxy was allowing every request to be proxied, no matter the original ressource's origin or filetype. This PR fixes it be allowing only relevant resources to be cached, i.e. Jellyfin/Emby images and TMDB images. fix #1012, #1013 * fix: resolve CodeQL error * fix: resolve CodeQL error * fix: resolve review comments * fix: resolve review comment * fix: resolve CodeQL error * fix: update imageproxy path --- server/routes/auth.ts | 15 +++------ server/routes/avatarproxy.ts | 23 +++++++++++-- server/routes/settings/index.ts | 7 +--- server/routes/user/index.ts | 8 +---- src/components/Blacklist/index.tsx | 3 ++ src/components/CollectionDetails/index.tsx | 2 ++ src/components/Common/CachedImage/index.tsx | 32 ++++++++++++------- src/components/Common/ImageFader/index.tsx | 1 + src/components/Common/Modal/index.tsx | 1 + src/components/CompanyCard/index.tsx | 1 + src/components/GenreCard/index.tsx | 1 + .../IssueDetails/IssueComment/index.tsx | 3 +- src/components/IssueDetails/index.tsx | 5 ++- src/components/IssueList/IssueItem/index.tsx | 5 ++- src/components/Layout/UserDropdown/index.tsx | 2 ++ src/components/ManageSlideOver/index.tsx | 2 ++ src/components/MovieDetails/index.tsx | 3 ++ src/components/PersonCard/index.tsx | 1 + src/components/PersonDetails/index.tsx | 1 + src/components/RequestCard/index.tsx | 4 +++ .../RequestList/RequestItem/index.tsx | 6 ++++ .../RequestModal/AdvancedRequester/index.tsx | 2 ++ .../RequestModal/CollectionRequestModal.tsx | 1 + src/components/Selector/index.tsx | 2 ++ src/components/TitleCard/index.tsx | 1 + src/components/TvDetails/index.tsx | 2 ++ .../UserList/JellyfinImportModal.tsx | 1 + src/components/UserList/index.tsx | 1 + .../UserProfile/ProfileHeader/index.tsx | 1 + 29 files changed, 97 insertions(+), 40 deletions(-) diff --git a/server/routes/auth.ts b/server/routes/auth.ts index 4e7f7727..560f04d5 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -262,8 +262,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { urlBase: body.urlBase, }); - const { externalHostname } = getSettings().jellyfin; - // Try to find deviceId that corresponds to jellyfin user, else generate a new one let user = await userRepository.findOne({ where: { jellyfinUsername: body.username }, @@ -281,11 +279,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => { // First we need to attempt to log the user in to jellyfin const jellyfinserver = new JellyfinAPI(hostname ?? '', undefined, deviceId); - const jellyfinHost = - externalHostname && externalHostname.length > 0 - ? externalHostname - : hostname; - const ip = req.ip; let clientIp; @@ -336,7 +329,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinAuthToken: account.AccessToken, permissions: Permission.ADMIN, avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` + ? `/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` : gravatarUrl(body.email || account.User.Name, { default: 'mm', size: 200, @@ -355,7 +348,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinAuthToken: account.AccessToken, permissions: Permission.ADMIN, avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` + ? `/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` : gravatarUrl(body.email || account.User.Name, { default: 'mm', size: 200, @@ -410,7 +403,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { ); // Update the users avatar with their jellyfin profile pic (incase it changed) if (account.User.PrimaryImageTag) { - const avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; + const avatar = `/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; if (avatar !== user.avatar) { const avatarProxy = new ImageProxy('avatar', ''); avatarProxy.clearCachedImage(user.avatar); @@ -467,7 +460,7 @@ authRoutes.post('/jellyfin', async (req, res, next) => { jellyfinDeviceId: deviceId, permissions: settings.main.defaultPermissions, avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` + ? `/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` : gravatarUrl(body.email || account.User.Name, { default: 'mm', size: 200, diff --git a/server/routes/avatarproxy.ts b/server/routes/avatarproxy.ts index 65638df2..e6f6f3b5 100644 --- a/server/routes/avatarproxy.ts +++ b/server/routes/avatarproxy.ts @@ -1,5 +1,8 @@ +import { MediaServerType } from '@server/constants/server'; import ImageProxy from '@server/lib/imageproxy'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; +import { getHostname } from '@server/utils/getHostname'; import { Router } from 'express'; const router = Router(); @@ -7,9 +10,25 @@ const router = Router(); const avatarImageProxy = new ImageProxy('avatar', ''); // Proxy avatar images router.get('/*', async (req, res) => { - const imagePath = req.url.startsWith('/') ? req.url.slice(1) : req.url; - + let imagePath = ''; try { + const jellyfinAvatar = req.url.match( + /(\/Users\/\w+\/Images\/Primary\/?\?tag=\w+&quality=90)$/ + )?.[1]; + if (!jellyfinAvatar) { + const mediaServerType = getSettings().main.mediaServerType; + throw new Error( + `Provided URL is not ${ + mediaServerType === MediaServerType.JELLYFIN + ? 'a Jellyfin' + : 'an Emby' + } avatar.` + ); + } + + const imageUrl = new URL(jellyfinAvatar, getHostname()); + imagePath = imageUrl.toString(); + const imageData = await avatarImageProxy.getImage(imagePath); res.writeHead(200, { diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 30c854af..3d6b6b0d 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -377,11 +377,6 @@ settingsRoutes.get('/jellyfin/library', async (req, res, next) => { settingsRoutes.get('/jellyfin/users', async (req, res) => { const settings = getSettings(); - const { externalHostname } = settings.jellyfin; - const jellyfinHost = - externalHostname && externalHostname.length > 0 - ? externalHostname - : getHostname(); const userRepository = getRepository(User); const admin = await userRepository.findOneOrFail({ @@ -401,7 +396,7 @@ settingsRoutes.get('/jellyfin/users', async (req, res) => { username: user.Name, id: user.Id, thumb: user.PrimaryImageTag - ? `${jellyfinHost}/Users/${user.Id}/Images/Primary/?tag=${user.PrimaryImageTag}&quality=90` + ? `/Users/${user.Id}/Images/Primary/?tag=${user.PrimaryImageTag}&quality=90` : gravatarUrl(user.Name, { default: 'mm', size: 200 }), email: user.Name, })); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index f8a0d41a..83ad0910 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -516,12 +516,6 @@ router.post( //const jellyfinUsersResponse = await jellyfinClient.getUsers(); const createdUsers: User[] = []; - const { externalHostname } = getSettings().jellyfin; - - const jellyfinHost = - externalHostname && externalHostname.length > 0 - ? externalHostname - : hostname; jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); const jellyfinUsers = await jellyfinClient.getUsers(); @@ -546,7 +540,7 @@ router.post( email: jellyfinUser?.Name, permissions: settings.main.defaultPermissions, avatar: jellyfinUser?.PrimaryImageTag - ? `${jellyfinHost}/Users/${jellyfinUser.Id}/Images/Primary/?tag=${jellyfinUser.PrimaryImageTag}&quality=90` + ? `/Users/${jellyfinUser.Id}/Images/Primary/?tag=${jellyfinUser.PrimaryImageTag}&quality=90` : gravatarUrl(jellyfinUser?.Name ?? '', { default: 'mm', size: 200, diff --git a/src/components/Blacklist/index.tsx b/src/components/Blacklist/index.tsx index 217f4cef..a752e95f 100644 --- a/src/components/Blacklist/index.tsx +++ b/src/components/Blacklist/index.tsx @@ -268,6 +268,7 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => { {title && title.backdropPath && (
    { className="relative h-auto w-12 flex-shrink-0 scale-100 transform-gpu overflow-hidden rounded-md transition duration-300 hover:scale-105" > { { {data.backdropPath && (
    {
    src; +export type CachedImageProps = ImageProps & { + src: string; + type: 'tmdb' | 'avatar'; +}; + /** * The CachedImage component should be used wherever * we want to offer the option to locally cache images. **/ -const CachedImage = ({ src, ...props }: ImageProps) => { +const CachedImage = ({ src, type, ...props }: CachedImageProps) => { const { currentSettings } = useSettings(); - let imageUrl = src; + let imageUrl: string; - if (typeof imageUrl === 'string' && imageUrl.startsWith('http')) { - const parsedUrl = new URL(imageUrl); - - if (parsedUrl.host === 'image.tmdb.org') { - if (currentSettings.cacheImages) - imageUrl = imageUrl.replace('https://image.tmdb.org', '/imageproxy'); - } else if (parsedUrl.host !== 'gravatar.com') { - imageUrl = '/avatarproxy/' + imageUrl; - } + if (type === 'tmdb') { + // tmdb stuff + imageUrl = + currentSettings.cacheImages && !src.startsWith('/') + ? src.replace(/^https:\/\/image\.tmdb\.org\//, '/imageproxy/') + : src; + } else if (type === 'avatar') { + // jellyfin avatar (in any) + const jellyfinAvatar = src.match( + /(\/Users\/\w+\/Images\/Primary\/?\?tag=\w+&quality=90)$/ + )?.[1]; + imageUrl = jellyfinAvatar ? `/avatarproxy` + jellyfinAvatar : src; + } else { + return null; } return ; diff --git a/src/components/Common/ImageFader/index.tsx b/src/components/Common/ImageFader/index.tsx index 20ccb698..930471e9 100644 --- a/src/components/Common/ImageFader/index.tsx +++ b/src/components/Common/ImageFader/index.tsx @@ -61,6 +61,7 @@ const ImageFader: ForwardRefRenderFunction = ( {...props} > ( {backdrop && (
    { >
    { tabIndex={0} > { {data.backdropPath && (
    {
    { className="group ml-1 inline-flex h-full items-center xl:ml-1.5" > { {title.backdropPath && (
    { className="relative h-auto w-12 flex-shrink-0 scale-100 transform-gpu overflow-hidden rounded-md transition duration-300 hover:scale-105" > { className="group flex items-center truncate" > { data-testid="user-menu" > {
    { {data.backdropPath && (
    {
    {
    { {data.profilePath && (
    { > { {title.backdropPath && (
    { > { className="w-20 flex-shrink-0 scale-100 transform-gpu cursor-pointer overflow-hidden rounded-md shadow-sm transition duration-300 hover:scale-105 hover:shadow-md sm:w-28" > { {title.backdropPath && (
    { className="relative h-auto w-12 flex-shrink-0 scale-100 transform-gpu overflow-hidden rounded-md transition duration-300 hover:scale-105" > { > { >
    { {data.backdropPath && (
    {
    = ({
    { className="h-10 w-10 flex-shrink-0" > {
    Date: Thu, 17 Oct 2024 23:12:41 +0800 Subject: [PATCH 51/53] docs(buildfromsource): remove latest/develop tabs and update instructions to support 2.0.0 (#1021) re #1020 --- docs/getting-started/buildfromsource.mdx | 69 +----------------------- 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/docs/getting-started/buildfromsource.mdx b/docs/getting-started/buildfromsource.mdx index 5b39912c..c22ff23a 100644 --- a/docs/getting-started/buildfromsource.mdx +++ b/docs/getting-started/buildfromsource.mdx @@ -12,49 +12,12 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ### Prerequisites - - - - [Node.js 18.x](https://nodejs.org/en/download/) - - [Yarn 1.x](https://classic.yarnpkg.com/lang/en/docs/install) - - [Git](https://git-scm.com/downloads) - - - - [Node.js 20.x](https://nodejs.org/en/download/) - [Pnpm 9.x](https://pnpm.io/installation) - [Git](https://git-scm.com/downloads) - - - ## Unix (Linux, macOS) ### Installation - - -1. Assuming you want the working directory to be `/opt/jellyseerr`, create the directory and navigate to it: -```bash -sudo mkdir -p /opt/jellyseerr && cd /opt/jellyseerr -``` -2. Clone the Jellyseerr repository and checkout the latest release: -```bash -git clone https://github.com/Fallenbagel/jellyseerr.git -cd jellyseerr -git checkout main -``` -3. Install the dependencies: -```bash -CYPRESS_INSTALL_BINARY=0 yarn install --frozen-lockfile --network-timeout 1000000 -``` -4. Build the project: -```bash -yarn build -``` -5. Start Jellyseerr: -```bash -yarn start -``` - - 1. Assuming you want the working directory to be `/opt/jellyseerr`, create the directory and navigate to it: ```bash sudo mkdir -p /opt/jellyseerr && cd /opt/jellyseerr @@ -77,8 +40,6 @@ pnpm build ```bash pnpm start ``` - - :::info You can now access Jellyseerr by visiting `http://localhost:5055` in your web browser. @@ -234,33 +195,6 @@ pm2 status jellyseerr ## Windows ### Installation - - -1. Assuming you want the working directory to be `C:\jellyseerr`, create the directory and navigate to it: -```powershell -mkdir C:\jellyseerr -cd C:\jellyseerr -``` -2. Clone the Jellyseerr repository and checkout the latest release: -```powershell -git clone https://github.com/Fallenbagel/jellyseerr.git . -git checkout main -``` -3. Install the dependencies: -```powershell -npm install -g win-node-env -set CYPRESS_INSTALL_BINARY=0 && yarn install --frozen-lockfile --network-timeout 1000000 -``` -4. Build the project: -```powershell -yarn build -``` -5. Start Jellyseerr: -```powershell -yarn start -``` - - 1. Assuming you want the working directory to be `C:\jellyseerr`, create the directory and navigate to it: ```powershell mkdir C:\jellyseerr @@ -284,8 +218,6 @@ pnpm build ```powershell pnpm start ``` - - :::tip You can add the environment variables to a `.env` file in the Jellyseerr directory. @@ -313,6 +245,7 @@ node dist/index.js - Set the trigger to "When the computer starts" - Set the action to "Start a program" - Set the program/script to the path of the `start-jellyseerr.bat` file +- Set the "Start in" to the jellyseerr directory. - Click "Finish" Now, Jellyseerr will start when the computer boots up in the background. From cbb1a74526ef5c003b7081c31146c52e7e551d60 Mon Sep 17 00:00:00 2001 From: Fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Fri, 18 Oct 2024 06:28:42 +0800 Subject: [PATCH 52/53] fix: fixes wrong avatar rendered for the modifiedBy user in request list (#1028) This fixes an issue where when the request is modified it was showing the avatar of the requester instead of the modifiedBy user fix #1017 --- src/components/RequestList/RequestItem/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index b1454790..80ba2ab7 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -635,7 +635,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { Date: Fri, 18 Oct 2024 12:24:29 +0200 Subject: [PATCH 53/53] feat: exit Jellyseerr when migration fails (#1026) --- server/lib/settings/index.ts | 2 +- server/lib/settings/migrator.ts | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 074a4fcd..0fc47af9 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -648,7 +648,7 @@ class Settings { if (data) { const parsedJson = JSON.parse(data); - this.data = await runMigrations(parsedJson); + this.data = await runMigrations(parsedJson, SETTINGS_PATH); this.data = merge(this.data, parsedJson); diff --git a/server/lib/settings/migrator.ts b/server/lib/settings/migrator.ts index 856016e1..002e5516 100644 --- a/server/lib/settings/migrator.ts +++ b/server/lib/settings/migrator.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import type { AllSettings } from '@server/lib/settings'; import logger from '@server/logger'; import fs from 'fs'; @@ -6,7 +7,8 @@ import path from 'path'; const migrationsDir = path.join(__dirname, 'migrations'); export const runMigrations = async ( - settings: AllSettings + settings: AllSettings, + SETTINGS_PATH: string ): Promise => { const migrations = fs .readdirSync(migrationsDir) @@ -17,14 +19,43 @@ export const runMigrations = async ( let migrated = settings; try { + const settingsBefore = JSON.stringify(migrated); + for (const migration of migrations) { migrated = await migration(migrated); } + + const settingsAfter = JSON.stringify(migrated); + + if (settingsBefore !== settingsAfter) { + // a migration occured + // we check that the new config will be saved + fs.writeFileSync(SETTINGS_PATH, JSON.stringify(migrated, undefined, ' ')); + const fileSaved = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf-8')); + if (JSON.stringify(fileSaved) !== settingsAfter) { + // something went wrong while saving file + throw new Error('Unable to save settings after migration.'); + } + } } catch (e) { logger.error( `Something went wrong while running settings migrations: ${e.message}`, { label: 'Settings Migrator' } ); + // we stop jellyseerr if the migration failed + console.log( + '====================================================================' + ); + console.log( + ' SOMETHING WENT WRONG WHILE RUNNING SETTINGS MIGRATIONS ' + ); + console.log( + ' Please check that your configuration folder is properly set up ' + ); + console.log( + '====================================================================' + ); + process.exit(); } return migrated;