Compare commits

..

No commits in common. "fbe1d1eed6fbd95168c73d0913b9cb3bb298e6c5" and "e6d49a2d53c90350a31cd418302a5c22d279f9b5" have entirely different histories.

20 changed files with 275 additions and 1944 deletions

File diff suppressed because it is too large Load diff

View file

@ -385,7 +385,7 @@
"LabelFontBoldness": "Výraznost písma",
"LabelFontFamily": "Rodina písem",
"LabelFontItalic": "Kurzíva",
"LabelFontScale": "Velikost písma",
"LabelFontScale": "Měřítko písma",
"LabelFontStrikethrough": "Přeškrtnutí",
"LabelFormat": "Formát",
"LabelFull": "Plné",

View file

@ -116,7 +116,7 @@
"ButtonViewAll": "Alles anzeigen",
"ButtonYes": "Ja",
"ErrorUploadFetchMetadataAPI": "Fehler beim Abrufen der Metadaten",
"ErrorUploadFetchMetadataNoResults": "Metadaten konnten nicht abgerufen werden. Versuche, den Titel und/oder den Autor zu aktualisieren.",
"ErrorUploadFetchMetadataNoResults": "Metadaten konnten nicht abgerufen werden. Versuche den Titel und/oder den Autor zu aktualisieren.",
"ErrorUploadLacksTitle": "Es muss ein Titel eingegeben werden",
"HeaderAccount": "Konto",
"HeaderAddCustomMetadataProvider": "Benutzerdefinierten Metadatenanbieter hinzufügen",
@ -710,7 +710,7 @@
"LabelUploaderDragAndDropFilesOnly": "Dateien per Drag & Drop hierher ziehen",
"LabelUploaderDropFiles": "Dateien löschen",
"LabelUploaderItemFetchMetadataHelp": "Automatisches Aktualisieren von Titel, Autor und Serie",
"LabelUseAdvancedOptions": "Erweiterte Optionen verwenden",
"LabelUseAdvancedOptions": "Nutze Erweiterte Optionen",
"LabelUseChapterTrack": "Kapiteldatei verwenden",
"LabelUseFullTrack": "Gesamte Datei verwenden",
"LabelUseZeroForUnlimited": "0 für unbegrenzt",
@ -737,7 +737,7 @@
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, musst du eine Instanz von <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. <br />Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter <code>http://192.168.1.1:8337</code> läuft, würdest du <code>http://192.168.1.1:8337/notify</code> eingeben.",
"MessageAsinCheck": "Stelle sicher, dass die ASIN aus der richtigen Audible Region verwendet wird, nicht Amazon.",
"MessageAuthenticationLegacyTokenWarning": "Alte API-Token werden in Zukunft entfernt. Benutze stattdessen <a href=\"/config/api-keys\">API Keys</a>.",
"MessageAuthenticationLegacyTokenWarning": "Alte API tokens werden in Zukunft entfernt. Benutze stattdessen <a href=\"/config/api-keys\">API Keys</a>.",
"MessageAuthenticationOIDCChangesRestart": "Nach dem Speichern muss der Server neugestartet werden um die OIDC Änderungen zu übernehmen.",
"MessageAuthenticationSecurityMessage": "Die Anmeldung wurde abgesichert. Benutzersitzungen werden getrennt, alle Benutzer müssen sich erneut anmelden.",
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in <code>/metadata/items</code> & <code>/metadata/authors</code> gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
@ -1103,11 +1103,11 @@
"ToastPodcastCreateFailed": "Podcast konnte nicht erstellt werden",
"ToastPodcastCreateSuccess": "Podcast erstellt",
"ToastPodcastEpisodeUpdated": "Podcast-Folge aktualisiert",
"ToastPodcastGetFeedFailed": "Fehler beim abrufen des Podcast-Feeds",
"ToastPodcastGetFeedFailed": "Fehler beim abrufen des Podcast Feeds",
"ToastPodcastNoEpisodesInFeed": "Keine Episoden in RSS Feed gefunden",
"ToastPodcastNoRssFeed": "Podcast enthält keinen RSS Feed",
"ToastProgressIsNotBeingSynced": "Fortschritt wird nicht synchronisiert, Wiedergabe wird neu gestartet",
"ToastProviderCreatedFailed": "Fehler beim Hinzufügen des Anbieters",
"ToastProviderCreatedFailed": "Fehler beim hinzufügen des Anbieters",
"ToastProviderCreatedSuccess": "Neuer Anbieter hinzugefügt",
"ToastProviderNameAndUrlRequired": "Name und URL notwendig",
"ToastProviderRemoveSuccess": "Anbieter entfernt",

View file

@ -100,11 +100,9 @@
"ButtonShiftTimes": "Χρόνοι Μετακίνησης",
"ButtonShow": "Εμφάνιση",
"ButtonStartM4BEncode": "Έναρξη Κωδικοποίησης M4B",
"ButtonStartMetadataEmbed": "Έναρξη Ενσωμάτωσης Μεταδεδομένων",
"ButtonStats": "Στατιστικά",
"ButtonSubmit": "Υποβολή",
"ButtonTest": "Δοκιμή",
"ButtonUnlinkOpenId": "Αποσύνδεση OpenID",
"ButtonUpload": "Μεταφόρτωση",
"ButtonUploadBackup": "Μεταφόρτωση Αντιγράφου Ασφαλείας",
"ButtonUploadCover": "Μεταφόρτωση Εξωφύλλου",
@ -113,17 +111,11 @@
"ButtonUserEdit": "Επεξεργασίας χρήστη {0}",
"ButtonViewAll": "Εμφάνιση Όλων",
"ButtonYes": "Ναι",
"ErrorUploadFetchMetadataAPI": "Σφάλμα κατά την ανάκτηση μεταδεδομένων",
"ErrorUploadFetchMetadataNoResults": "Δεν ήταν δυνατή η ανάκτηση των μεταδεδομένων - δοκιμάστε να ενημερώσετε τον τίτλο και/ή τον συγγραφέα",
"ErrorUploadLacksTitle": "Πρέπει να έχει τίτλο",
"HeaderAccount": "Λογαριασμός",
"HeaderAddCustomMetadataProvider": "Προσθήκη Προσαρμοσμένου Παρόχου Μεταδεδομένων",
"HeaderAdvanced": "Για Προχωρημένους",
"HeaderApiKeys": "Κλειδιά API",
"HeaderAppriseNotificationSettings": "Ρυθμίσεις Ειδοποιήσεων Apprise",
"HeaderAudioTracks": "Κομμάτια Ήχου",
"HeaderAudiobookTools": "Εργαλεία Διαχείρισης Αρχείων Audiobooks",
"HeaderAuthentication": "Αυθεντικοποίηση",
"HeaderBackups": "Αντίγραφα Ασφαλείας",
"HeaderBulkChapterModal": "Προσθήκη Πολλαπλών Κεφαλαίων",
"HeaderChangePassword": "Αλλαγή Κωδικού Πρόσβασης",
@ -224,7 +216,6 @@
"LabelChapters": "Κεφάλαια",
"LabelChaptersFound": "κεφάλαια βρέθηκαν",
"LabelClosePlayer": "Κλείσιμο αναπαραγωγής",
"LabelCollapseSeries": "Σύμπτυξη Σειράς",
"LabelCollection": "Συλλογή",
"LabelCollections": "Συλλογές",
"LabelComplete": "Ολοκλήρωση",

View file

@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Br {0}",
"LabelLibraryItem": "Stavka knjižnice",
"LabelLibraryName": "Ime knjižnice",
"LabelLibrarySortByProgress": "Napredak: Zadnje ažuriranje",
"LabelLibrarySortByProgressFinished": "Napredak: Završeno",
"LabelLibrarySortByProgressStarted": "Napredak: Započeto",
"LabelLibrarySortByProgress": "Napredak: zadnje ažurirano",
"LabelLibrarySortByProgressFinished": "Napredak: završeno",
"LabelLibrarySortByProgressStarted": "Napredak: započeto",
"LabelLimit": "Ograničenje",
"LabelLineSpacing": "Razmak između redaka",
"LabelListenAgain": "Ponovno poslušaj",

View file

@ -205,7 +205,7 @@
"HeaderSleepTimer": "Alvásidőzítő",
"HeaderStatsLargestItems": "Legnagyobb elemek",
"HeaderStatsLongestItems": "Leghosszabb elemek (órában)",
"HeaderStatsMinutesListeningChart": "Hallgatási grafikon percben (az elmúlt 7 napból)",
"HeaderStatsMinutesListeningChart": "Hallgatási grafikon percekben (az elmúlt 7 napból)",
"HeaderStatsRecentSessions": "Legutóbbi munkamenetek",
"HeaderStatsTop10Authors": "Top 10 szerző",
"HeaderStatsTop5Genres": "Top 5 műfaj",
@ -643,8 +643,8 @@
"LabelStatsAuthors": "Szerző",
"LabelStatsBestDay": "Legjobb nap",
"LabelStatsDailyAverage": "Napi átlag",
"LabelStatsDays": "Nap",
"LabelStatsDaysListened": "Hallgatással töltött nap",
"LabelStatsDays": "Napok",
"LabelStatsDaysListened": "Hallgatással töltött napok",
"LabelStatsHours": "Órák",
"LabelStatsInARow": "egymás után",
"LabelStatsItemsFinished": "Befejezett elem",

View file

@ -214,9 +214,7 @@
"LabelEnd": "終了",
"LabelEndOfChapter": "チャプターの最後",
"LabelEpisode": "エピソード",
"LabelEpisodes": "エピソード",
"LabelEpisodic": "エピソード",
"LabelExplicit": "露骨な表現",
"LabelExplicit": "",
"LabelFeedURL": "Feed URL",
"LabelFile": "ファイル",
"LabelFileBirthtime": "ファイル作成日時",

View file

@ -34,7 +34,7 @@
"ButtonEditChapters": "Rediger kapittel",
"ButtonEditPodcast": "Rediger podcast",
"ButtonEnable": "Aktiver",
"ButtonFireAndFail": "Utfør og feil",
"ButtonFireAndFail": "Kjør ved feil",
"ButtonFireOnTest": "Kjør onTest-kommando",
"ButtonForceReScan": "Tving skann",
"ButtonFullPath": "Full sti",
@ -113,7 +113,7 @@
"ButtonUploadOPMLFile": "Last opp OPML fil",
"ButtonUserDelete": "Slett bruker {0}",
"ButtonUserEdit": "Rediger bruker {0}",
"ButtonViewAll": "Vis alt",
"ButtonViewAll": "Vis alle",
"ButtonYes": "Ja",
"ErrorUploadFetchMetadataAPI": "Feil ved innhenting av metadata",
"ErrorUploadFetchMetadataNoResults": "Kunne ikke hente metadata - forsøk å oppdatere tittel og/eller forfatter",
@ -309,7 +309,6 @@
"LabelDeleteFromFileSystemCheckbox": "Slett fra filsystemet (fjern haken for kun å ta bort fra databasen)",
"LabelDescription": "Beskrivelse",
"LabelDeselectAll": "Fjern valg",
"LabelDetectedPattern": "Oppdaget mønster:",
"LabelDevice": "Enhet",
"LabelDeviceInfo": "Enhetsinformasjon",
"LabelDeviceIsAvailableTo": "Enheten er tilgjengelig for...",
@ -378,12 +377,11 @@
"LabelFilterByUser": "Filtrer etter bruker",
"LabelFindEpisodes": "Finn episoder",
"LabelFinished": "Fullført",
"LabelFinishedDate": "Fullført {0}",
"LabelFolder": "Mappe",
"LabelFolders": "Mapper",
"LabelFontBold": "Fet",
"LabelFontBoldness": "Skrifttykkelse",
"LabelFontFamily": "Skriftfamilie",
"LabelFontFamily": "Fontfamilie",
"LabelFontItalic": "Kursiv",
"LabelFontScale": "Font størrelse",
"LabelFontStrikethrough": "Gjennomstreking",
@ -436,9 +434,7 @@
"LabelLibraryFilterSublistEmpty": "Ingen {0}",
"LabelLibraryItem": "Bibliotek enhet",
"LabelLibraryName": "Bibliotek navn",
"LabelLibrarySortByProgress": "Fremdrift: Sist oppdatert",
"LabelLibrarySortByProgressFinished": "Fremdrift: Fullført",
"LabelLibrarySortByProgressStarted": "Fremdrift: Startet",
"LabelLibrarySortByProgress": "Fremgang: Sist oppdatert",
"LabelLimit": "Begrensning",
"LabelLineSpacing": "Linjemellomrom",
"LabelListenAgain": "Lytt igjen",
@ -447,9 +443,8 @@
"LabelLogLevelWarn": "Varsel",
"LabelLookForNewEpisodesAfterDate": "Se etter nye episoder etter denne datoen",
"LabelLowestPriority": "Laveste prioritet",
"LabelMatchConfidence": "Konfidens",
"LabelMatchExistingUsersBy": "Knytt sammen eksisterende brukere basert på",
"LabelMatchExistingUsersByDescription": "Brukes for å koble til eksisterende brukere. Når koblingen er i orden vil brukerne bli identifisert med en unik id fra SSO-tilbyderen",
"LabelMatchExistingUsersByDescription": "Brukes for å koble til eksisterende brukere. Når koblingen er i orden vil brukerne bli identifisert med en unik id fra SSO-tilbyderen.",
"LabelMaxEpisodesToDownload": "Maksimalt antall episoder som skal lastes ned. Bruk 0 for ubegrenset.",
"LabelMaxEpisodesToDownloadPerCheck": "Maksimalt antall nye episoder som skal lastes ned per sjekk",
"LabelMaxEpisodesToKeep": "Maksimalt antall episoder som skal beholdes",
@ -458,7 +453,7 @@
"LabelMediaType": "Medie type",
"LabelMetaTag": "Meta tag",
"LabelMetaTags": "Meta tags",
"LabelMetadataOrderOfPrecedenceDescription": "Høyere prioritert kilder for metadata overstyrer laverer prioriterte kilder for metadata",
"LabelMetadataOrderOfPrecedenceDescription": "Høyere prioritert kilder for metadata overstyrer laverer prioriterte kilder for metadata.",
"LabelMetadataProvider": "Metadata Leverandør",
"LabelMinute": "Minutt",
"LabelMinutes": "Minutter",
@ -477,9 +472,7 @@
"LabelNewestAuthors": "Nyeste forfattere",
"LabelNewestEpisodes": "Nyeste episoder",
"LabelNextBackupDate": "Neste sikkerhetskopi dato",
"LabelNextChapters": "Neste kapitler blir:",
"LabelNextScheduledRun": "Neste planlagte kjøring",
"LabelNoApiKeys": "Ingen API-nøkler",
"LabelNoCustomMetadataProviders": "Ingen egendefinerte tilbydere for metadata",
"LabelNoEpisodesSelected": "Ingen episoder valgt",
"LabelNotFinished": "Ikke fullført",
@ -495,11 +488,10 @@
"LabelNotificationsMaxQueueSize": "Maksimalt antall varslinger i kø",
"LabelNotificationsMaxQueueSizeHelp": "Hendelser er begrenset til avfyre én gang per sekund. Hendelser blir ignorert om køen er full. Dette forhindrer overflod av varslinger.",
"LabelNumberOfBooks": "Antall bøker",
"LabelNumberOfChapters": "Antall kapitler:",
"LabelNumberOfEpisodes": "# episoder",
"LabelOpenIDAdvancedPermsClaimDescription": "Navnet på OpenID claim'et som inneholder avanserte tilganger for brukerhandlinger i applikasjonen som vil brukes for ikke-administratorroller (<b>hvis konfigurert</b>). Hvis claim'et mangler fra responsen, nektes tilgang til ABS. Hvis en enkelt opsjon mangler, blir behandlet som <code>false</code>. Påse at identitetstilbyderens claim stemmer overens med den forventede strukturen:",
"LabelOpenIDClaims": "La følge valg være tomme for å slå av avanserte gruppe og tillatelser. Gruppen \"Bruker\" vil da også automatisk legges til.",
"LabelOpenIDGroupClaimDescription": "Navn på OpenID-forespørsel som inneholder en lite over brukerens grupper. Vanligvis kalt <code>grupper</code>. <b>Om konfigurert</b>, vil applikasjonen tildele roller baseret på brukerens gruppemedlemsskaper, gitt disse grupper er navngitt (uten forbehold for store og små bokstaver) 'admin', 'user' eller 'guest' i forespørsel. Forespørselen burde inneholde en liste (og hvis brukeren tilhører flere grupper), applikasjonen vil tildele rolle med høyeste adgangsnivå. Hvis ingen grupper matcher vil adgang bli nektet.",
"LabelOpenIDGroupClaimDescription": "Navn på OpenID-forespørsel som inneholder en lite over brukerens grupper. Vanligvis kalt <code>grupper</code>. <b>Om konfigurert</b>, vil applikasjonen tildele roller baseret på brukerens gruppemedlemsskaper, gitt disse grupper er navngitt (uten forbehold for store og små bokstaver) 'admin', 'user' eller 'guest' i forespørsel. Forespørselen burde inneholde en liste (og hvis brukeren tilhører flere grupper), applikasjonen vil tildele rolle med høyeste adgangsnivå. Hvis ingen grupper matcher vil adgang bli nektet.",
"LabelOpenRSSFeed": "Åpne RSS Feed",
"LabelOverwrite": "Overskriv",
"LabelPaginationPageXOfY": "Side {0} av {1}",
@ -517,7 +509,6 @@
"LabelPersonalYearReview": "Oppsummering av året ditt ({0})",
"LabelPhotoPathURL": "Bilde sti/URL",
"LabelPlayMethod": "Avspillingsmetode",
"LabelPlaybackRateIncrementDecrement": "Trinnstørrelse for økning/senking av avspillingshastighet",
"LabelPlayerChapterNumberMarker": "{0} av {1}",
"LabelPlaylists": "Spilleliste",
"LabelPodcast": "Podcast",
@ -570,7 +561,6 @@
"LabelSelectAll": "Velg alt",
"LabelSelectAllEpisodes": "Velg alle episoder",
"LabelSelectEpisodesShowing": "Velg {0} episoder vist",
"LabelSelectUser": "Velg bruker",
"LabelSelectUsers": "Velg brukere",
"LabelSendEbookToDevice": "Send Ebok til...",
"LabelSequence": "Sekvens",
@ -638,7 +628,6 @@
"LabelStartTime": "Start Tid",
"LabelStarted": "Startet",
"LabelStartedAt": "Startet",
"LabelStartedDate": "Startet {0}",
"LabelStatsAudioTracks": "Lydspor",
"LabelStatsAuthors": "Forfattere",
"LabelStatsBestDay": "Beste dag",
@ -668,7 +657,6 @@
"LabelTheme": "Tema",
"LabelThemeDark": "Mørk",
"LabelThemeLight": "Lys",
"LabelThemeSepia": "Sepia",
"LabelTimeBase": "Tidsbase",
"LabelTimeDurationXHours": "{0} timer",
"LabelTimeDurationXMinutes": "{0} minutter",
@ -737,32 +725,24 @@
"MessageAddToPlayerQueue": "Legg til i kø",
"MessageAppriseDescription": "For å bruke denne funksjonen trenger du en instans av <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> kjørende eller et API som håndterer disse forespørslene. <br />Apprise API URL skal være hele URL-en til varslingen, f.eks., hvis din API-instans er på <code>http://192.168.1.1:8337</code> så skal du bruke <code>http://192.168.1.1:8337/notify</code>.",
"MessageAsinCheck": "Påse at du bruker ASIN fra den riktige Audible-regionen, ikke Amazon.",
"MessageAuthenticationLegacyTokenWarning": "Eldre API-tokener vil bli fjernet i fremtiden. Bruk <a href=\"/config/api-keys\">API-nøkler</a> i stedet.",
"MessageAuthenticationOIDCChangesRestart": "Etter å ha lagret, start serveren din på nytt for at OIDC-endringene skal tre i kraft.",
"MessageAuthenticationSecurityMessage": "Autentisering er forbedret av sikkerhetshensyn. Alle brukere må logge inn på nytt.",
"MessageBackupsDescription": "Sikkerhetskopier inkluderer, brukerfremgang, detaljer om bibliotekgjenstander, tjener instillinger og bilder lagret under <code>/metadata/items</code> og <code>/metadata/authors</code>. Sikkerhetskopier <strong>vil ikke</strong> inkludere filer som er lagret i bibliotek mappene.",
"MessageBackupsLocationEditNote": "Viktig: Endring av mappen for sikkerhetskopi hverken endrer eller flytter eksisterende sikkerhetskopier!",
"MessageBackupsLocationNoEditNote": "Viktig: Mappen for sikkerhetskopi satt i en miljøvariabel og kan ikke endres her.",
"MessageBackupsLocationNoEditNote": "NB: Mappen for sikkerhetskopi settes i en miljøvariabel og kan ikke endres her.",
"MessageBackupsLocationPathEmpty": "Mappen for sikkerhetskopiering må angis",
"MessageBatchEditPopulateMapDetailsAllHelp": "Fyll aktiverte felt med data fra alle elementer. Felt med flere verdier blir slått sammen",
"MessageBatchEditPopulateMapDetailsItemHelp": "Fyll aktiverte kartdetaljfelt med data fra dette elementet",
"MessageBatchQuickMatchDescription": "Kjapt søk vil forsøke å legge til manglende omslag og metadata for de valgte gjenstandene. Aktiver dette valget for å tillate Kjapt søk til å overskrive eksisterende omslag og/eller metadata.",
"MessageBookshelfNoCollections": "Du har ikke laget noen samlinger ennå",
"MessageBookshelfNoCollectionsHelp": "Samlinger er offentlige. Alle brukere med tilgang til biblioteket kan se dem.",
"MessageBookshelfNoRSSFeeds": "Ingen RSS feed er åpen",
"MessageBookshelfNoResultsForFilter": "Ingen resultat for filter \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Ingen resultater for søket",
"MessageBookshelfNoSeries": "Du har ingen serier",
"MessageBulkChapterPattern": "Hvor mange kapitler vil du legge til med dette nummereringsmønsteret?",
"MessageChapterEndIsAfter": "Kapittel slutt er etter slutt av lydboken",
"MessageChapterErrorFirstNotZero": "Første kapittel starter på 0",
"MessageChapterErrorStartGteDuration": "Feil start tid, må være mindre enn lengde på lydbok",
"MessageChapterErrorStartLtPrev": "Feil start tid, må være større eller det samme som forrige kapittel start tid",
"MessageChapterStartIsAfter": "Kapittel start er etter slutten av din lydbok",
"MessageChaptersNotFound": "Fant ikke kapitler",
"MessageCheckingCron": "Sjekker cron...",
"MessageConfirmCloseFeed": "Er du sikker på at du vil lukke denne feeden?",
"MessageConfirmDeleteApiKey": "Er du sikker på at du vil slette API-nøkkelen \"{0}\"?",
"MessageConfirmDeleteBackup": "Er du sikker på at du vil slette sikkerhetskopi for {0}?",
"MessageConfirmDeleteDevice": "Er du sikker på at du vil slette e-leser enheten \"{0}\"?",
"MessageConfirmDeleteFile": "Dette vil slette filen fra filsystemet. Er du sikker?",
@ -781,7 +761,7 @@
"MessageConfirmMarkSeriesFinished": "Er du sikker på at du vil markere alle bøkene i serien som fullført?",
"MessageConfirmMarkSeriesNotFinished": "Er du sikker på at du vil markere alle bøkene i serien som ikke fullført?",
"MessageConfirmNotificationTestTrigger": "Utløs dette varselet med test-data?",
"MessageConfirmPurgeCache": "Tømming av mellomlagring vil slette hele mappen <code>/metadata/cache</code>. <br /><br />Er du sikker på at du vil slette mappen?",
"MessageConfirmPurgeCache": "Tømming av mellomlagring vil slette hele mappen <code>/metadata/cache</code>. <br /><br />Er du sikker på at du du vil slette mappen?",
"MessageConfirmPurgeItemsCache": "(Purge items cache) Dette vil sletter hele mappen <code>/metadata/cache/items</code>.<br />Er du sikker?",
"MessageConfirmQuickEmbed": "Advarsel! Rask innbygging av metadata tar ikke backup av lyd-filene først. Forsikre deg om at du har sikkerhetskopi av filene. <br><br> Fortsett?",
"MessageConfirmQuickMatchEpisodes": "Hurtig gjenkjenning av episoder overskriver detaljene hvis en match blir funnet. Kun episoder som ikke allerede er matchet blir oppdatert. Er du sikker?",
@ -790,7 +770,6 @@
"MessageConfirmRemoveAuthor": "Er du sikker på at du vil fjerne forfatteren \"{0}\"?",
"MessageConfirmRemoveCollection": "Er du sikker på at du vil fjerne samling\"{0}\"?",
"MessageConfirmRemoveEpisode": "Er du sikker på at du vil fjerne episode \"{0}\"?",
"MessageConfirmRemoveEpisodeNote": "Merk: Dette sletter ikke lydfilen med mindre du slår på \"Hard delete file\"",
"MessageConfirmRemoveEpisodes": "Er du sikker på at du vil fjerne {0} episoder?",
"MessageConfirmRemoveListeningSessions": "Er du sikker på at du vil fjerne {0} lytte-sesjoner?",
"MessageConfirmRemoveMetadataFiles": "Er du sikker på at du vil fjerne alle metadata.{0}-filer i mappene for biblioteks-elementer?",
@ -816,11 +795,8 @@
"MessageFeedURLWillBe": "Feed URL vil bli {0}",
"MessageFetching": "Henter...",
"MessageForceReScanDescription": "vil skanne alle filene igjen som en ny skann. Lyd fil ID3 tagger, OPF filer og tekstfiler vil bli skannet som nye.",
"MessageHeatmapListeningTimeTooltip": "<strong>{0} lytter</strong> på {1}",
"MessageHeatmapNoListeningSessions": "Ingen lytteøkter på {0}",
"MessageImportantNotice": "Viktig varsel!",
"MessageInsertChapterBelow": "Sett inn kapittel under",
"MessageInvalidAsin": "Ugyldig ASIN",
"MessageItemsSelected": "{0} Gjenstander valgt",
"MessageItemsUpdated": "{0} Gjenstander oppdatert",
"MessageJoinUsOn": "Følg oss nå",
@ -866,7 +842,6 @@
"MessageNoTasksRunning": "Ingen oppgaver kjører",
"MessageNoUpdatesWereNecessary": "Ingen oppdatering var nødvendig",
"MessageNoUserPlaylists": "Du har ingen spillelister",
"MessageNoUserPlaylistsHelp": "Spillelister er private. Bare brukeren som oppretter dem kan se dem.",
"MessageNotYetImplemented": "Ikke implementert ennå",
"MessageOpmlPreviewNote": "PS: Dette er en forhåndvisning av en OPML-fil. Den faktiske podcast-tittelen hentes direkte fra RSS-feeden.",
"MessageOr": "eller",
@ -889,10 +864,8 @@
"MessageRestoreBackupConfirm": "Er du sikker på at du vil gjenopprette sikkerhetskopien som var laget",
"MessageRestoreBackupWarning": "gjenoppretting av sikkerhetskopi vil overskrive hele databasen under /config og omslagsbilde under /metadata/items og /metadata/authors.<br /><br />Sikkerhetskopier endrer ikke noen filer under dine bibliotekmapper. Hvis du har aktivert tjenerinstillingen for å lagre omslagsbilder og metadata i bibliotekmapper så vil ikke de filene bli tatt sikkerhetskopi eller overskrevet.<br /><br />Alle klientene som bruker din tjener vil bli fornyet automatisk.",
"MessageScheduleLibraryScanNote": "For de fleste brukere er det anbefalt å la denne funksjonen være slått av, og la mappeovervåkeren stå på. Mappeovervåkeren oppdager automatisk endringer i biblioteksmappene. Mappeovervåkeren fungerer ikke med alle filsystemer (f.eks. NFS) og da kan planlagt skanning av bibliotekene brukes i steden for.",
"MessageScheduleRunEveryWeekdayAtTime": "Kjør hver {0} kl. {1}",
"MessageSearchResultsFor": "Søk resultat for",
"MessageSelected": "{0} valgt",
"MessageSeriesSequenceCannotContainSpaces": "Serienummer kan ikke inneholde mellomrom",
"MessageServerCouldNotBeReached": "Tjener kunne ikke bli nådd",
"MessageSetChaptersFromTracksDescription": "Sett kapitler ved å bruke hver lydfil som kapittel og kapitteltittel som lydfilnavnet",
"MessageShareExpirationWillBe": "Utløp vil være <strong>{0}</strong>",
@ -913,27 +886,6 @@
"MessageTaskFailedToMergeAudioFiles": "Kunne ikke slå sammen lydfiler",
"MessageTaskFailedToMoveM4bFile": "Kunne ikke flytte M4B-fil",
"MessageTaskFailedToWriteMetadataFile": "Kunne ikke lagre metadata-fil",
"MessageTaskMatchingBooksInLibrary": "Samsvarende bøker i biblioteket \"{0}\"",
"MessageTaskNoFilesToScan": "Ingen filer å skanne",
"MessageTaskOpmlImport": "OPML-import",
"MessageTaskOpmlImportDescription": "Oppretter podkaster fra {0} RSS-feeder",
"MessageTaskOpmlImportFeed": "OPML-importfeed",
"MessageTaskOpmlImportFeedDescription": "Importerer RSS-feed \"{0}\"",
"MessageTaskOpmlImportFeedFailed": "Kunne ikke hente podcast-feed",
"MessageTaskOpmlImportFeedPodcastDescription": "Oppretter podkast \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podkast finnes allerede på stien",
"MessageTaskOpmlImportFeedPodcastFailed": "Misslykkes å opprette podcast",
"MessageTaskOpmlImportFinished": "La til {0} podkaster",
"MessageTaskOpmlParseFailed": "Klarte ikke å tolke OPML-fil",
"MessageTaskOpmlParseFastFail": "Ugyldig OPML-fil: <opml>-tagg ble ikke funnet ELLER en <outline>-tagg ble ikke funnet",
"MessageTaskOpmlParseNoneFound": "Fant ingen feeder i OPML-filen",
"MessageTaskScanItemsAdded": "{0} lagt til",
"MessageTaskScanItemsMissing": "{0} mangler",
"MessageTaskScanItemsUpdated": "{0} oppdatert",
"MessageTaskScanNoChangesNeeded": "Ingen endringer nødvendig",
"MessageTaskScanningFileChanges": "Skanner filendringer i \"{0}\"",
"MessageTaskScanningLibrary": "Skanner biblioteket \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "Målkatalogen er ikke skrivbar",
"MessageThinking": "Tenker...",
"MessageUploaderItemFailed": "Opplastning mislykkes",
"MessageUploaderItemSuccess": "Opplastning fullført!",
@ -951,43 +903,13 @@
"NoteUploaderFoldersWithMediaFiles": "Mapper med mediefiler vil bli behandlet som separate bibliotekgjenstander.",
"NoteUploaderOnlyAudioFiles": "Om man laster opp kun lydfiler så vil hver lydfil bli behandlet som en separat lydbok.",
"NoteUploaderUnsupportedFiles": "Filer som ikke er støttet vil bli ignorert. Når man velger eller slipper en mappe, filer som ikke er en mappe vil bli ignorert.",
"NotificationOnBackupCompletedDescription": "Utløses når en sikkerhetskopi er fullført",
"NotificationOnBackupFailedDescription": "Utløses når en sikkerhetskopi mislykkes",
"NotificationOnEpisodeDownloadedDescription": "Utløses når en podkastepisode lastes ned automatisk",
"NotificationOnRSSFeedDisabledDescription": "Utløses når automatiske episodenedlastinger deaktiveres på grunn av for mange mislykkede forsøk",
"NotificationOnRSSFeedFailedDescription": "Utløses når RSS-feedforespørselen mislykkes for en automatisk episodenedlasting",
"NotificationOnTestDescription": "Hendelse for testing av varslingssystemet",
"PlaceholderBulkChapterInput": "Skriv inn kapitteltittel eller bruk nummerering (f.eks. 'Episode 1', 'Kapittel 10', '1.')",
"PlaceholderNewCollection": "Ny samlingsnavn",
"PlaceholderNewFolderPath": "Ny mappesti",
"PlaceholderNewPlaylist": "Ny spillelistenavn",
"PlaceholderSearch": "Søk..",
"PlaceholderSearchEpisode": "Søk episode..",
"StatsAuthorsAdded": "forfattere lagt til",
"StatsBooksAdded": "bøker lagt til",
"StatsBooksAdditional": "Noen av tilleggene inkluderer…",
"StatsBooksFinished": "bøker fullført",
"StatsBooksFinishedThisYear": "Noen bøker fullført i år…",
"StatsBooksListenedTo": "bøker lyttet til",
"StatsCollectionGrewTo": "Boksamlingen din vokste til…",
"StatsSessions": "økter",
"StatsSpentListening": "brukt på lytting",
"StatsTopAuthor": "BESTE FORFATTER",
"StatsTopAuthors": "BESTE FORFATTERE",
"StatsTopGenre": "BESTE SJANGER",
"StatsTopGenres": "BESTE SJANGRE",
"StatsTopMonth": "BESTE MÅNED",
"StatsTopNarrator": "BESTE FORTELLER",
"StatsTopNarrators": "BESTE FORTELLERE",
"StatsTotalDuration": "Med en total varighet på…",
"StatsYearInReview": "ÅRET SOM GIKK",
"ToastAccountUpdateSuccess": "Konto oppdatert",
"ToastAppriseUrlRequired": "Du må angi en Apprise-URL",
"ToastAsinRequired": "ASIN er påkrevd",
"ToastAuthorImageRemoveSuccess": "Forfatter bilde fjernet",
"ToastAuthorNotFound": "Fant ikke forfatter \"{0}\"",
"ToastAuthorRemoveSuccess": "Forfatter fjernet",
"ToastAuthorSearchNotFound": "Fant ikke forfatter",
"ToastAuthorUpdateMerged": "Forfatter slått sammen",
"ToastAuthorUpdateSuccess": "Forfatter oppdatert",
"ToastAuthorUpdateSuccessNoImageFound": "Forfatter oppdater (ingen bilde funnet)",
@ -1001,7 +923,6 @@
"ToastBackupRestoreFailed": "Misslykkes å gjenopprette sikkerhetskopi",
"ToastBackupUploadFailed": "Misslykkes å laste opp sikkerhetskopi",
"ToastBackupUploadSuccess": "Sikkerhetskopi lastet opp",
"ToastBatchApplyDetailsToItemsSuccess": "Detaljer brukt på elementene",
"ToastBatchDeleteFailed": "Sletting feilet på utvalget",
"ToastBatchDeleteSuccess": "Sletting av samling utført",
"ToastBatchQuickMatchFailed": "Feil ved rask integrering av metadata!",
@ -1011,25 +932,17 @@
"ToastBookmarkCreateFailed": "Misslykkes å opprette bokmerke",
"ToastBookmarkCreateSuccess": "Bokmerke lagt til",
"ToastBookmarkRemoveSuccess": "Bokmerke fjernet",
"ToastBulkChapterInvalidCount": "Skriv inn et tall mellom 1 og 150",
"ToastCachePurgeFailed": "Kunne ikke å slette mellomlager",
"ToastCachePurgeSuccess": "Mellomlager slettet",
"ToastChapterLocked": "Kapittelet er låst.",
"ToastChapterStartTimeAdjusted": "Kapittelstart ble justert med {0} sekunder",
"ToastChaptersAllLocked": "Alle kapitler er låst. Lås opp noen kapitler for å flytte tidene.",
"ToastChaptersHaveErrors": "Kapittel har feil",
"ToastChaptersInvalidShiftAmountLast": "Ugyldig forskyvningsverdi. Starttid for siste kapittel vil gå utover varigheten til denne lydboken.",
"ToastChaptersInvalidShiftAmountStart": "Ugyldig forskyvningsverdi. Det første kapitlet ville fått null eller negativ lengde og blitt overskrevet av det andre kapitlet. Øk starttiden til det andre kapitlet.",
"ToastChaptersMustHaveTitles": "Kapittel må ha titler",
"ToastChaptersRemoved": "Kapitler fjernet",
"ToastChaptersUpdated": "Kapitler oppdatert",
"ToastCollectionItemsAddFailed": "Feil med å legge til element(er)",
"ToastCollectionRemoveSuccess": "Samling fjernet",
"ToastCollectionUpdateSuccess": "samlingupdated",
"ToastConnectionNotAvailable": "Tilkobling er ikke tilgjengelig. Prøv igjen senere",
"ToastCoverSearchFailed": "Finner ikke bokomslag",
"ToastCoverUpdateFailed": "Oppdatering av bilde feilet",
"ToastDateTimeInvalidOrIncomplete": "Dato og klokkeslett er ugyldig eller ufullstendig",
"ToastDeleteFileFailed": "Kunne ikke slette fil",
"ToastDeleteFileSuccess": "Fil slettet",
"ToastDeviceAddFailed": "Kunne ikke legge til enhet",
@ -1042,9 +955,6 @@
"ToastEpisodeDownloadQueueClearFailed": "Kunne ikke tømme køen",
"ToastEpisodeDownloadQueueClearSuccess": "Nedlastingskø for eposider tømt",
"ToastEpisodeUpdateSuccess": "{0} episoder oppdatert",
"ToastErrorCannotShare": "Kan ikke dele direkte på denne enheten",
"ToastFailedToCreate": "Kunne ikke opprette",
"ToastFailedToDelete": "Kunne ikke slette",
"ToastFailedToLoadData": "Kunne ikke laste inn data",
"ToastFailedToMatch": "Kunne ikke matche",
"ToastFailedToShare": "Deling feilet",
@ -1052,7 +962,6 @@
"ToastInvalidImageUrl": "Ugyldig URL for bilde",
"ToastInvalidMaxEpisodesToDownload": "Ugyldig maksimalt antall for nedlasting av episoder",
"ToastInvalidUrl": "Ugyldig URL",
"ToastInvalidUrls": "Én eller flere URL-er er ugyldige",
"ToastItemCoverUpdateSuccess": "Omslag oppdatert",
"ToastItemDeletedFailed": "Kunne ikke slette element",
"ToastItemDeletedSuccess": "Element slettet",
@ -1077,7 +986,6 @@
"ToastMustHaveAtLeastOnePath": "Påkrevd med minst én mappe",
"ToastNameEmailRequired": "Navn og e-post påkrevd",
"ToastNameRequired": "Navn er påkrevd",
"ToastNewApiKeyUserError": "Du må velge en bruker",
"ToastNewEpisodesFound": "{0} nye episoder funnet",
"ToastNewUserCreatedFailed": "Kunne ikke opprette konto: \"{0}\"",
"ToastNewUserCreatedSuccess": "Ny konto opprettet",
@ -1086,7 +994,6 @@
"ToastNewUserTagError": "Velg minst en tag",
"ToastNewUserUsernameError": "Skriv inn brukernavn",
"ToastNoNewEpisodesFound": "Ingen nye episoder funnet",
"ToastNoRSSFeed": "Podkasten har ikke en RSS-feed",
"ToastNoUpdatesNecessary": "Ingen oppdateringer nødvendig",
"ToastNotificationCreateFailed": "Kunne ikke opprette varsling",
"ToastNotificationDeleteFailed": "Kunne ikke slette varsling",
@ -1102,7 +1009,6 @@
"ToastPlaylistUpdateSuccess": "Spilleliste oppdatert",
"ToastPodcastCreateFailed": "Misslykkes å opprette podcast",
"ToastPodcastCreateSuccess": "Podcast opprettet",
"ToastPodcastEpisodeUpdated": "Episode oppdatert",
"ToastPodcastGetFeedFailed": "Kunne ikke hente podcast-feed",
"ToastPodcastNoEpisodesInFeed": "Ingen episoder funnet i RSS-feed",
"ToastPodcastNoRssFeed": "Podcast har ingen RSS-feed",
@ -1127,7 +1033,6 @@
"ToastSelectAtLeastOneUser": "Velg minst én bruker",
"ToastSendEbookToDeviceFailed": "Misslykkes å sende ebok",
"ToastSendEbookToDeviceSuccess": "Ebok sendt til \"{0}\"",
"ToastSeriesSubmitFailedSameName": "Kan ikke legge til to serier med samme navn",
"ToastSeriesUpdateFailed": "Misslykkes å oppdatere serie",
"ToastSeriesUpdateSuccess": "Serie oppdatert",
"ToastServerSettingsUpdateSuccess": "Server-innstillinger oppdatert",
@ -1146,20 +1051,10 @@
"ToastUnknownError": "Ukjent feil",
"ToastUnlinkOpenIdFailed": "Kunne ikke koble bruker fra OpenID",
"ToastUnlinkOpenIdSuccess": "Bruker koblet fra OpenID",
"ToastUploaderFilepathExistsError": "Filstien \"{0}\" finnes allerede på serveren",
"ToastUploaderItemExistsInSubdirectoryError": "Elementet \"{0}\" bruker en underkatalog av opplastingsstien.",
"ToastUserDeleteFailed": "Misslykkes å slette bruker",
"ToastUserDeleteSuccess": "Bruker slettet",
"ToastUserPasswordChangeSuccess": "Passord ble endret",
"ToastUserPasswordMismatch": "Passord må stemme overens",
"ToastUserPasswordMustChange": "Nytt passord kan ikke være identisk med gammelt passord",
"ToastUserRootRequireName": "Root-brukernavn er påkrevd",
"TooltipAddChapters": "Legg til kapittel(er)",
"TooltipAddOneSecond": "Legg til 1 sekund",
"TooltipAdjustChapterStart": "Klikk for å justere starttid",
"TooltipLockAllChapters": "Lås alle kapitler",
"TooltipLockChapter": "Lås kapittel (Shift+klikk for område)",
"TooltipSubtractOneSecond": "Trekk fra 1 sekund",
"TooltipUnlockAllChapters": "Lås opp alle kapitler",
"TooltipUnlockChapter": "Lås opp kapittel (Shift+klikk for område)"
"ToastUserRootRequireName": "Root-brukernavn er påkrevd"
}

View file

@ -11,7 +11,7 @@
"ButtonApplyChapters": "Zatwierdź rozdziały",
"ButtonAuthors": "Autorzy",
"ButtonBack": "Wstecz",
"ButtonBatchEditPopulateFromExisting": "Uzupełnij na podstawie istniejących",
"ButtonBatchEditPopulateFromExisting": "Powiel z poprzednich",
"ButtonBatchEditPopulateMapDetails": "Powiel szczegóły mapy",
"ButtonBrowseForFolder": "Wyszukaj folder",
"ButtonCancel": "Anuluj",
@ -55,7 +55,7 @@
"ButtonNext": "Następny",
"ButtonNextChapter": "Następny rozdział",
"ButtonNextItemInQueue": "Następny element w kolejce",
"ButtonOk": "OK",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Otwórz feed",
"ButtonOpenManager": "Otwórz menadżera",
"ButtonPause": "Wstrzymaj",
@ -127,7 +127,7 @@
"HeaderAudiobookTools": "Narzędzia do zarządzania audiobookami",
"HeaderAuthentication": "Uwierzytelnianie",
"HeaderBackups": "Kopie zapasowe",
"HeaderBulkChapterModal": "Dodaj kilka rozdziałów",
"HeaderBulkChapterModal": "Dodaj wiele rozdziałów",
"HeaderChangePassword": "Zmień hasło",
"HeaderChapters": "Rozdziały",
"HeaderChooseAFolder": "Wybierz folder",
@ -275,7 +275,7 @@
"LabelBonus": "Bonus",
"LabelBooks": "Książki",
"LabelButtonText": "Tekst przycisku",
"LabelByAuthor": "Autor {0}",
"LabelByAuthor": "autorstwa {0}",
"LabelChangePassword": "Zmień hasło",
"LabelChannels": "Kanały",
"LabelChapterCount": "{0} rozdziałów",
@ -286,7 +286,7 @@
"LabelClickToUseCurrentValue": "Kliknij by zastosować aktualną wartość",
"LabelClosePlayer": "Zamknij odtwarzacz",
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Zwiń serię",
"LabelCollapseSeries": "Podsumuj serię",
"LabelCollapseSubSeries": "Zwiń podserie",
"LabelCollection": "Kolekcja",
"LabelCollections": "Kolekcje",
@ -300,7 +300,7 @@
"LabelCoverImageURL": "URL okładki",
"LabelCoverProvider": "Dostawca okładki",
"LabelCreatedAt": "Utworzone",
"LabelCronExpression": "Wyrażenie harmonogramowania zadań cron",
"LabelCronExpression": "Wyrażenie CRON",
"LabelCurrent": "Aktualny",
"LabelCurrently": "Obecnie:",
"LabelCustomCronExpression": "Niestandardowe wyrażenie Cron:",
@ -339,11 +339,11 @@
"LabelEnable": "Włącz",
"LabelEncodingBackupLocation": "Kopia zapasowa twoich oryginalnych plików audio będzie się znajdować w:",
"LabelEncodingChaptersNotEmbedded": "W audiobookach wielościeżkowych rozdziały nie są osadzone.",
"LabelEncodingClearItemCache": "Pamiętaj, aby okresowo czyścić pamięć podręczną elementów.",
"LabelEncodingClearItemCache": "Pamiętaj o okresowym czyszczeniu pamięci podręcznej elementów.",
"LabelEncodingFinishedM4B": "Ukończony plik M4B zostanie umieszczony w folderze audiobooka pod adresem:",
"LabelEncodingInfoEmbedded": "Metadane zostaną osadzone w ścieżkach audio w folderze z audiobookiem.",
"LabelEncodingStartedNavigation": "Po uruchomieniu zadania możesz opuścić tę stronę.",
"LabelEncodingTimeWarning": "Kodowanie może potrwać do 30 minut.",
"LabelEncodingTimeWarning": "Konwersja może potrwać do 30 minut.",
"LabelEncodingWarningAdvancedSettings": "Ostrzeżenie: Nie aktualizuj tych ustawień, jeśli nie jesteś zaznajomiony ze sposobem działania ffmpeg i opcji konwersji.",
"LabelEncodingWatcherDisabled": "Jeśli monitorowanie folderów jest wyłączone, należy ponownie przeskanować audiobooka.",
"LabelEnd": "Zakończ",
@ -359,8 +359,8 @@
"LabelExample": "Przykład",
"LabelExpandSeries": "Rozwiń serie",
"LabelExpandSubSeries": "Rozwiń podserie",
"LabelExpired": "Wygasły",
"LabelExpiresAt": "Wygasa o",
"LabelExpired": "Przeterminowane",
"LabelExpiresAt": "Wygasa w",
"LabelExpiresInSeconds": "Wygasa za (sekund)",
"LabelExpiresNever": "Nigdy",
"LabelExplicit": "18+",
@ -436,7 +436,7 @@
"LabelLibraryFilterSublistEmpty": "Brak {0}",
"LabelLibraryItem": "Element biblioteki",
"LabelLibraryName": "Nazwa biblioteki",
"LabelLibrarySortByProgress": "Postęp: Ostatnia aktualizacja",
"LabelLibrarySortByProgress": "Postęp: Ostatnio zaktualizowane",
"LabelLibrarySortByProgressFinished": "Postęp: Ukończony",
"LabelLibrarySortByProgressStarted": "Postęp: Rozpoczęty",
"LabelLimit": "Limit",
@ -450,7 +450,7 @@
"LabelMatchConfidence": "Zaufanie",
"LabelMatchExistingUsersBy": "Dopasuje istniejących użytkowników poprzez",
"LabelMatchExistingUsersByDescription": "Służy do łączenia istniejących użytkowników. Po połączeniu użytkownicy zostaną dopasowani za pomocą unikalnego identyfikatora od dostawcy SSO",
"LabelMaxEpisodesToDownload": "Maksymalna liczba odcinków do pobrania. Użyj 0, aby uzyskać nieograniczoną liczbę.",
"LabelMaxEpisodesToDownload": "Maksymalna liczba odcinków do pobrania. Użyj 0, aby wyłączyć ograniczenie.",
"LabelMaxEpisodesToDownloadPerCheck": "Maksymalna liczba nowych odcinków do pobrania na jedno sprawdzenie",
"LabelMaxEpisodesToKeep": "Maksymalna liczba odcinków do zachowania",
"LabelMaxEpisodesToKeepHelp": "Wartość 0 wyłącza maksymalny limit. Po automatycznym pobraniu nowego odcinka, najstarszy odcinek zostanie usunięty, jeśli masz ich więcej niż X. Spowoduje to usunięcie tylko 1 odcinka na nowe pobieranie.",
@ -498,8 +498,7 @@
"LabelNumberOfChapters": "Liczba rozdziałów:",
"LabelNumberOfEpisodes": "# Odcinków",
"LabelOpenIDAdvancedPermsClaimDescription": "Nazwa deklaracji OpenID zawierającej zaawansowane uprawnienia do działań użytkownika w aplikacji, które będą miały zastosowanie do ról innych niż administracyjne (<b>jeśli skonfigurowano</b>). Jeśli deklaracja nie zostanie uwzględniona w odpowiedzi, dostęp do ABS zostanie zablokowany. Brak jednej opcji zostanie uznany za <code>fałsz</code>. Upewnij się, że deklaracja dostawcy tożsamości jest zgodna z oczekiwaną strukturą:",
"LabelOpenIDClaims": "Pozostaw poniższe opcje puste, aby wyłączyć zaawansowane przypisywanie grup i uprawnień. Wówczas automatycznie zostanie przypisana grupa „Użytkownik”.",
"LabelOpenIDGroupClaimDescription": "Nazwa roszczenia OpenID zawierającego listę grup użytkownika. Powszechnie nazywane <code>grupami</code>. <b>Jeśli skonfigurowano</b>, aplikacja automatycznie przypisze role na podstawie przynależności użytkownika do grup, pod warunkiem, że te grupy są nazwane bez uwzględniania wielkości liter „admin”, „user” lub „guest” w roszczeniu. Roszczenie powinno zawierać listę, a jeśli użytkownik należy do wielu grup, aplikacja przypisze rolę odpowiadającą najwyższemu poziomowi dostępu. Jeśli żadna grupa nie będzie pasować, dostęp zostanie odrzucony.",
"LabelOpenIDClaims": "Pozostaw poniższe opcje puste, aby wyłączyć zaawansowane przypisywanie grup i uprawnień. Automatycznie zostanie przypisana grupa „Użytkownik”.",
"LabelOpenRSSFeed": "Otwórz kanał RSS",
"LabelOverwrite": "Nadpisz",
"LabelPaginationPageXOfY": "Strona {0} z {1}",
@ -574,11 +573,11 @@
"LabelSelectUsers": "Wybór użytkowników",
"LabelSendEbookToDevice": "Wyślij ebook do...",
"LabelSequence": "Kolejność",
"LabelSerial": "Numer serii",
"LabelSerial": "Seria",
"LabelSeries": "Serie",
"LabelSeriesName": "Nazwy serii",
"LabelSeriesProgress": "Postęp w serii",
"LabelServerLogLevel": "Poziom logów servera",
"LabelServerLogLevel": "Poziom logowania servera",
"LabelServerYearReview": "Podsumowanie serwera w roku ({0})",
"LabelSetEbookAsPrimary": "Ustaw jako pierwszy",
"LabelSetEbookAsSupplementary": "Ustaw jako dodatkowy",
@ -622,7 +621,7 @@
"LabelSettingsStoreMetadataWithItemHelp": "Domyślnie metadane są przechowywane w folderze /metadata/items, włączenie tej opcji spowoduje, że okładka będzie przechowywana w folderze ksiązki. Tylko jedna okładka o nazwie pliku \"cover\" będzie przechowywana",
"LabelSettingsTimeFormat": "Format czasu",
"LabelShare": "Udostępnij",
"LabelShareDownloadableHelp": "Zezwala użytkownikom z linkiem udostępniania na pobranie pliku zip elementu biblioteki.",
"LabelShareDownloadableHelp": "Użytkownicy mogą przy pomocy linka ściągnąć archiwum ZIP pozycji biblioteki",
"LabelShareOpen": "Otwórz udział",
"LabelShareURL": "Link do udziału",
"LabelShowAll": "Pokaż wszystko",
@ -661,11 +660,11 @@
"LabelTagsAccessibleToUser": "Tagi dostępne dla użytkownika",
"LabelTagsNotAccessibleToUser": "Znaczniki niedostępne dla użytkownika",
"LabelTasks": "Uruchomione zadania",
"LabelTextEditorBulletedList": "Lista wypunktowana",
"LabelTextEditorBulletedList": "Lista punktowana",
"LabelTextEditorLink": "Link",
"LabelTextEditorNumberedList": "Lista numerowana",
"LabelTextEditorUnlink": "Usuń link",
"LabelTheme": "Motyw",
"LabelTheme": "Kompozycja",
"LabelThemeDark": "Ciemny",
"LabelThemeLight": "Jasny",
"LabelThemeSepia": "Sepia",
@ -687,13 +686,13 @@
"LabelToolsMakeM4bDescription": "Tworzy plik w formacie .M4B, który zawiera metadane, okładkę oraz rozdziały.",
"LabelToolsSplitM4b": "Podziel plik .M4B na pliki .MP3",
"LabelToolsSplitM4bDescription": "Podziel plik .M4B na pliki .MP3 na rozdziały z załączonymi metadanymi oraz okładką.",
"LabelTotalDuration": "Całkowita długość",
"LabelTotalDuration": "TCałkowita długość",
"LabelTotalTimeListened": "Całkowity czas odtwarzania",
"LabelTrackFromFilename": "Ścieżka z nazwy pliku",
"LabelTrackFromMetadata": "Ścieżka z metadanych",
"LabelTracks": "Ścieżki",
"LabelTracksMultiTrack": "Wielościeżkowy",
"LabelTracksNone": "Brak utworów",
"LabelTracksNone": "Brak ścieżek",
"LabelTracksSingleTrack": "Pojedyncza ścieżka",
"LabelTrailer": "Zwiastun",
"LabelType": "Typ",
@ -710,7 +709,7 @@
"LabelUploaderDragAndDropFilesOnly": "Przeciągnij i upuść pliki",
"LabelUploaderDropFiles": "Puść pliki",
"LabelUploaderItemFetchMetadataHelp": "Automatycznie pobierz tytuł, autora i serie",
"LabelUseAdvancedOptions": "Użyj ustawień zaawansowanych",
"LabelUseAdvancedOptions": "Opcje zaawansowane",
"LabelUseChapterTrack": "Użyj ścieżki rozdziału",
"LabelUseFullTrack": "Użycie ścieżki rozdziału",
"LabelUseZeroForUnlimited": "Użyj 0, aby wyłączyć ograniczenia",
@ -724,7 +723,6 @@
"LabelViewQueue": "Wyświetlaj kolejkę odtwarzania",
"LabelVolume": "Głośność",
"LabelWebRedirectURLsDescription": "Zezwól na te adresy URL w swoim dostawcy OAuth, aby umożliwić przekierowanie z powrotem do aplikacji internetowej po zalogowaniu:",
"LabelWebRedirectURLsSubfolder": "Podfolder dla adresów URL przekierowań",
"LabelWeekdaysToRun": "Dni tygodnia",
"LabelXBooks": "{0} książek",
"LabelXItems": "{0} elementów",
@ -745,7 +743,7 @@
"MessageBackupsLocationNoEditNote": "Uwaga: Lokalizacja kopii zapasowej jest ustawiona poprzez zmienną środowiskową i nie może być tutaj zmieniona.",
"MessageBackupsLocationPathEmpty": "Ścieżka do kopii zapasowej nie może być pusta",
"MessageBatchEditPopulateMapDetailsAllHelp": "Wypełnij włączone pola danymi ze wszystkich elementów. Pola z wieloma wartościami zostaną scalone.",
"MessageBatchEditPopulateMapDetailsItemHelp": "Wypełnia włączone pola szczegółów mapy danymi z tego elementu",
"MessageBatchEditPopulateMapDetailsItemHelp": "Wypełnij pola szczegółów mapy włączonej danymi z tego elementu",
"MessageBatchQuickMatchDescription": "Quick Match będzie próbował dodać brakujące okładki i metadane dla wybranych elementów. Włącz poniższe opcje, aby umożliwić Quick Match nadpisanie istniejących okładek i/lub metadanych.",
"MessageBookshelfNoCollections": "Nie posiadasz jeszcze żadnych kolekcji",
"MessageBookshelfNoCollectionsHelp": "Kolekcje są publiczne. Wszyscy użytkownicy mający dostęp do biblioteki mogą je zobaczyć.",
@ -917,21 +915,15 @@
"MessageTaskNoFilesToScan": "Brak plików do skanowania",
"MessageTaskOpmlImport": "Importuj OPML",
"MessageTaskOpmlImportDescription": "Tworzenie {0} podcastów z kanałów RSS",
"MessageTaskOpmlImportFeed": "Importuje plik OPML",
"MessageTaskOpmlImportFeedDescription": "Importowanie kanału RSS „{0}”",
"MessageTaskOpmlImportFeedFailed": "Nie udało się pobrać kanału podcastowego",
"MessageTaskOpmlImportFeedPodcastDescription": "Tworzenie podcastu \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast już istnieje pod podaną ścieżką",
"MessageTaskOpmlImportFeedPodcastFailed": "Nie udało się utworzyć podcastu",
"MessageTaskOpmlImportFinished": "Dodano {0} podcastów",
"MessageTaskOpmlParseFailed": "Błąd parsowania pliku OPML",
"MessageTaskOpmlParseFastFail": "Nieprawidłowy plik OPML. Nie znaleziono tagu <opml> LUB nie znaleziono tagu <outline>.",
"MessageTaskOpmlParseNoneFound": "Nie znaleziono kanałów w pliku OPML",
"MessageTaskScanItemsAdded": "Dodano {0}",
"MessageTaskScanItemsMissing": "Brakuje {0}",
"MessageTaskScanItemsUpdated": "Zaktualizowano {0}",
"MessageTaskScanNoChangesNeeded": "Brak zmian",
"MessageTaskScanningFileChanges": "Skanowanie zmian w plikach w „{0}”",
"MessageTaskScanningLibrary": "Skanowanie biblioteki \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "Brak prawa zapisu do folderu docelowego",
"MessageThinking": "Myślę...",
@ -952,7 +944,6 @@
"NoteUploaderOnlyAudioFiles": "Jeśli przesyłasz tylko pliki audio, każdy plik audio będzie traktowany jako osobny audiobook.",
"NoteUploaderUnsupportedFiles": "Nieobsługiwane pliki są ignorowane. Podczas dodawania folderu, inne pliki, które nie znajdują się w folderze elementu, są ignorowane.",
"NotificationOnTestDescription": "Zdarzenie używane do testowania systemu powiadomień",
"PlaceholderBulkChapterInput": "Wpisz tytuł rozdziału lub użyj numeracji (np. „Odcinek 1”, „Rozdział 10”, „1.”)",
"PlaceholderNewCollection": "Nowa nazwa kolekcji",
"PlaceholderNewFolderPath": "Nowa ścieżka folderu",
"PlaceholderNewPlaylist": "Nowa nazwa playlisty",
@ -1027,12 +1018,8 @@
"ToastDeviceTestEmailFailed": "NIeudana próba wysłania testowego maila",
"ToastDeviceTestEmailSuccess": "Testowy email został wysłany",
"ToastEmailSettingsUpdateSuccess": "Ustawienia email zaktualizowane",
"ToastEncodeCancelFailed": "Nie udało się anulować kodowania",
"ToastEncodeCancelSucces": "Kodowanie anulowane",
"ToastEpisodeDownloadQueueClearFailed": "Nie udało się wyczyścić kolejki",
"ToastEpisodeDownloadQueueClearSuccess": "Wyczyszczono kolejkę epizodów do ściągnięcia",
"ToastEpisodeUpdateSuccess": "Zaktualizowano {0} odcinków",
"ToastErrorCannotShare": "Nie można udostępniać natywnie na tym urządzeniu.",
"ToastInvalidImageUrl": "Nieprawidłowy URL obrazu",
"ToastInvalidUrl": "Nieprawidłowy URL",
"ToastInvalidUrls": "Jeden lub więcej URL-i są nieprawidłowe",

View file

@ -436,8 +436,8 @@
"LabelLibraryFilterSublistEmpty": "Sem {0}",
"LabelLibraryItem": "Item da Biblioteca",
"LabelLibraryName": "Nome da Biblioteca",
"LabelLibrarySortByProgress": "Progresso: Ultima Atualização",
"LabelLibrarySortByProgressFinished": "Progresso: Terminado",
"LabelLibrarySortByProgress": "Última Atualização",
"LabelLibrarySortByProgressFinished": "Concluído",
"LabelLibrarySortByProgressStarted": "Progresso: Iniciado",
"LabelLimit": "Limite",
"LabelLineSpacing": "Espaçamento entre linhas",

View file

@ -11,581 +11,140 @@
"ButtonApplyChapters": "Aplică Capitole",
"ButtonAuthors": "Autori",
"ButtonBack": "Înapoi",
"ButtonBatchEditPopulateFromExisting": "Populează din existente",
"ButtonBatchEditPopulateMapDetails": "Populează detaliile hărții",
"ButtonBrowseForFolder": "Caută un dosar",
"ButtonCancel": "Anulează",
"ButtonCancelEncode": "Anulare codificare",
"ButtonChangeRootPassword": "Schimbare parolă de root",
"ButtonCheckAndDownloadNewEpisodes": "Verifică și descarcă episoade noi",
"ButtonChooseAFolder": "Alege un dosar",
"ButtonChooseFiles": "Alege fișiere",
"ButtonClearFilter": "Șterge filtrul",
"ButtonClose": "Închide",
"ButtonCloseFeed": "Închide sursa",
"ButtonCloseSession": "Închide Sesiunea Curentă",
"ButtonCollections": "Colecții",
"ButtonConfigureScanner": "Configurare scaner",
"ButtonCreate": "Creează",
"ButtonCreateBackup": "Creează backup",
"ButtonDelete": "Șterge",
"ButtonDownloadQueue": "Coadă",
"ButtonEdit": "Editare",
"ButtonEditChapters": "Editare capitole",
"ButtonEditPodcast": "Editare podcast",
"ButtonEnable": "Activează",
"ButtonForceReScan": "Forțează rescanare",
"ButtonFullPath": "Calea completă",
"ButtonHide": "Ascunde",
"ButtonHome": "Acasă",
"ButtonIssues": "Probleme",
"ButtonJumpBackward": "Sari înapoi",
"ButtonJumpForward": "Sari înainte",
"ButtonIssues": "Erori",
"ButtonLatest": "Noutăți",
"ButtonLibrary": "Bibliotecă",
"ButtonLogout": "Deconectare",
"ButtonLookup": "Căutare",
"ButtonManageTracks": "Gestionează pista",
"ButtonMapChapterTitles": "Maparea titlurilor capitolelor",
"ButtonMatchAllAuthors": "Potriviește toți autorii",
"ButtonMatchBooks": "Potrivește Cărți",
"ButtonNevermind": "Anulează",
"ButtonNext": "Următorul",
"ButtonNextChapter": "Următorul Capitol",
"ButtonNextItemInQueue": "Următorul Articol în Coadă",
"ButtonOk": "OK",
"ButtonOpenFeed": "Vezi noutățile",
"ButtonOpenManager": "Deschide Managerul",
"ButtonPause": "Pauză",
"ButtonPlay": "Redă",
"ButtonPlayAll": "Redă tot",
"ButtonPlaying": "Redare",
"ButtonPlaylists": "Liste",
"ButtonPrevious": "Anterior",
"ButtonPreviousChapter": "Capitolul Anterior",
"ButtonProbeAudioFile": "Analizare Fișier Audio",
"ButtonPurgeAllCache": "Golire Cache Completă",
"ButtonPurgeItemsCache": "Golire Cache Articole",
"ButtonQueueAddItem": "Adaugă la Coadă",
"ButtonQueueRemoveItem": "Sterge din Coadă",
"ButtonQuickEmbed": "Încorporare Rapidă",
"ButtonQuickEmbedMetadata": "Metadate pentru Încorporare Rapidă",
"ButtonQuickMatch": "Potrivire Rapidă",
"ButtonReScan": "Rescanare",
"ButtonRead": "Citește",
"ButtonReadLess": "Citește Mai Puțin",
"ButtonReadLess": "Mai puțin",
"ButtonReadMore": "Afișează mai mult",
"ButtonRefresh": "Reîmprospătare",
"ButtonRemove": "Elimină",
"ButtonRemoveAll": "Eliminați Tot",
"ButtonRemoveAllLibraryItems": "Ștergerea tuturor Articolelor din Librărie",
"ButtonRemoveFromContinueListening": "Ștergere din \"Continuă să Asculți\"",
"ButtonRemoveFromContinueReading": "Ștergere din \"Continuă să citești\"",
"ButtonRemoveSeriesFromContinueSeries": "Ștergere Serie din \"Continuă Seria\"",
"ButtonReset": "Resetează",
"ButtonResetToDefault": "Resetează la valorile implicite",
"ButtonRestore": "Restaurare",
"ButtonSave": "Salvează",
"ButtonSaveAndClose": "Salvează și Închide",
"ButtonSaveTracklist": "Salvare Pistă",
"ButtonScan": "Scanează",
"ButtonScanLibrary": "Scanează Librăria",
"ButtonScrollLeft": "Derulează spre stânga",
"ButtonScrollRight": "Derulează spre Dreapta",
"ButtonSearch": "Caută",
"ButtonSelectFolderPath": "Selectează Calea către Dosar",
"ButtonSeries": "Serii",
"ButtonSetChaptersFromTracks": "Setează capitole din piste",
"ButtonShare": "Distribuie",
"ButtonShiftTimes": "Aliniează timpi",
"ButtonShow": "Arată",
"ButtonStartM4BEncode": "Începe Codarea M4B",
"ButtonStartMetadataEmbed": "Începe Încorporarea Metadatelor",
"ButtonStats": "Statistici",
"ButtonSubmit": "Trimite",
"ButtonTest": "Testează",
"ButtonUnlinkOpenId": "Deconectare OpenID",
"ButtonUpload": "Încarcă",
"ButtonUploadBackup": "Încarcă Backup",
"ButtonUploadCover": "Încarcă Copertă",
"ButtonUploadOPMLFile": "Încarcă Fișier OPML",
"ButtonUserDelete": "Șterge userul {0}",
"ButtonUserEdit": "Editează userul {0}",
"ButtonViewAll": "Vizualizează tot",
"ButtonYes": "Da",
"ErrorUploadFetchMetadataAPI": "Eroare în descărcarea metadatelor",
"ErrorUploadFetchMetadataNoResults": "Nu s-au putut prelua metadatele - încearcă să editezi titlul și/sau autorul",
"ErrorUploadLacksTitle": "Trebuie să aibă un titlu",
"HeaderAccount": "Cont",
"HeaderAddCustomMetadataProvider": "Adaugă Furnizor de Metadate Personalizat",
"HeaderAdvanced": "Avansat",
"HeaderApiKeys": "Chei API",
"HeaderAppriseNotificationSettings": "Setări Notificări Apprise",
"HeaderAudioTracks": "Înregistrări audio",
"HeaderAudiobookTools": "Instrumente pentru Gestionarea Fișierelor Audiobook",
"HeaderAuthentication": "Autentificare",
"HeaderBackups": "Copii de siguranță",
"HeaderBulkChapterModal": "Adaugă Multiple Capitole",
"HeaderChangePassword": "Schimbă Parola",
"HeaderChapters": "Capitole",
"HeaderChooseAFolder": "Alege Dosar",
"HeaderCollection": "Colecție",
"HeaderCollectionItems": "Conținutul colecției",
"HeaderCover": "Copertă",
"HeaderCurrentDownloads": "Descărcări Curente",
"HeaderCustomMessageOnLogin": "Mesaj Personalizat la Autentificare",
"HeaderCustomMetadataProviders": "Furnizor de Metadate Personalizat",
"HeaderDetails": "Detalii",
"HeaderDownloadQueue": "Coadă de Descărcare",
"HeaderEbookFiles": "Ebook-uri",
"HeaderEmail": "Email",
"HeaderEmailSettings": "Setări Email",
"HeaderEpisodes": "Episoade",
"HeaderEreaderDevices": "Dispozitive eReader",
"HeaderEreaderSettings": "Setări eReader",
"HeaderFiles": "Fișiere",
"HeaderFindChapters": "Caută Capitol",
"HeaderIgnoredFiles": "Fișiere Ignorate",
"HeaderItemFiles": "Fișiere Articol",
"HeaderLastListeningSession": "Ultima Sesiune de Ascultare",
"HeaderLatestEpisodes": "Episoade recente",
"HeaderLibraries": "Biblioteci",
"HeaderLibraryFiles": "Fișiere in Librărie",
"HeaderLibraryStats": "Statistici Librărie",
"HeaderListeningSessions": "Sesiuni de Ascultare",
"HeaderListeningStats": "Statistici Ascultare",
"HeaderLogin": "Autentifică",
"HeaderLogs": "Loguri",
"HeaderManageGenres": "Gestionează Genuri",
"HeaderManageTags": "Gestionează Etichete",
"HeaderMapDetails": "Detaliile Hărții",
"HeaderMatch": "Potrivește",
"HeaderMetadataOrderOfPrecedence": "Prioritatea Metadatelor",
"HeaderMetadataToEmbed": "Metadate pentru Încorporare",
"HeaderNewAccount": "Cont nou",
"HeaderNewApiKey": "Cheie API Nouă",
"HeaderNewLibrary": "Librărie Nouă",
"HeaderNotificationCreate": "Creează Notificare",
"HeaderNotificationUpdate": "Actualizare Notificare",
"HeaderNotifications": "Notificări",
"HeaderOpenIDConnectAuthentication": "Autentificare prin OpenID",
"HeaderOpenListeningSessions": "Deschide Sesiuni de Ascultare",
"HeaderOpenRSSFeed": "Deschide flux RSS",
"HeaderOtherFiles": "Alte Fișiere",
"HeaderPasswordAuthentication": "Autentificare cu Parolă",
"HeaderPermissions": "Permisiuni",
"HeaderPlayerQueue": "Coadă Player",
"HeaderPlayerSettings": "Setări Player",
"HeaderPlaylist": "Listă de redare",
"HeaderPlaylistItems": "Conținut listă",
"HeaderPodcastsToAdd": "Podcast de Adăugat",
"HeaderPresets": "Presetări",
"HeaderPreviewCover": "Previzualizare Copertă",
"HeaderRSSFeedGeneral": "Date RSS",
"HeaderRSSFeedIsOpen": "RSS activ",
"HeaderRSSFeeds": "Fluxuri RSS",
"HeaderRemoveEpisode": "Elimină Episod",
"HeaderRemoveEpisodes": "Elimină {0} Episoade",
"HeaderSavedMediaProgress": "Progres Media Salvat",
"HeaderSchedule": "Planifică",
"HeaderScheduleEpisodeDownloads": "Planifică Descărcare Automată a Episoadelor",
"HeaderScheduleLibraryScans": "Planifică Scanarea Automată a Librăriei",
"HeaderSession": "Sesiuni",
"HeaderSetBackupSchedule": "Planifică Backup",
"HeaderSettings": "Setări",
"HeaderSettingsDisplay": "Afișaj",
"HeaderSettingsExperimental": "Caracteristici Experimentale",
"HeaderSettingsGeneral": "General",
"HeaderSettingsScanner": "Scaner",
"HeaderSettingsSecurity": "Securitate",
"HeaderSettingsWebClient": "Client Web",
"HeaderSleepTimer": "Timer de somn",
"HeaderStatsLargestItems": "Cele mai mari articole",
"HeaderStatsLongestItems": "Cele mai lungi articole (ore)",
"HeaderStatsMinutesListeningChart": "Minute ascultate (ultimele 7 zile)",
"HeaderStatsRecentSessions": "Sesiuni recente",
"HeaderStatsTop10Authors": "Top 10 Autori",
"HeaderStatsTop5Genres": "Top 5 Genuri",
"HeaderTableOfContents": "Cuprins",
"HeaderTools": "Unelte",
"HeaderUpdateAccount": "Actualizare Cont",
"HeaderUpdateApiKey": "Actualizare Cheie API",
"HeaderUpdateAuthor": "Actualizare Autor",
"HeaderUpdateDetails": "Actualizare Detalii",
"HeaderUpdateLibrary": "Actualizare Librărie",
"HeaderUsers": "Utilizatori",
"HeaderYearReview": "Trecere în revistă a anului {0}",
"HeaderYourStats": "Progresul tău",
"LabelAbridged": "Abreviat",
"LabelAbridgedChecked": "Abreviat (verificat)",
"LabelAbridgedUnchecked": "Neprescurtat (neverificat)",
"LabelAccessibleBy": "Accesibil prin",
"LabelAccountType": "Tip de Cont",
"LabelAccountTypeAdmin": "Administrator",
"LabelAccountTypeGuest": "Oaspete",
"LabelAccountTypeUser": "Utilizator",
"LabelActivities": "Activități",
"LabelActivity": "Activitate",
"LabelAddToCollection": "Adaugă la Colecție",
"LabelAddToCollectionBatch": "Adaugare {0} Cărți la Colecție",
"LabelAddToPlaylist": "Adaugă în listă",
"LabelAddToPlaylistBatch": "Adaugare {0} Articole la Listă",
"LabelAddedAt": "Adăugat la",
"LabelAddedDate": "Adăugat {0}",
"LabelAdminUsersOnly": "Doar Administratori",
"LabelAll": "Toate",
"LabelAllEpisodesDownloaded": "Toate episoadele descărcate",
"LabelAllUsers": "Toți Utilizatorii",
"LabelAllUsersExcludingGuests": "Toți utilizatorii cu excepția oaspeților",
"LabelAllUsersIncludingGuests": "Toți utilizatorii inclusiv oaspeții",
"LabelAlreadyInYourLibrary": "Deja în bibliotecă",
"LabelApiKeyCreated": "Cheia API \"{0}\" creată cu succes.",
"LabelApiKeyCreatedDescription": "Copiază cheia API acum deoarece nu va mai fi disponibilă pentru vizualizare.",
"LabelApiKeyUser": "Acționează în numele utilizatorului",
"LabelApiKeyUserDescription": "Această cheie API va avea aceleași permisiuni ca utilizatorul în numele căruia acționează. In loguri va părea că utilizatorul lansa cererile.",
"LabelApiToken": "Token API",
"LabelAppend": "Atașează",
"LabelAudioBitrate": "Rata de Biți Audio (e.g. 128k)",
"LabelAudioChannels": "Canale Audio (1 sau 2)",
"LabelAudioCodec": "Codec Audio",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Prenume Nume)",
"LabelAuthorLastFirst": "Autor (Nume, Prenume)",
"LabelAuthors": "Autori",
"LabelAutoDownloadEpisodes": "Descarcă automat episoadele",
"LabelAutoFetchMetadata": "Descarcă Automat Metadate",
"LabelAutoFetchMetadataHelp": "Descarcă metadate pentru titlu, autor si serii pentru eficientizarea încărcării. Metadatele suplimentare s-ar putea să trebuiască potrivite după încărcare.",
"LabelAutoLaunch": "Lansare automată",
"LabelAutoLaunchDescription": "Redirecționează automat către furnizorul de autentificare când navighez la pagina de autentificare (cale de suprascriere manuală <code>/login?autoLaunch=0</code>)",
"LabelAutoRegister": "Înregistrare Automată",
"LabelAutoRegisterDescription": "Creează utilizatori automat dupa autentificare",
"LabelBackToUser": "Înapoi la Utilizator",
"LabelBackupAudioFiles": "Copii de rezervă a Fișierelor Audio",
"LabelBackupLocation": "Locația Copiilor de Rezervă",
"LabelBackupsEnableAutomaticBackups": "Copii de Rezervă Automate",
"LabelBackupsEnableAutomaticBackupsHelp": "Copiile de Rezervă au fost salvate în /metadata/backups",
"LabelBackupsMaxBackupSize": "Dimensiunea maximă a copiilor de rezervă (în GB) (0 pentru nelimitat)",
"LabelBackupsMaxBackupSizeHelp": "Ca protecție împotriva configurațiilor greșite, backup-ul va eșua dacă trece de limita de dimensiune configurată.",
"LabelBackupsNumberToKeep": "Numărul copiilor de siguranță de păstrat",
"LabelBackupsNumberToKeepHelp": "Doar 1 copie de siguranță va fi ștearsă odata deci dacă există mai multe copii de siguranță vor trebui șterse manual.",
"LabelBitrate": "Rată de biți",
"LabelBonus": "Bonus",
"LabelBooks": "Cărți",
"LabelButtonText": "Textul Butonului",
"LabelByAuthor": "de {0}",
"LabelChangePassword": "Schimbare Parolă",
"LabelChannels": "Canale",
"LabelChapterCount": "{0} Capitole",
"LabelChapterTitle": "Titlul Capitolului",
"LabelChapters": "Capitole",
"LabelChaptersFound": "capitole găsite",
"LabelClickForMoreInfo": "Click pentru mai multe informații",
"LabelClickToUseCurrentValue": "Click pentru a folosi valoarea curentă",
"LabelClosePlayer": "Închide playerul",
"LabelCodec": "Codec",
"LabelCollapseSeries": "Restrânge seriile",
"LabelCollapseSubSeries": "Restrânge Sub-Seriile",
"LabelCollection": "Colecție",
"LabelCollections": "Colecții",
"LabelComplete": "Finalizat",
"LabelConfirmPassword": "Confirmare Parolă",
"LabelContinueListening": "Ascultă în continuare",
"LabelContinueReading": "Continuă lectura",
"LabelContinueSeries": "Continuă seria",
"LabelCorsAllowed": "Origini CORS Permise",
"LabelCover": "Copertă",
"LabelCoverImageURL": "URL-ul Imaginii de Copertă",
"LabelCoverProvider": "Furnizor Copertă",
"LabelCreatedAt": "Creat la",
"LabelCronExpression": "Expresie Cron",
"LabelCurrent": "Curent",
"LabelCurrently": "Acum:",
"LabelCustomCronExpression": "Expresie Cron Personalizată:",
"LabelDatetime": "Data și ora",
"LabelDays": "Zile",
"LabelDeleteFromFileSystemCheckbox": "Șterge fișierele din sistem (debifeaza pentru a șterge doar din baza de date)",
"LabelDescription": "Descriere",
"LabelDeselectAll": "Deselectați Tot",
"LabelDetectedPattern": "Tipar Identificat:",
"LabelDevice": "Dispozitiv",
"LabelDeviceInfo": "Informații Dispozitiv",
"LabelDeviceIsAvailableTo": "Dispozitiv accesibil lui...",
"LabelDirectory": "Dosar",
"LabelDiscFromFilename": "Disc din Numele Fișierului",
"LabelDiscFromMetadata": "Disc din Metadate",
"LabelDiscover": "Descoperă",
"LabelDownload": "Descarcă",
"LabelDownloadNEpisodes": "Descarcă {0} episoade",
"LabelDownloadable": "Descărcabil",
"LabelDuration": "Durată",
"LabelDurationComparisonExactMatch": "(potrivire exactă)",
"LabelDurationComparisonLonger": "({0} mai lung)",
"LabelDurationComparisonShorter": "({0} mai scurt)",
"LabelDurationFound": "Durată identificată:",
"LabelEbook": "Carte electronică",
"LabelEbooks": "Cărți electronice",
"LabelEdit": "Editare",
"LabelEmail": "Email",
"LabelEmailSettingsFromAddress": "De la Adresa",
"LabelEmailSettingsRejectUnauthorized": "Respingere certificate neautorizate",
"LabelEmailSettingsRejectUnauthorizedHelp": "Dezactivarea verificării certificatelor SSL vă poate expune conexiunea la riscuri de securitate, cum ar fi atacuri de tip man-in-the-middle. Dezactivați această opțiune dacă înțelegeti implicațiile și aveți încredere în serverul de mail la care vă conectați.",
"LabelEmailSettingsSecure": "Sigur",
"LabelEmailSettingsSecureHelp": "Dacă e adevărat, conexiunea se va realiza prin TLS către server. Dacă e fals, TLS este folosit dacă serverul suporta extensia STARTTLS. În majoritatea cazurilor setati adevărat dacă folosiți portul 465. Pentru portul 587 sau 25 setati fals. (referinta nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Adresă de Test",
"LabelEmbeddedCover": "Încorporează Copertă",
"LabelEnable": "Activează",
"LabelEncodingBackupLocation": "O copie de siguranță a fișierului audio original va fi salvată în:",
"LabelEncodingChaptersNotEmbedded": "Capitolele nu sunt încorporate în cărțile audio cu mai multe track-uri.",
"LabelEncodingClearItemCache": "Asigurați-vă că ștergeți articolele din cache periodic.",
"LabelEncodingFinishedM4B": "Fișierul M4B va fi adaugat în dosarul dvs. de cărți audio când codificarea e terminată:",
"LabelEncodingInfoEmbedded": "Metadatele vor fi încorporate în fișierele audio din interiorul dosarului dvs. cu cărți audio.",
"LabelEncodingStartedNavigation": "Odată pornită sarcina poti naviga din această pagină.",
"LabelEncodingTimeWarning": "Codificarea poate dura până la 30 de minute.",
"LabelEncodingWarningAdvancedSettings": "Avertizare: Nu modificați aceste setări dacă nu sunteți familiar cu opțiunile de codare ffmpeg .",
"LabelEncodingWatcherDisabled": "Dacă ați dezactivat funcția de urmările va trebui sa rescanați acestă carte audio la ulterior.",
"LabelEnd": "Sfârșit",
"LabelEndOfChapter": "Sfârșitul capitolului",
"LabelEpisode": "Episod",
"LabelEpisodeNotLinkedToRssFeed": "Episoade nelegate de un flux RSS",
"LabelEpisodeNumber": "Episodul #{0}",
"LabelEpisodeTitle": "Titlul Episodului",
"LabelEpisodeType": "Tipul Episodului",
"LabelEpisodeUrlFromRssFeed": "URL-ul Episodului din Fluxul RSS",
"LabelEpisodes": "Episoade",
"LabelEpisodic": "Episodic",
"LabelExample": "Exemplu",
"LabelExpandSeries": "Extinde Seriile",
"LabelExpandSubSeries": "Extinde Sub-Seriile",
"LabelExpired": "Expirat",
"LabelExpiresAt": "Expiră La",
"LabelExpiresInSeconds": "Expiră în (secunde)",
"LabelExpiresNever": "Niciodată",
"LabelExplicit": "Explicit",
"LabelExplicitChecked": "Explicit (verificat)",
"LabelExplicitUnchecked": "Neexplicit (neverificat)",
"LabelExportOPML": "Exportă OPML",
"LabelFeedURL": "Flux URL",
"LabelFetchingMetadata": "Aducere Metadate",
"LabelFeedURL": "URL flux",
"LabelFile": "Fișier",
"LabelFileBirthtime": "Data creării fișierului",
"LabelFileBornDate": "Creat {0}",
"LabelFileModified": "Fișier modificat",
"LabelFileModifiedDate": "Modificat {0}",
"LabelFilename": "Nume fișier",
"LabelFilterByUser": "Filtrare după Utilizator",
"LabelFindEpisodes": "Găsire Episoade",
"LabelFinished": "Finalizat",
"LabelFinishedDate": "Finalizat {0}",
"LabelFolder": "Dosar",
"LabelFolders": "Dosare",
"LabelFontBold": "Îngroșat",
"LabelFontBoldness": "Grosimea fontului",
"LabelFontFamily": "Familia Fontului",
"LabelFontItalic": "Cursiv",
"LabelFontScale": "Mărimea fontului",
"LabelFontStrikethrough": "Tăiat cu o linie",
"LabelFormat": "Format",
"LabelFull": "Întreg",
"LabelGenre": "Gen",
"LabelGenres": "Genuri",
"LabelHardDeleteFile": "Ștergere definitivă a fișierului",
"LabelHasEbook": "Are carte electronică",
"LabelHasSupplementaryEbook": "Are carte electronică suplimentară",
"LabelHideSubtitles": "Ascunde Subtitrări",
"LabelHighestPriority": "Prioritatea cea mai ridicată",
"LabelHost": "Gazdă",
"LabelHour": "Ora",
"LabelHours": "Ore",
"LabelIcon": "Pictogramă",
"LabelImageURLFromTheWeb": "URL-ul imaginii de pe web",
"LabelInProgress": "În desfășurare",
"LabelIncludeInTracklist": "Include în Lista de Melodii",
"LabelIncomplete": "Incomplet",
"LabelInterval": "Interval",
"LabelIntervalCustomDailyWeekly": "Personalizat zilnic/saptămânal",
"LabelIntervalEvery12Hours": "La fiecare 12 ore",
"LabelIntervalEvery15Minutes": "La fiecare 15 minute",
"LabelIntervalEvery2Hours": "La fiecare 2 ore",
"LabelIntervalEvery30Minutes": "La fiecare 30 minute",
"LabelIntervalEvery6Hours": "La fiecare 6 ore",
"LabelIntervalEveryDay": "În fiecare zi",
"LabelIntervalEveryHour": "În fiecare oră",
"LabelIntervalEveryMinute": "La fiecare minut",
"LabelInvert": "Inversează",
"LabelItem": "Articol",
"LabelJumpBackwardAmount": "Sari înapoi cu",
"LabelJumpForwardAmount": "Sari înainte cu",
"LabelLanguage": "Limbă",
"LabelLanguageDefaultServer": "Limba Prestabilită a Serverului",
"LabelLanguages": "Limbi",
"LabelLastBookAdded": "Ultima Carte Adăugată",
"LabelLastBookUpdated": "Ultima Carte Actualizată",
"LabelLastProgressDate": "Ultimul progres: {0}",
"LabelLastSeen": "Ultima dată văzut",
"LabelLastTime": "Ultima dată",
"LabelLastUpdate": "Ultima actualizare",
"LabelLayout": "Aspect",
"LabelLayoutSinglePage": "Pagină unică",
"LabelLayoutSplitPage": "Pagină împărțită",
"LabelLess": "Mai puțin",
"LabelLibrariesAccessibleToUser": "Biblioteci Accesibile Utilizatorului",
"LabelLibrary": "Bibliotecă",
"LabelLibraryFilterSublistEmpty": "Numărul {0}",
"LabelLibraryItem": "Articol din Bibliotecă",
"LabelLibraryName": "Numele Bibliotecii",
"LabelLibrarySortByProgress": "Progres: Ultima Actualizare",
"LabelLibrarySortByProgressFinished": "Progres: Finalizat",
"LabelLibrarySortByProgressStarted": "Progres: Început",
"LabelLimit": "Limită",
"LabelLineSpacing": "Spațiere între rânduri",
"LabelListenAgain": "Ascultă din nou",
"LabelLogLevelDebug": "Depanare",
"LabelLogLevelInfo": "Informații",
"LabelLogLevelWarn": "Avertizare",
"LabelLookForNewEpisodesAfterDate": "Caută episoade noi după această dată",
"LabelLowestPriority": "Cea Mai Scăzută Prioritate",
"LabelMatchConfidence": "Încredere",
"LabelMatchExistingUsersBy": "Potrivire utilizatori existenți prin",
"LabelMatchExistingUsersByDescription": "Folosit pentru a conecta utilizatorii existenți. Odata conectați, utilizatorii vor fi potriviți după un ID unic trimis de furnizorul SSO",
"LabelMaxEpisodesToDownload": "Numarul maxim # de episoade de descărcat. Folosiți 0 pentru nelimitat.",
"LabelMaxEpisodesToDownloadPerCheck": "Numărul maxim # de episoade de descărcat per verificare",
"LabelMaxEpisodesToKeep": "Numarul maxim # de episoade păstrate",
"LabelMaxEpisodesToKeepHelp": "Valorea 0 nu stabilește o limită maximă. După ce un episod nou a fost descărcat automat, cel mai vechi episod va fi șters dacă aveți mai mult de X episoade. Se va șterge câte un episod vechi pentru fiecare episod nou descărcat.",
"LabelMediaPlayer": "Player Media",
"LabelMediaType": "Tip media",
"LabelMetaTag": "Etichetă Meta",
"LabelMetaTags": "Etichete Meta",
"LabelMetadataOrderOfPrecedenceDescription": "Sursele de metadate cu prioritate mai mare o să suprascrie sursele de metadate cu prioritate mai mică",
"LabelMetadataProvider": "Furnizor Metadate",
"LabelMinute": "Minut",
"LabelMinutes": "Minute",
"LabelMissing": "Lipsă",
"LabelMissingEbook": "Nu are carte electronică",
"LabelMissingSupplementaryEbook": "Nu are carte electronică adițională",
"LabelMobileRedirectURIs": "URL-uri de redirecționare Mobile Permise",
"LabelMobileRedirectURIsDescription": "Aceasta este o listă cu URI-uri valide pentru redirectionare a aplicațiilor mobile. URI-ul predefinit este <code>audiobookshelf://oauth</code>,, care poate fi sters sau suplimentat cu URI-uri adiționale pentru integrarea cu alte aplicații. Folosirea unui asterisc (<code>*</code>) ca singur element permite orice URI.",
"LabelMore": "Mai multe",
"LabelMoreInfo": "Mai multe informații",
"LabelName": "Nume",
"LabelNarrator": "Narator",
"LabelNarrators": "Naratori",
"LabelNew": "Nou",
"LabelNewPassword": "Parolă Nouă",
"LabelNewestAuthors": "Autori noi",
"LabelNewestEpisodes": "Episoade noi",
"LabelNextBackupDate": "Următoarea dată a copiilor de siguranță",
"LabelNextChapters": "Următoarele capitole vor fi:",
"LabelNextScheduledRun": "Urmatoarea rulare programată",
"LabelNoApiKeys": "Nu exista chei API",
"LabelNoCustomMetadataProviders": "Nu există furnizori de metadate personalizați",
"LabelNoEpisodesSelected": "Nici un episod selectat",
"LabelNotFinished": "Nefinalizat",
"LabelNotStarted": "Neînceput",
"LabelNotes": "Note",
"LabelNotificationAppriseURL": "URL-ul(urile) Apprise",
"LabelNotificationAvailableVariables": "Variabile disponibile",
"LabelNotificationBodyTemplate": "Corpul Șablonului",
"LabelNotificationEvent": "Eveniment de notificare",
"LabelNotificationTitleTemplate": "Titlul Șablonului",
"LabelNotificationsMaxFailedAttempts": "Număr de încercări eșuate maxim",
"LabelNotificationsMaxFailedAttemptsHelp": "Notificările sunt dezactivate dacă nu reușesc să fie trimise de acest număr de ori",
"LabelNotificationsMaxQueueSize": "Dimensiunea maximă a cozii pentru evenimentele de notificare",
"LabelNotificationsMaxQueueSizeHelp": "Evenimentele sunt limitate la 1 per secunda. Evenimentele vor fi ignorate dacă coada este plină. Previne spamarea cu notificări.",
"LabelNumberOfBooks": "Numărul de Cărți",
"LabelNumberOfChapters": "Număr de capitole:",
"LabelNumberOfEpisodes": "# de Episoade",
"LabelOpenIDAdvancedPermsClaimDescription": "Numele revendicării OpenID care conține permisiuni avansate pentru acțiunile utilizatorului în interiorul aplicației care vor fi aplicate rolurilor non-administrator (<b>dacă e configurat</b>). Dacă revendicarea nu e prezentă în răspunsul primit, accesul către ABS e refuzat. Dacă o singură opțiune lipsește, va fi tratată ca <code>falsă</code>. Asigurați-vă că revendicările furnizorului de securitate corespund structurii așteptate:",
"LabelOpenIDClaims": "Lăsați urmatoarele opțiuni goale pentru a dezactiva atribuirea avansată de grupuri și permisiuni, asignând grupul \"Utilizatori\" automat.",
"LabelOpenRSSFeed": "Flux Open RSS",
"LabelOverwrite": "Suprascrie",
"LabelPaginationPageXOfY": "Pagina {0} din {1}",
"LabelPassword": "Parolă",
"LabelPath": "Cale",
"LabelPermanent": "Permanent",
"LabelPermissionsAccessAllLibraries": "Poate accesa toate bibliotecile",
"LabelPermissionsAccessAllTags": "Poate accesa toate etichetele",
"LabelPermissionsAccessExplicitContent": "Poate Accesa Conținut Explicit",
"LabelPermissionsCreateEreader": "Poate Crea Cititoare Electronice",
"LabelPermissionsDelete": "Poate Șterge",
"LabelPermissionsDownload": "Poate Descărca",
"LabelPermissionsUpdate": "Poate Actualiza",
"LabelPermissionsUpload": "Poate Încărca",
"LabelPersonalYearReview": "Recapitularea Anului tău ({0})",
"LabelPhotoPathURL": "Calea/URL-ul Fotografiei",
"LabelPlayMethod": "Metoda de Redare",
"LabelPlaybackRateIncrementDecrement": "Incrementare/Decrementare a Ratei de Redare cu",
"LabelPlayerChapterNumberMarker": "{0} din {1}",
"LabelPlaylists": "Liste de redare",
"LabelPodcast": "Podcast",
"LabelPodcastSearchRegion": "Regiunea căutării podcastului",
"LabelPodcastType": "Tipul Podcastului",
"LabelPodcasts": "Podcasturi",
"LabelPort": "Portul",
"LabelPrefixesToIgnore": "Prefix de ignorat (fără a ține cont de majuscule)",
"LabelPreventIndexing": "Împiedică indexarea fluxului în directoarele iTunes și Google Podcasts",
"LabelPrimaryEbook": "eCarte Principală",
"LabelProgress": "Progres",
"LabelProvider": "Furnizor",
"LabelProviderAuthorizationValue": "Valoarea Antetului de Autorizare",
"LabelPubDate": "Data publicării",
"LabelPublishYear": "Anul publicării",
"LabelPublishedDate": "Publicat la {0}",
"LabelPublishedDecade": "Deceniul Publicării",
"LabelPublishedDecades": "Deceniile Publicării",
"LabelPublisher": "Editor",
"LabelPublishers": "Editori",
"LabelRSSFeedCustomOwnerEmail": "Email personalizat al proprietarului",
"LabelRSSFeedCustomOwnerName": "Nume personalizat al proprietarului",
"LabelRSSFeedOpen": "Flux RSS deschis",
"LabelRSSFeedPreventIndexing": "Previne indexarea",
"LabelRSSFeedSlug": "Identificator flux RSS",
"LabelRSSFeedURL": "URL-ul Fluxului RSS",
"LabelRandomly": "Aleatoriu",
"LabelReAddSeriesToContinueListening": "Readăugare serie la \"Continuă să asculți\"",
"LabelRead": "Citește",
"LabelReadAgain": "Citește din nou",
"LabelReadEbookWithoutProgress": "Citire eCarte fără a memora progresul",
"LabelRecentSeries": "Serii recente",
"LabelRecentlyAdded": "Adăugate recent",
"LabelRecommended": "Recomandat",
"LabelRedo": "Refă",
"LabelRegion": "Regiune",
"LabelReleaseDate": "Data Lansării",
"LabelRemoveAllMetadataAbs": "Ștergerea tuturor fișierelor metadata.abs",
"LabelRemoveAllMetadataJson": "Ștergerea tuturor fișierelor metadata.json",
"LabelRemoveAudibleBranding": "Ștergerea Audible intro și outro din capitole",
"LabelRemoveCover": "Șterge coperta",
"LabelRemoveMetadataFile": "Șterge fisierele metadate din dosarele bibliotecii",
"LabelRemoveMetadataFileHelp": "Șterge toate fișierele metadata.json și metadata.abs din {0} dosare.",
"LabelRowsPerPage": "Rânduri pe pagină",
"LabelSearchTerm": "Termen de căutat",
"LabelSearchTitle": "Titlu de căutat",
"LabelSearchTitleOrASIN": "Titlu de căutat sau ASN",
"LabelSeason": "Sezon",
"LabelSeasonNumber": "Sezonul #{0}",
"LabelSelectAll": "Selectează tot",
"LabelSelectAllEpisodes": "Selectează toate episoadele",
"LabelSelectEpisodesShowing": "Selectează {0} episoade dintre cele afișate",
"LabelSelectUser": "Selectare utilizator",
"LabelSelectUsers": "Selectare utilizatori",
"LabelSendEbookToDevice": "Trimite eCarte către...",
"LabelSequence": "Secvență",
"LabelSerial": "Serie",
"LabelSeries": "Serii",
"LabelSeriesName": "Numele Seriilor",
"LabelSeriesProgress": "Progresul Seriilor",
"LabelServerLogLevel": "Nivelul de Jurnal al Serverului",
"LabelServerYearReview": "Anul Serverului în Retrospectivă ({0})",
"LabelSetEbookAsPrimary": "Setează ca principală",
"LabelSetEbookAsSupplementary": "Setează ca suplimentară",
"LabelSettingsAllowIframe": "Permite încorporarea intr-un iframe",
"LabelSettingsAudiobooksOnly": "Doar cărți audio",
"LabelSettingsAudiobooksOnlyHelp": "Activarea acestei set[ri va ignora fișierele eBook daca acestea nu se află într-un dosar al unei cărți audio, caz în care vor fi setate ca eBook suplimentar",
"LabelSettingsBookshelfViewHelp": "Design scheumorf cu rafturi de lemn",
"LabelSettingsChromecastSupport": "Suport Chromecast",
"LabelSettingsDateFormat": "Formatul Datei",
"LabelSettingsEnableWatcher": "Urmărește în mod automat bibliotecile pentru schimbări",
"LabelSettingsEnableWatcherForLibrary": "Urmărește în mod automat biblioteca pentru schimbări",
"LabelShowAll": "Afișează tot",
"LabelSize": "Dimensiune",
"LabelSleepTimer": "Timer de somn",

View file

@ -23,7 +23,7 @@
"ButtonClearFilter": "Zrušiť filter",
"ButtonClose": "Uzavrieť",
"ButtonCloseFeed": "Zatvoriť zdroj",
"ButtonCloseSession": "Ukončiť aktívne relácie",
"ButtonCloseSession": "Ukončiť otvorené pripojenie",
"ButtonCollections": "Kolekcie",
"ButtonConfigureScanner": "Nastaviť skener",
"ButtonCreate": "Vytvoriť",
@ -150,12 +150,12 @@
"HeaderIgnoredFiles": "Ignorované súbory",
"HeaderItemFiles": "Položka Súbory",
"HeaderItemMetadataUtils": "Položka Nástroje metadát",
"HeaderLastListeningSession": "Posledná relácia",
"HeaderLastListeningSession": "Posledné pripojenie",
"HeaderLatestEpisodes": "Posledné epizódy",
"HeaderLibraries": "Knižnice",
"HeaderLibraryFiles": "Súbory knižnice",
"HeaderLibraryStats": "Štatistiky knižnice",
"HeaderListeningSessions": "Relácie",
"HeaderListeningSessions": "Pripojenia",
"HeaderListeningStats": "Štatistiky počúvania",
"HeaderLogin": "Prihlásenie",
"HeaderLogs": "Záznamy udalostí",
@ -172,7 +172,7 @@
"HeaderNotificationUpdate": "Aktualizovať notifikáciu",
"HeaderNotifications": "Notifikácie",
"HeaderOpenIDConnectAuthentication": "Overenie pripojenia OpenID",
"HeaderOpenListeningSessions": "Aktívne relácie",
"HeaderOpenListeningSessions": "Aktívne pripojenia",
"HeaderOpenRSSFeed": "Otvoriť RSS zdroj",
"HeaderOtherFiles": "Ostatné súbory",
"HeaderPasswordAuthentication": "Overenie heslom",
@ -189,7 +189,7 @@
"HeaderRSSFeeds": "RSS zdroje",
"HeaderRemoveEpisode": "Odstrániť epizódu",
"HeaderRemoveEpisodes": "Odstrániť {0} epizód",
"HeaderSavedMediaProgress": "Stav uložených médií",
"HeaderSavedMediaProgress": "Priebeh uložených médií",
"HeaderSchedule": "Plán",
"HeaderScheduleEpisodeDownloads": "Naplánovať automatické sťahovanie epizód",
"HeaderScheduleLibraryScans": "Naplánovanovať automatické skenovanie knižnice",
@ -222,7 +222,7 @@
"LabelAbridged": "Skrátená verzia",
"LabelAbridgedChecked": "Skrátená verzia (zaškrtnuté)",
"LabelAbridgedUnchecked": "Neskrátená verzia (nezaškrtnuté)",
"LabelAccessibleBy": "Dostupné pre",
"LabelAccessibleBy": "Prístupné pre",
"LabelAccountType": "Typ účtu",
"LabelAccountTypeAdmin": "Administrátor",
"LabelAccountTypeGuest": "Hosť",
@ -230,7 +230,7 @@
"LabelActivities": "Aktivity",
"LabelActivity": "Aktivita",
"LabelAddToCollection": "Pridať do zbierky",
"LabelAddToCollectionBatch": "Pridať {0} kníh do zbierky",
"LabelAddToCollectionBatch": "Pridať {0} kníh do kolekcie",
"LabelAddToPlaylist": "Pridať do playlistu",
"LabelAddToPlaylistBatch": "Pridať {0} položie do playlistu",
"LabelAddedAt": "Pridané",
@ -288,8 +288,8 @@
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Zbaliť série",
"LabelCollapseSubSeries": "Zbaliť podsérie",
"LabelCollection": "Zbierka",
"LabelCollections": "Zbierky",
"LabelCollection": "Kolekcia",
"LabelCollections": "Kolekcie",
"LabelComplete": "Hotovo",
"LabelConfirmPassword": "Potvrdiť heslo",
"LabelContinueListening": "Pokračovať v počúvaní",
@ -383,7 +383,7 @@
"LabelFolders": "Priečinky",
"LabelFontBold": "Tučné",
"LabelFontBoldness": "Hrúbka písma",
"LabelFontFamily": "písmo",
"LabelFontFamily": "Písmo",
"LabelFontItalic": "Kurzíva",
"LabelFontScale": "Veľkosť písma",
"LabelFontStrikethrough": "Preškrtnuté",
@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Žiadne {0}",
"LabelLibraryItem": "Položka knižnice",
"LabelLibraryName": "Názov knižnice",
"LabelLibrarySortByProgress": "Stav: Naposledy aktualizované",
"LabelLibrarySortByProgressFinished": "Stav: Dokončené",
"LabelLibrarySortByProgressStarted": "Stav: Začal",
"LabelLibrarySortByProgress": "Pokrok: Aktualizované",
"LabelLibrarySortByProgressFinished": "Pokrok: Dokončené",
"LabelLibrarySortByProgressStarted": "Pokrok: Začiatok",
"LabelLimit": "Limit",
"LabelLineSpacing": "Riadkovanie",
"LabelListenAgain": "Počúvať znova",
@ -528,7 +528,7 @@
"LabelPrefixesToIgnore": "Ignorované predpony (bez ohľadu na veľkosť písmen)",
"LabelPreventIndexing": "Zabráni indexácii vašich zdrojov službami iTunes a Google podcast directories",
"LabelPrimaryEbook": "Primárny e-book",
"LabelProgress": "Aktuálny stav",
"LabelProgress": "Stav",
"LabelProvider": "Poskytovateľ",
"LabelProviderAuthorizationValue": "Obsah hlavičky autorizácie",
"LabelPubDate": "Dátum publikovania",
@ -548,7 +548,7 @@
"LabelReAddSeriesToContinueListening": "Znova pridať série do pokračujúceho počúvania",
"LabelRead": "Načítať",
"LabelReadAgain": "Čítať znova",
"LabelReadEbookWithoutProgress": "Čítať e-knihu bez zmeny stavu",
"LabelReadEbookWithoutProgress": "Čítať e-knihu bez sledovania pokroku",
"LabelRecentSeries": "Posledné série",
"LabelRecentlyAdded": "Posledné pridané",
"LabelRecommended": "Odporúčané",
@ -577,7 +577,7 @@
"LabelSerial": "Na pokračovanie",
"LabelSeries": "Série",
"LabelSeriesName": "Názov série",
"LabelSeriesProgress": "Aktuálny stav série",
"LabelSeriesProgress": "Pokrok série",
"LabelServerLogLevel": "Úroveň logovania servera",
"LabelServerYearReview": "Rok servera v prehľade ({0})",
"LabelSetEbookAsPrimary": "Nastaviť ako primárny",
@ -1015,7 +1015,7 @@
"ToastCachePurgeFailed": "Vyčistenie vyrovnávacej pamäte zlyhalo",
"ToastCachePurgeSuccess": "Vyrovnávacia pamäť vyčistená",
"ToastChapterLocked": "Kapitola je zamknutá.",
"ToastChapterStartTimeAdjusted": "Čas začiatku kapitoly upravený o {0} sekúnd",
"ToastChapterStartTimeAdjusted": "Čas začiatku kapitoly upravený o {0} sek.",
"ToastChaptersAllLocked": "Všetky kapitoly sú zamknuté. Odomknite niektoré kapitoly, aby ste posunuli ich časy.",
"ToastChaptersHaveErrors": "Kapitoly obsahujú chyby",
"ToastChaptersInvalidShiftAmountLast": "Neplatná hodnota veľkosti posunutia. Začiatok poslednej kapitoly by ležal za koncom audioknihy.",
@ -1026,7 +1026,7 @@
"ToastCollectionItemsAddFailed": "Pridanie položky/-iek do zbierky zlyhalo",
"ToastCollectionRemoveSuccess": "Zbierka odstránená",
"ToastCollectionUpdateSuccess": "Zbierka aktualizovaná",
"ToastConnectionNotAvailable": "Pripojenie je nedostupné. Skúste to neskôr",
"ToastConnectionNotAvailable": "Pripojenie je nedostupné. Skúste to neskôr.",
"ToastCoverSearchFailed": "Vyhľadanie obalu sa nepodarilo",
"ToastCoverUpdateFailed": "Aktualizácia prebalu zlyhala",
"ToastDateTimeInvalidOrIncomplete": "Dátum a čas sú neplatné alebo neúplné",

View file

@ -104,7 +104,7 @@
"ButtonStartM4BEncode": "Zaženi M4B prekodiranje",
"ButtonStartMetadataEmbed": "Začni vdelavo metapodatkov",
"ButtonStats": "Statistika",
"ButtonSubmit": "Pošlji",
"ButtonSubmit": "Potrdi",
"ButtonTest": "Test",
"ButtonUnlinkOpenId": "Prekini povezavo OpenID",
"ButtonUpload": "Naloži",
@ -436,9 +436,9 @@
"LabelLibraryFilterSublistEmpty": "Ne {0}",
"LabelLibraryItem": "Element knjižnice",
"LabelLibraryName": "Ime knjižnice",
"LabelLibrarySortByProgress": "Napredek: Zadnja posodobitev",
"LabelLibrarySortByProgressFinished": "Napredek: Končano",
"LabelLibrarySortByProgressStarted": "Napredek: Začelo se je",
"LabelLibrarySortByProgress": "Napredek: Nazadnje posodobljen",
"LabelLibrarySortByProgressFinished": "Napredej: Končano",
"LabelLibrarySortByProgressStarted": "Napredek: Začeto",
"LabelLimit": "Omejitev",
"LabelLineSpacing": "Vrstični razmak",
"LabelListenAgain": "Poslušaj znova",

View file

@ -1,6 +1,6 @@
{
"ButtonAdd": "Lägg till",
"ButtonAddApiKey": "Lägg till API-nyckel",
"ButtonAddApiKey": "Addera API-nyckel",
"ButtonAddChapters": "Lägg till kapitel",
"ButtonAddDevice": "Lägg till enhet",
"ButtonAddLibrary": "Lägg till bibliotek",
@ -48,7 +48,7 @@
"ButtonLogout": "Logga ut",
"ButtonLookup": "Sök",
"ButtonManageTracks": "Hantera spår",
"ButtonMapChapterTitles": "Mappa kapitelrubriker",
"ButtonMapChapterTitles": "Karta kapitelrubriker",
"ButtonMatchAllAuthors": "Matcha alla författare",
"ButtonMatchBooks": "Matcha böcker",
"ButtonNevermind": "Glöm det",
@ -104,7 +104,7 @@
"ButtonStartM4BEncode": "Starta M4B-omkodning",
"ButtonStartMetadataEmbed": "Infoga metadata",
"ButtonStats": "Statistik",
"ButtonSubmit": "Skicka",
"ButtonSubmit": "Spara",
"ButtonTest": "Testa",
"ButtonUnlinkOpenId": "Koppla ifrån OpenID",
"ButtonUpload": "Ladda upp",
@ -123,7 +123,7 @@
"HeaderAdvanced": "Avancerad",
"HeaderApiKeys": "API-nyckel",
"HeaderAppriseNotificationSettings": "Inställningar av meddelanden med Apprise",
"HeaderAudioTracks": "Ljudspår",
"HeaderAudioTracks": "Ljudfiler",
"HeaderAudiobookTools": "Hantering av ljudboksfiler",
"HeaderAuthentication": "Autentisering",
"HeaderBackups": "Säkerhetskopior",
@ -202,7 +202,7 @@
"HeaderSettingsScanner": "Skanner",
"HeaderSettingsSecurity": "Säkerhet",
"HeaderSettingsWebClient": "Webklient",
"HeaderSleepTimer": "Insomningstimer",
"HeaderSleepTimer": "Timer för att sova",
"HeaderStatsLargestItems": "Största objekten",
"HeaderStatsLongestItems": "Längsta objekten (timmar)",
"HeaderStatsMinutesListeningChart": "Minuters lyssning (senaste 7 dagarna)",
@ -231,10 +231,10 @@
"LabelActivity": "Aktivitet",
"LabelAddToCollection": "Lägg till i en samling",
"LabelAddToCollectionBatch": "Lägg till {0} böcker i samlingen",
"LabelAddToPlaylist": "Lägg till i spellista",
"LabelAddToPlaylist": "Lägg till i en spellista",
"LabelAddToPlaylistBatch": "Lägg till {0} objekt i Spellistan",
"LabelAddedAt": "Datum adderad",
"LabelAddedDate": "Tillagd {0}",
"LabelAddedDate": "Adderad {0}",
"LabelAdminUsersOnly": "Endast administratörer",
"LabelAll": "Alla",
"LabelAllEpisodesDownloaded": "Alla avsnitt är nedladdade",
@ -363,14 +363,14 @@
"LabelExpiresAt": "Gäller till och med",
"LabelExpiresInSeconds": "Upphör om (sekunder)",
"LabelExpiresNever": "Aldrig",
"LabelExplicit": "Vuxeninnehåll",
"LabelExplicit": "Bestämd",
"LabelExplicitChecked": "Explicit version (markerad)",
"LabelExplicitUnchecked": "Ej Explicit version (ej markerad)",
"LabelExportOPML": "Exportera OPML-information",
"LabelFeedURL": "URL-adress för flödet",
"LabelFetchingMetadata": "Hämtar metadata",
"LabelFile": "Fil",
"LabelFileBirthtime": "Tidpunkt, tillagd",
"LabelFileBirthtime": "Tidpunkt, adderad",
"LabelFileBornDate": "Skapad {0}",
"LabelFileModified": "Tidpunkt, ändrad",
"LabelFileModifiedDate": "Ändrad {0}",
@ -629,7 +629,7 @@
"LabelShowSeconds": "Visa i sekunder",
"LabelShowSubtitles": "Visa underrubriker",
"LabelSize": "Storlek",
"LabelSleepTimer": "Insomningstimer",
"LabelSleepTimer": "Sovtimer",
"LabelSlug": "Kortnamn",
"LabelSortAscending": "Stigande",
"LabelSortDescending": "Fallande",

View file

@ -63,7 +63,7 @@ class MeController {
* @param {Response} res
*/
async getItemListeningSessions(req, res) {
const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.libraryItemId)
const libraryItem = await Database.libraryItemModel.findByPk(req.params.libraryItemId)
const episode = await Database.podcastEpisodeModel.findByPk(req.params.episodeId)
if (!libraryItem || (libraryItem.isPodcast && !episode)) {
@ -71,12 +71,6 @@ class MeController {
return res.sendStatus(404)
}
// Check if user has access to this library item
if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
Logger.error(`[MeController] User "${req.user.username}" attempted to access listening sessions for library item "${req.params.libraryItemId}" without access`)
return res.sendStatus(403)
}
const mediaItemId = episode?.id || libraryItem.mediaId
let listeningSessions = await this.getUserItemListeningSessionsHelper(req.user.id, mediaItemId)
@ -131,13 +125,6 @@ class MeController {
* @param {Response} res
*/
async removeMediaProgress(req, res) {
// Verify the media progress belongs to the current user
const mediaProgress = req.user.mediaProgresses.find((mp) => mp.id === req.params.id)
if (!mediaProgress) {
Logger.error(`[MeController] Media progress not found or does not belong to user "${req.user.username}"`)
return res.sendStatus(404)
}
await Database.mediaProgressModel.removeById(req.params.id)
req.user.mediaProgresses = req.user.mediaProgresses.filter((mp) => mp.id !== req.params.id)
@ -205,16 +192,7 @@ class MeController {
* @param {Response} res
*/
async createBookmark(req, res) {
const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
if (!libraryItem) {
return res.sendStatus(404)
}
// Check if user has access to this library item
if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
Logger.error(`[MeController] User "${req.user.username}" attempted to create bookmark for library item "${req.params.id}" without access`)
return res.sendStatus(403)
}
if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const { time, title } = req.body
if (isNullOrNaN(time)) {
@ -238,16 +216,7 @@ class MeController {
* @param {Response} res
*/
async updateBookmark(req, res) {
const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
if (!libraryItem) {
return res.sendStatus(404)
}
// Check if user has access to this library item
if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
Logger.error(`[MeController] User "${req.user.username}" attempted to update bookmark for library item "${req.params.id}" without access`)
return res.sendStatus(403)
}
if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const { time, title } = req.body
if (isNullOrNaN(time)) {
@ -276,16 +245,7 @@ class MeController {
* @param {Response} res
*/
async removeBookmark(req, res) {
const libraryItem = await Database.libraryItemModel.getExpandedById(req.params.id)
if (!libraryItem) {
return res.sendStatus(404)
}
// Check if user has access to this library item
if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
Logger.error(`[MeController] User "${req.user.username}" attempted to remove bookmark for library item "${req.params.id}" without access`)
return res.sendStatus(403)
}
if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const time = Number(req.params.time)
if (isNaN(time)) {

View file

@ -227,7 +227,7 @@ class BookFinder {
title = this.#removeAuthorFromTitle(title)
const titleTransformers = [
[/(: |[,;_]| by ).*/g, ''], // Remove subtitle
[/([,:;_]| by ).*/g, ''], // Remove subtitle
[/(^| )\d+k(bps)?( |$)/, ' '], // Remove bitrate
[/ (2nd|3rd|\d+th)\s+ed(\.|ition)?/g, ''], // Remove edition
[/(^| |\.)(m4b|m4a|mp3)( |$)/g, ''], // Remove file-type
@ -646,11 +646,11 @@ class BookFinder {
module.exports = new BookFinder()
function hasSubtitle(title) {
return title.includes(': ') || title.includes(' - ')
return title.includes(':') || title.includes(' - ')
}
function stripSubtitle(title) {
if (title.includes(': ')) {
return title.split(': ')[0].trim()
if (title.includes(':')) {
return title.split(':')[0].trim()
} else if (title.includes(' - ')) {
return title.split(' - ')[0].trim()
}

View file

@ -22,7 +22,7 @@ function parseNfoMetadata(nfoText) {
switch (key) {
case 'title':
{
const titleMatch = value.match(/^(.*?): (.*)$/)
const titleMatch = value.match(/^(.*?):(.*)$/)
if (titleMatch) {
metadata.title = titleMatch[1].trim()
metadata.subtitle = titleMatch[2].trim()

View file

@ -1,636 +0,0 @@
const { expect } = require('chai')
const { Sequelize } = require('sequelize')
const sinon = require('sinon')
const Database = require('../../../server/Database')
const ApiRouter = require('../../../server/routers/ApiRouter')
const MeController = require('../../../server/controllers/MeController')
const Auth = require('../../../server/Auth')
const Logger = require('../../../server/Logger')
const SocketAuthority = require('../../../server/SocketAuthority')
describe('MeController - IDOR Security Tests', () => {
/** @type {ApiRouter} */
let apiRouter
beforeEach(async () => {
global.ServerSettings = {}
Database.sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
Database.sequelize.uppercaseFirst = (str) => (str ? `${str[0].toUpperCase()}${str.substr(1)}` : '')
await Database.buildModels()
// Create mock server object with required dependencies
const mockServer = {
auth: new Auth(),
playbackSessionManager: { sessions: [] },
abMergeManager: {},
backupManager: {},
podcastManager: {},
audioMetadataManager: {},
cronManager: {},
emailManager: {},
apiCacheManager: { middleware: (req, res, next) => next() }
}
apiRouter = new ApiRouter(mockServer)
sinon.stub(Logger, 'info')
sinon.stub(Logger, 'error')
sinon.stub(SocketAuthority, 'clientEmitter')
})
afterEach(async () => {
sinon.restore()
// Clear all tables
await Database.sequelize.sync({ force: true })
})
describe('removeMediaProgress - IDOR Protection', () => {
let user1, user2
let mediaProgress1, mediaProgress2
beforeEach(async () => {
// Create two users
user1 = await Database.userModel.create({
username: 'user1',
pash: 'hashed_password_1',
type: 'user',
isActive: true
})
user2 = await Database.userModel.create({
username: 'user2',
pash: 'hashed_password_2',
type: 'user',
isActive: true
})
// Create library and book
const library = await Database.libraryModel.create({ name: 'Test Library', mediaType: 'book' })
const libraryFolder = await Database.libraryFolderModel.create({ path: '/test', libraryId: library.id })
const book = await Database.bookModel.create({ title: 'Test Book', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
const libraryItem = await Database.libraryItemModel.create({
libraryFiles: [],
mediaId: book.id,
mediaType: 'book',
libraryId: library.id,
libraryFolderId: libraryFolder.id
})
// Create media progress for each user
mediaProgress1 = await Database.mediaProgressModel.create({
userId: user1.id,
mediaItemId: book.id,
mediaItemType: 'book',
duration: 1000,
currentTime: 500,
isFinished: false
})
mediaProgress2 = await Database.mediaProgressModel.create({
userId: user2.id,
mediaItemId: book.id,
mediaItemType: 'book',
duration: 1000,
currentTime: 300,
isFinished: false
})
// Load media progresses into users
user1.mediaProgresses = await user1.getMediaProgresses()
user2.mediaProgresses = await user2.getMediaProgresses()
})
it('should allow user to delete their own media progress', async () => {
const fakeReq = {
user: user1,
params: { id: mediaProgress1.id }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
await MeController.removeMediaProgress(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(200)).to.be.true
// Verify media progress was deleted
const deletedProgress = await Database.mediaProgressModel.findByPk(mediaProgress1.id)
expect(deletedProgress).to.be.null
})
it('should prevent user from deleting another users media progress (IDOR)', async () => {
const fakeReq = {
user: user1,
params: { id: mediaProgress2.id } // Trying to delete user2's progress
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
await MeController.removeMediaProgress(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(404)).to.be.true
// Verify media progress was NOT deleted
const existingProgress = await Database.mediaProgressModel.findByPk(mediaProgress2.id)
expect(existingProgress).to.not.be.null
expect(existingProgress.userId).to.equal(user2.id)
})
it('should return 404 for non-existent media progress', async () => {
const fakeReq = {
user: user1,
params: { id: 'non-existent-id' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
await MeController.removeMediaProgress(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(404)).to.be.true
})
})
describe('Bookmark Operations - Authorization Checks', () => {
let user1, user2
let library1, library2
let libraryItem1, libraryItem2
beforeEach(async () => {
// Create two users with different library access
user1 = await Database.userModel.create({
username: 'user1',
pash: 'hashed_password_1',
type: 'user',
isActive: true,
librariesAccessible: null // Access to all libraries
})
user2 = await Database.userModel.create({
username: 'user2',
pash: 'hashed_password_2',
type: 'user',
isActive: true,
librariesAccessible: [] // Will be set to specific library
})
// Create two libraries
library1 = await Database.libraryModel.create({ name: 'Library 1', mediaType: 'book' })
library2 = await Database.libraryModel.create({ name: 'Library 2', mediaType: 'book' })
// User2 only has access to library1
user2.librariesAccessible = [library1.id]
await user2.save()
const libraryFolder1 = await Database.libraryFolderModel.create({ path: '/test1', libraryId: library1.id })
const libraryFolder2 = await Database.libraryFolderModel.create({ path: '/test2', libraryId: library2.id })
const book1 = await Database.bookModel.create({ title: 'Book 1', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
const book2 = await Database.bookModel.create({ title: 'Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
libraryItem1 = await Database.libraryItemModel.create({
libraryFiles: [],
mediaId: book1.id,
mediaType: 'book',
libraryId: library1.id,
libraryFolderId: libraryFolder1.id
})
libraryItem2 = await Database.libraryItemModel.create({
libraryFiles: [],
mediaId: book2.id,
mediaType: 'book',
libraryId: library2.id,
libraryFolderId: libraryFolder2.id
})
// Initialize bookmarks
user1.bookmarks = []
user2.bookmarks = []
})
describe('createBookmark', () => {
it('should allow user to create bookmark for accessible library item', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
const bookmark = { libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark', createdAt: Date.now() }
const fakeReq = {
user: {
...user2.toJSON(),
id: user2.id,
username: user2.username,
checkCanAccessLibraryItem: () => true,
createBookmark: sinon.stub().resolves(bookmark),
toOldJSONForBrowser: () => ({ id: user2.id, username: user2.username })
},
params: { id: libraryItem1.id },
body: { time: 100, title: 'Test Bookmark' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.createBookmark(fakeReq, fakeRes)
expect(fakeRes.json.calledOnce).to.be.true
expect(fakeRes.json.calledWith(bookmark)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
it('should prevent user from creating bookmark for inaccessible library item (IDOR)', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
const fakeReq = {
user: user2, // user2 doesn't have access to library2
params: { id: libraryItem2.id },
body: { time: 100, title: 'Test Bookmark' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
// Mock getExpandedById
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.createBookmark(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(403)).to.be.true
expect(fakeRes.json.called).to.be.false
Database.libraryItemModel.getExpandedById.restore()
})
it('should return 404 for non-existent library item', async () => {
const fakeReq = {
user: user1,
params: { id: 'non-existent-id' },
body: { time: 100, title: 'Test Bookmark' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
// Mock getExpandedById to return null
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(null)
await MeController.createBookmark(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(404)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
it('should validate bookmark time parameter', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
const fakeReq = {
user: {
...user1.toJSON(),
id: user1.id,
username: user1.username,
checkCanAccessLibraryItem: () => true
},
params: { id: libraryItem1.id },
body: { time: null, title: 'Test Bookmark' } // null time is invalid
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.createBookmark(fakeReq, fakeRes)
expect(fakeRes.status.calledWith(400)).to.be.true
expect(fakeRes.send.calledWith('Invalid time')).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
})
describe('updateBookmark', () => {
beforeEach(async () => {
// Add existing bookmark to user1
user1.bookmarks = [{ libraryItemId: libraryItem1.id, time: 100, title: 'Original Title' }]
await user1.save()
})
it('should allow user to update bookmark for accessible library item', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
const bookmark = { libraryItemId: libraryItem1.id, time: 100, title: 'Updated Title' }
const fakeReq = {
user: {
...user1.toJSON(),
id: user1.id,
username: user1.username,
checkCanAccessLibraryItem: () => true,
updateBookmark: sinon.stub().resolves(bookmark),
toOldJSONForBrowser: () => ({ id: user1.id, username: user1.username })
},
params: { id: libraryItem1.id },
body: { time: 100, title: 'Updated Title' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.updateBookmark(fakeReq, fakeRes)
expect(fakeRes.json.calledOnce).to.be.true
expect(fakeRes.json.calledWith(bookmark)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
it('should prevent user from updating bookmark for inaccessible library item (IDOR)', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
const fakeReq = {
user: user2, // user2 doesn't have access to library2
params: { id: libraryItem2.id },
body: { time: 100, title: 'Updated Title' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.updateBookmark(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(403)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
})
describe('removeBookmark', () => {
beforeEach(async () => {
// Add existing bookmark to user1
user1.bookmarks = [{ libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark' }]
await user1.save()
})
it('should allow user to remove bookmark for accessible library item', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
const fakeReq = {
user: {
...user1.toJSON(),
id: user1.id,
username: user1.username,
checkCanAccessLibraryItem: () => true,
findBookmark: sinon.stub().returns({ libraryItemId: libraryItem1.id, time: 100, title: 'Test Bookmark' }),
removeBookmark: sinon.stub().resolves(true),
toOldJSONForBrowser: () => ({ id: user1.id, username: user1.username })
},
params: { id: libraryItem1.id, time: '100' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.removeBookmark(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(200)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
it('should prevent user from removing bookmark for inaccessible library item (IDOR)', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
const fakeReq = {
user: user2, // user2 doesn't have access to library2
params: { id: libraryItem2.id, time: '100' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.removeBookmark(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(403)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
it('should validate time parameter is a number', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
const fakeReq = {
user: {
...user1.toJSON(),
id: user1.id,
username: user1.username,
checkCanAccessLibraryItem: () => true
},
params: { id: libraryItem1.id, time: 'not-a-number' }
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
await MeController.removeBookmark(fakeReq, fakeRes)
expect(fakeRes.status.calledWith(400)).to.be.true
expect(fakeRes.send.calledWith('Invalid time')).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
})
})
describe('getItemListeningSessions - Authorization Check', () => {
let user1, user2
let library1, library2
let libraryItem1, libraryItem2
beforeEach(async () => {
// Create two users with different library access
user1 = await Database.userModel.create({
username: 'user1',
pash: 'hashed_password_1',
type: 'user',
isActive: true,
librariesAccessible: null // Access to all libraries
})
user2 = await Database.userModel.create({
username: 'user2',
pash: 'hashed_password_2',
type: 'user',
isActive: true,
librariesAccessible: [] // Will be set to specific library
})
// Create two libraries
library1 = await Database.libraryModel.create({ name: 'Library 1', mediaType: 'book' })
library2 = await Database.libraryModel.create({ name: 'Library 2', mediaType: 'book' })
// User2 only has access to library1
user2.librariesAccessible = [library1.id]
await user2.save()
const libraryFolder1 = await Database.libraryFolderModel.create({ path: '/test1', libraryId: library1.id })
const libraryFolder2 = await Database.libraryFolderModel.create({ path: '/test2', libraryId: library2.id })
const book1 = await Database.bookModel.create({ title: 'Book 1', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
const book2 = await Database.bookModel.create({ title: 'Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
libraryItem1 = await Database.libraryItemModel.create({
libraryFiles: [],
mediaId: book1.id,
mediaType: 'book',
libraryId: library1.id,
libraryFolderId: libraryFolder1.id
})
libraryItem2 = await Database.libraryItemModel.create({
libraryFiles: [],
mediaId: book2.id,
mediaType: 'book',
libraryId: library2.id,
libraryFolderId: libraryFolder2.id
})
})
it('should allow user to view listening sessions for accessible library item', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem1.id)
// Create mock context with getUserItemListeningSessionsHelper
const mockContext = {
getUserItemListeningSessionsHelper: sinon.stub().resolves([{ id: 'session1', timeListening: 300, startedAt: Date.now() }])
}
const fakeReq = {
user: {
...user1.toJSON(),
id: user1.id,
username: user1.username,
checkCanAccessLibraryItem: () => true
},
params: { libraryItemId: libraryItem1.id },
query: {}
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
sinon.stub(Database.podcastEpisodeModel, 'findByPk').resolves(null)
await MeController.getItemListeningSessions.bind(mockContext)(fakeReq, fakeRes)
expect(fakeRes.json.calledOnce).to.be.true
expect(fakeRes.sendStatus.called).to.be.false
// Verify the payload structure
const payload = fakeRes.json.firstCall.args[0]
expect(payload).to.have.property('total')
expect(payload).to.have.property('sessions')
Database.libraryItemModel.getExpandedById.restore()
Database.podcastEpisodeModel.findByPk.restore()
})
it('should prevent user from viewing listening sessions for inaccessible library item (IDOR)', async () => {
const expandedItem = await Database.libraryItemModel.getExpandedById(libraryItem2.id)
const fakeReq = {
user: user2, // user2 doesn't have access to library2
params: { libraryItemId: libraryItem2.id },
query: {}
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(expandedItem)
sinon.stub(Database.podcastEpisodeModel, 'findByPk').resolves(null)
await MeController.getItemListeningSessions.bind(apiRouter)(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(403)).to.be.true
expect(fakeRes.json.called).to.be.false
Database.libraryItemModel.getExpandedById.restore()
Database.podcastEpisodeModel.findByPk.restore()
})
it('should return 404 for non-existent library item', async () => {
const fakeReq = {
user: user1,
params: { libraryItemId: 'non-existent-id' },
query: {}
}
const fakeRes = {
sendStatus: sinon.spy(),
status: sinon.stub().returnsThis(),
send: sinon.spy(),
json: sinon.spy()
}
sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(null)
await MeController.getItemListeningSessions.bind(apiRouter)(fakeReq, fakeRes)
expect(fakeRes.sendStatus.calledWith(404)).to.be.true
Database.libraryItemModel.getExpandedById.restore()
})
})
})

View file

@ -35,10 +35,7 @@ describe('TitleCandidates', () => {
['adds candidate, removing author', `anna karenina by ${cleanAuthor}`, ['anna karenina']],
['does not add empty candidate after removing author', cleanAuthor, []],
['adds candidate, removing subtitle', 'anna karenina: subtitle', ['anna karenina']],
['adds candidate, not stripping subtitle for bare colon in title', '10:04', ['10:04']],
['adds candidate, not stripping subtitle for colon between words without space', 'making the mission:impossible movies', ['making the mission:impossible movies']],
['adds candidate + variant, removing "by ..."', 'anna karenina by arnold schwarzenegger', ['anna karenina', 'anna karenina by arnold schwarzenegger']],
['adds candidate + variant, removing "by ..." when title has bare colon', '10:04 by ben lerner', ['10:04', '10:04 by ben lerner']],
['adds candidate + variant, removing bitrate', 'anna karenina 64kbps', ['anna karenina', 'anna karenina 64kbps']],
['adds candidate + variant, removing edition 1', 'anna karenina 2nd edition', ['anna karenina', 'anna karenina 2nd edition']],
['adds candidate + variant, removing edition 2', 'anna karenina 4th ed.', ['anna karenina', 'anna karenina 4th ed.']],

View file

@ -21,20 +21,6 @@ describe('parseNfoMetadata', () => {
expect(result.subtitle).to.equal('A Novel')
})
it('does not split title on bare colon without space', () => {
const nfoText = 'Title: 10:04'
const result = parseNfoMetadata(nfoText)
expect(result.title).to.equal('10:04')
expect(result.subtitle).to.be.undefined
})
it('does not split title on colon between words without space', () => {
const nfoText = 'Title: Making the Mission:Impossible Movies'
const result = parseNfoMetadata(nfoText)
expect(result.title).to.equal('Making the Mission:Impossible Movies')
expect(result.subtitle).to.be.undefined
})
it('parses authors', () => {
const nfoText = 'Author: F. Scott Fitzgerald'
const result = parseNfoMetadata(nfoText)