Browse Source

Merge pull request #1252 from jellyfin/release-10.3.z

Backmerge release 10.3.0
Bond-009 6 years ago
parent
commit
a8da122fb3
32 changed files with 391 additions and 200 deletions
  1. 4 4
      Dockerfile
  2. 3 3
      Dockerfile.arm
  3. 3 3
      Dockerfile.arm64
  4. BIN
      Emby.Dlna/Images/logo120.png
  5. BIN
      Emby.Dlna/Images/logo240.png
  6. BIN
      Emby.Dlna/Images/logo48.png
  7. 4 4
      Emby.Server.Implementations/ApplicationHost.cs
  8. 2 11
      Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
  9. 1 0
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  10. 33 11
      Emby.Server.Implementations/Library/UserManager.cs
  11. 12 12
      Emby.Server.Implementations/Localization/Core/cs.json
  12. 2 2
      Emby.Server.Implementations/Localization/Core/da.json
  13. 4 4
      Emby.Server.Implementations/Localization/Core/el.json
  14. 83 83
      Emby.Server.Implementations/Localization/Core/gsw.json
  15. 4 4
      Emby.Server.Implementations/Localization/Core/kk.json
  16. 33 33
      Emby.Server.Implementations/Localization/Core/sl-SI.json
  17. 93 0
      Emby.Server.Implementations/Localization/Core/zh-TW.json
  18. 0 2
      Emby.Server.Implementations/Udp/UdpServer.cs
  19. 32 6
      Emby.Server.Implementations/Updates/InstallationManager.cs
  20. 6 1
      MediaBrowser.Api/UserService.cs
  21. 24 0
      MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
  22. 5 0
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  23. 5 0
      MediaBrowser.Model/System/PublicSystemInfo.cs
  24. 0 4
      MediaBrowser.Model/System/SystemInfo.cs
  25. 1 1
      MediaBrowser.WebDashboard/jellyfin-web
  26. 2 2
      SharedVersion.cs
  27. 1 1
      build.yaml
  28. 15 2
      bump_version
  29. 6 0
      deployment/debian-package-x64/pkg-src/changelog
  30. 3 1
      deployment/fedora-package-x64/pkg-src/jellyfin.spec
  31. 5 1
      deployment/windows/build-jellyfin.ps1
  32. 5 5
      deployment/windows/install-jellyfin.ps1

+ 4 - 4
Dockerfile

@@ -1,6 +1,6 @@
-ARG DOTNET_VERSION=2
+ARG DOTNET_VERSION=2.2
 
-FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder
+FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
 WORKDIR /repo
 COPY . .
 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
@@ -8,7 +8,7 @@ RUN bash -c "source deployment/common.build.sh && \
     build_jellyfin Jellyfin.Server Release linux-x64 /jellyfin"
 
 FROM jellyfin/ffmpeg as ffmpeg
-FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
+FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}
 # libfontconfig1 is required for Skia
 RUN apt-get update \
  && apt-get install --no-install-recommends --no-install-suggests -y \
@@ -21,7 +21,7 @@ RUN apt-get update \
 COPY --from=ffmpeg / /
 COPY --from=builder /jellyfin /jellyfin
 
-ARG JELLYFIN_WEB_VERSION=10.2.2
+ARG JELLYFIN_WEB_VERSION=10.3.0
 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

+ 3 - 3
Dockerfile.arm

@@ -8,7 +8,7 @@ FROM alpine as qemu_extract
 COPY --from=qemu /usr/bin qemu-arm-static.tar.gz
 RUN tar -xzvf qemu-arm-static.tar.gz
 
-FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch as builder
+FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
 WORKDIR /repo
 COPY . .
 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
@@ -21,7 +21,7 @@ RUN bash -c "source deployment/common.build.sh && \
     build_jellyfin Jellyfin.Server Release linux-arm /jellyfin"
 
 
-FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm32v7
+FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm32v7
 COPY --from=qemu_extract qemu-arm-static /usr/bin
 RUN apt-get update \
  && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
@@ -30,7 +30,7 @@ RUN apt-get update \
  && chmod 777 /cache /config /media
 COPY --from=builder /jellyfin /jellyfin
 
-ARG JELLYFIN_WEB_VERSION=10.2.2
+ARG JELLYFIN_WEB_VERSION=10.3.0
 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

+ 3 - 3
Dockerfile.arm64

@@ -9,7 +9,7 @@ COPY --from=qemu /usr/bin qemu-aarch64-static.tar.gz
 RUN tar -xzvf qemu-aarch64-static.tar.gz
 
 
-FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch as builder
+FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
 WORKDIR /repo
 COPY . .
 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
@@ -22,7 +22,7 @@ RUN bash -c "source deployment/common.build.sh && \
     build_jellyfin Jellyfin.Server Release linux-arm64 /jellyfin"
 
 
-FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm64v8
+FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm64v8
 COPY --from=qemu_extract qemu-aarch64-static /usr/bin
 RUN apt-get update \
  && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
@@ -31,7 +31,7 @@ RUN apt-get update \
  && chmod 777 /cache /config /media
 COPY --from=builder /jellyfin /jellyfin
 
-ARG JELLYFIN_WEB_VERSION=10.2.2
+ARG JELLYFIN_WEB_VERSION=10.3.0
 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && rm -rf /jellyfin/jellyfin-web \
  && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

BIN
Emby.Dlna/Images/logo120.png


BIN
Emby.Dlna/Images/logo240.png


BIN
Emby.Dlna/Images/logo48.png


+ 4 - 4
Emby.Server.Implementations/ApplicationHost.cs

@@ -1025,8 +1025,8 @@ namespace Emby.Server.Implementations
 
         private async void PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> args)
         {
-            string dir = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(args.Argument.targetFilename));
-            var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.TopDirectoryOnly)
+            string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name);
+            var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories)
                         .Select(x => Assembly.LoadFrom(x))
                         .SelectMany(x => x.ExportedTypes)
                         .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
@@ -1325,7 +1325,7 @@ namespace Emby.Server.Implementations
         {
             if (Directory.Exists(ApplicationPaths.PluginsPath))
             {
-                foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly))
+                foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
                 {
                     Logger.LogInformation("Loading assembly {Path}", file);
                     yield return Assembly.LoadFrom(file);
@@ -1404,7 +1404,6 @@ namespace Emby.Server.Implementations
                 HasPendingRestart = HasPendingRestart,
                 IsShuttingDown = IsShuttingDown,
                 Version = ApplicationVersion,
-                ProductName = ApplicationProductName,
                 WebSocketPortNumber = HttpPort,
                 CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
                 Id = SystemId,
@@ -1461,6 +1460,7 @@ namespace Emby.Server.Implementations
             return new PublicSystemInfo
             {
                 Version = ApplicationVersion,
+                ProductName = ApplicationProductName,
                 Id = SystemId,
                 OperatingSystem = OperatingSystem.Id.ToString(),
                 WanAddress = wanAddress,

+ 2 - 11
Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs

@@ -74,23 +74,14 @@ namespace Emby.Server.Implementations.Configuration
         /// </summary>
         private void UpdateMetadataPath()
         {
-            string metadataPath;
-
             if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
             {
-                metadataPath = GetInternalMetadataPath();
+                ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
             }
             else
             {
-                metadataPath = Path.Combine(Configuration.MetadataPath, "metadata");
+                ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath;
             }
-
-            ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
-        }
-
-        private string GetInternalMetadataPath()
-        {
-            return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
         }
 
         /// <summary>

+ 1 - 0
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -201,6 +201,7 @@ namespace Emby.Server.Implementations.HttpServer
                 case DirectoryNotFoundException _:
                 case FileNotFoundException _:
                 case ResourceNotFoundException _: return 404;
+                case MethodNotAllowedException _: return 405;
                 case RemoteServiceUnavailableException _: return 502;
                 default: return 500;
             }

+ 33 - 11
Emby.Server.Implementations/Library/UserManager.cs

@@ -277,24 +277,35 @@ namespace Emby.Server.Implementations.Library
                 .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
 
             var success = false;
+            string updatedUsername = null;
             IAuthenticationProvider authenticationProvider = null;
 
             if (user != null)
             {
                 var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
                 authenticationProvider = authResult.Item1;
-                success = authResult.Item2;
+                updatedUsername = authResult.Item2;
+                success = authResult.Item3;
             }
             else
             {
                 // user is null
                 var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
                 authenticationProvider = authResult.Item1;
-                success = authResult.Item2;
+                updatedUsername = authResult.Item2;
+                success = authResult.Item3;
 
                 if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider))
                 {
-                    user = await CreateUser(username).ConfigureAwait(false);
+                    // We should trust the user that the authprovider says, not what was typed
+                    if (updatedUsername != username)
+                    {
+                        username = updatedUsername;
+                    }
+
+                    // Search the database for the user again; the authprovider might have created it
+                    user = Users
+                        .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
 
                     var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy;
                     if (hasNewUserPolicy != null)
@@ -414,32 +425,40 @@ namespace Emby.Server.Implementations.Library
             return providers;
         }
 
-        private async Task<bool> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
+        private async Task<Tuple<string, bool>> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
         {
             try
             {
                 var requiresResolvedUser = provider as IRequiresResolvedUser;
+                ProviderAuthenticationResult authenticationResult = null;
                 if (requiresResolvedUser != null)
                 {
-                    await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false);
+                    authenticationResult = await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false);
                 }
                 else
                 {
-                    await provider.Authenticate(username, password).ConfigureAwait(false);
+                    authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false);
+                }
+
+                if(authenticationResult.Username != username)
+                {
+                    _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
+                    username = authenticationResult.Username;
                 }
 
-                return true;
+                return new Tuple<string, bool>(username, true);
             }
             catch (Exception ex)
             {
                 _logger.LogError(ex, "Error authenticating with provider {provider}", provider.Name);
 
-                return false;
+                return new Tuple<string, bool>(username, false);
             }
         }
 
-        private async Task<Tuple<IAuthenticationProvider, bool>> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
+        private async Task<Tuple<IAuthenticationProvider, string, bool>> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
         {
+            string updatedUsername = null;
             bool success = false;
             IAuthenticationProvider authenticationProvider = null;
 
@@ -458,11 +477,14 @@ namespace Emby.Server.Implementations.Library
             {
                 foreach (var provider in GetAuthenticationProviders(user))
                 {
-                    success = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+                    var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+                    updatedUsername = providerAuthResult.Item1;
+                    success = providerAuthResult.Item2;
 
                     if (success)
                     {
                         authenticationProvider = provider;
+                        username = updatedUsername;
                         break;
                     }
                 }
@@ -484,7 +506,7 @@ namespace Emby.Server.Implementations.Library
                 }
             }
 
-            return new Tuple<IAuthenticationProvider, bool>(authenticationProvider, success);
+            return new Tuple<IAuthenticationProvider, string, bool>(authenticationProvider, username, success);
         }
 
         private void UpdateInvalidLoginAttemptCount(User user, int newValue)

+ 12 - 12
Emby.Server.Implementations/Localization/Core/cs.json

@@ -5,7 +5,7 @@
     "Artists": "Umělci",
     "AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
     "Books": "Knihy",
-    "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+    "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie",
     "Channels": "Kanály",
     "ChapterNameValue": "Kapitola {0}",
     "Collections": "Kolekce",
@@ -16,14 +16,14 @@
     "Folders": "Složky",
     "Genres": "Žánry",
     "HeaderAlbumArtists": "Umělci alba",
-    "HeaderCameraUploads": "Camera Uploads",
+    "HeaderCameraUploads": "Nahrané fotografie",
     "HeaderContinueWatching": "Pokračovat ve sledování",
     "HeaderFavoriteAlbums": "Oblíbená alba",
-    "HeaderFavoriteArtists": "Oblíbení umělci",
+    "HeaderFavoriteArtists": "Oblíbení interpreti",
     "HeaderFavoriteEpisodes": "Oblíbené epizody",
     "HeaderFavoriteShows": "Oblíbené seriály",
-    "HeaderFavoriteSongs": "Oblíbené písně",
-    "HeaderLiveTV": "Živá TV",
+    "HeaderFavoriteSongs": "Oblíbená hudba",
+    "HeaderLiveTV": "Live TV",
     "HeaderNextUp": "Nadcházející",
     "HeaderRecordingGroups": "Skupiny nahrávek",
     "HomeVideos": "Domáci videa",
@@ -34,17 +34,17 @@
     "LabelRunningTimeValue": "Délka média: {0}",
     "Latest": "Nejnovější",
     "MessageApplicationUpdated": "Jellyfin Server byl aktualizován",
-    "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+    "MessageApplicationUpdatedTo": "Jellyfin server byl aktualizován na verzi {0}",
     "MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizována",
     "MessageServerConfigurationUpdated": "Konfigurace serveru aktualizována",
     "MixedContent": "Smíšený obsah",
     "Movies": "Filmy",
     "Music": "Hudba",
     "MusicVideos": "Hudební klipy",
-    "NameInstallFailed": "{0} installation failed",
+    "NameInstallFailed": "Instalace {0} selhala",
     "NameSeasonNumber": "Sezóna {0}",
     "NameSeasonUnknown": "Neznámá sezóna",
-    "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+    "NewVersionIsAvailable": "Nová verze Jellyfin serveru je k dispozici ke stažení.",
     "NotificationOptionApplicationUpdateAvailable": "Dostupná aktualizace aplikace",
     "NotificationOptionApplicationUpdateInstalled": "Aktualizace aplikace instalována",
     "NotificationOptionAudioPlayback": "Přehrávání audia zahájeno",
@@ -70,12 +70,12 @@
     "ProviderValue": "Poskytl: {0}",
     "ScheduledTaskFailedWithName": "{0} selhalo",
     "ScheduledTaskStartedWithName": "{0} zahájeno",
-    "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+    "ServerNameNeedsToBeRestarted": "{0} vyžaduje restart",
     "Shows": "Seriály",
     "Songs": "Skladby",
     "StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
     "SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}",
-    "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+    "SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
     "SubtitlesDownloadedForItem": "Staženy titulky pro {0}",
     "Sync": "Synchronizace",
     "System": "Systém",
@@ -88,10 +88,10 @@
     "UserOfflineFromDevice": "{0} se odpojil od {1}",
     "UserOnlineFromDevice": "{0} se připojil z {1}",
     "UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}",
-    "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
+    "UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány",
     "UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}",
     "UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
-    "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+    "ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií",
     "ValueSpecialEpisodeName": "Speciál - {0}",
     "VersionNumber": "Verze {0}"
 }

+ 2 - 2
Emby.Server.Implementations/Localization/Core/da.json

@@ -61,8 +61,8 @@
     "NotificationOptionUserLockedOut": "Bruger låst ude",
     "NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
     "NotificationOptionVideoPlaybackStopped": "Videoafspilning stoppet",
-    "Photos": "Fotos",
-    "Playlists": "Spillelister",
+    "Photos": "Fotoer",
+    "Playlists": "Afspilningslister",
     "Plugin": "Plugin",
     "PluginInstalledWithName": "{0} blev installeret",
     "PluginUninstalledWithName": "{0} blev afinstalleret",

+ 4 - 4
Emby.Server.Implementations/Localization/Core/el.json

@@ -16,7 +16,7 @@
     "Folders": "Φάκελοι",
     "Genres": "Είδη",
     "HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
-    "HeaderCameraUploads": "Camera Uploads",
+    "HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
     "HeaderContinueWatching": "Συνεχίστε να παρακολουθείτε",
     "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
     "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
@@ -34,7 +34,7 @@
     "LabelRunningTimeValue": "Διάρκεια: {0}",
     "Latest": "Πρόσφατα",
     "MessageApplicationUpdated": "Ο Jellyfin Server έχει ενημερωθεί",
-    "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+    "MessageApplicationUpdatedTo": "Ο server Jellyfin αναβαθμίστηκε σε έκδοση {0}",
     "MessageNamedServerConfigurationUpdatedWithValue": "Η ενότητα {0} ρύθμισης παραμέτρων του server έχει ενημερωθεί",
     "MessageServerConfigurationUpdated": "Η ρύθμιση παραμέτρων του server έχει ενημερωθεί",
     "MixedContent": "Ανάμεικτο Περιεχόμενο",
@@ -49,7 +49,7 @@
     "NotificationOptionApplicationUpdateInstalled": "Η ενημέρωση εφαρμογής εγκαταστάθηκε",
     "NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε",
     "NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε",
-    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
+    "NotificationOptionCameraImageUploaded": "Μεταφορτώθηκε φωτογραφία απο κάμερα",
     "NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
     "NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
     "NotificationOptionPluginError": "Αποτυχία του plugin",
@@ -75,7 +75,7 @@
     "Songs": "Τραγούδια",
     "StartupEmbyServerIsLoading": "Ο Jellyfin Server φορτώνει. Παρακαλώ δοκιμάστε σε λίγο.",
     "SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}",
-    "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+    "SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}",
     "SubtitlesDownloadedForItem": "Οι υπότιτλοι κατέβηκαν για {0}",
     "Sync": "Συγχρονισμός",
     "System": "Σύστημα",

+ 83 - 83
Emby.Server.Implementations/Localization/Core/gsw.json

@@ -1,97 +1,97 @@
 {
-    "Albums": "Albums",
-    "AppDeviceValues": "App: {0}, Device: {1}",
-    "Application": "Application",
-    "Artists": "Artists",
-    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
+    "Albums": "Albom",
+    "AppDeviceValues": "App: {0}, Grät: {1}",
+    "Application": "Aawändig",
+    "Artists": "Könstler",
+    "AuthenticationSucceededWithUserName": "{0} het sech aagmäudet",
     "Books": "Büecher",
-    "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
-    "Channels": "Channels",
-    "ChapterNameValue": "Chapter {0}",
-    "Collections": "Collections",
-    "DeviceOfflineWithName": "{0} has disconnected",
-    "DeviceOnlineWithName": "{0} is connected",
-    "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
-    "Favorites": "Favorites",
-    "Folders": "Folders",
+    "CameraImageUploadedFrom": "Es nöis Foti esch ufeglade worde vo {0}",
+    "Channels": "Kanäu",
+    "ChapterNameValue": "Kapitu {0}",
+    "Collections": "Sammlige",
+    "DeviceOfflineWithName": "{0} esch offline gange",
+    "DeviceOnlineWithName": "{0} esch online cho",
+    "FailedLoginAttemptWithUserName": "Fäugschlagne Aamäudeversuech vo {0}",
+    "Favorites": "Favorite",
+    "Folders": "Ordner",
     "Genres": "Genres",
-    "HeaderAlbumArtists": "Albuminterprete",
-    "HeaderCameraUploads": "Camera Uploads",
+    "HeaderAlbumArtists": "Albom-Könstler",
+    "HeaderCameraUploads": "Kamera-Uploads",
     "HeaderContinueWatching": "Wiiterluege",
-    "HeaderFavoriteAlbums": "Favorite Albums",
-    "HeaderFavoriteArtists": "Besti Interpret",
-    "HeaderFavoriteEpisodes": "Favorite Episodes",
-    "HeaderFavoriteShows": "Favorite Shows",
-    "HeaderFavoriteSongs": "Besti Lieder",
-    "HeaderLiveTV": "Live TV",
-    "HeaderNextUp": "Next Up",
+    "HeaderFavoriteAlbums": "Lieblingsalbe",
+    "HeaderFavoriteArtists": "Lieblings-Interprete",
+    "HeaderFavoriteEpisodes": "Lieblingsepisode",
+    "HeaderFavoriteShows": "Lieblingsserie",
+    "HeaderFavoriteSongs": "Lieblingslieder",
+    "HeaderLiveTV": "Live-Färnseh",
+    "HeaderNextUp": "Als nächts",
     "HeaderRecordingGroups": "Ufnahmegruppe",
     "HomeVideos": "Heimfilmli",
     "Inherit": "Hinzuefüege",
-    "ItemAddedWithName": "{0} was added to the library",
-    "ItemRemovedWithName": "{0} was removed from the library",
-    "LabelIpAddressValue": "Ip address: {0}",
-    "LabelRunningTimeValue": "Running time: {0}",
-    "Latest": "Letschte",
-    "MessageApplicationUpdated": "Jellyfin Server has been updated",
-    "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
-    "MessageServerConfigurationUpdated": "Server configuration has been updated",
-    "MixedContent": "Gmischte Inhalt",
-    "Movies": "Movies",
+    "ItemAddedWithName": "{0} esch de Bibliothek dezuegfüegt worde",
+    "ItemRemovedWithName": "{0} esch vo de Bibliothek entfärnt worde",
+    "LabelIpAddressValue": "IP-Adrässe: {0}",
+    "LabelRunningTimeValue": "Loufziit: {0}",
+    "Latest": "Nöischti",
+    "MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde",
+    "MessageApplicationUpdatedTo": "Jellyfin Server esch of Version {0} aktualisiert worde",
+    "MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde",
+    "MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
+    "MixedContent": "Gmeschti Inhäut",
+    "Movies": "Film",
     "Music": "Musig",
-    "MusicVideos": "Musigfilm",
-    "NameInstallFailed": "{0} installation failed",
-    "NameSeasonNumber": "Season {0}",
-    "NameSeasonUnknown": "Season Unknown",
-    "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
-    "NotificationOptionApplicationUpdateAvailable": "Application update available",
-    "NotificationOptionApplicationUpdateInstalled": "Application update installed",
-    "NotificationOptionAudioPlayback": "Audio playback started",
-    "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
-    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
-    "NotificationOptionInstallationFailed": "Installation failure",
-    "NotificationOptionNewLibraryContent": "New content added",
-    "NotificationOptionPluginError": "Plugin failure",
-    "NotificationOptionPluginInstalled": "Plugin installed",
-    "NotificationOptionPluginUninstalled": "Plugin uninstalled",
-    "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
-    "NotificationOptionServerRestartRequired": "Server restart required",
-    "NotificationOptionTaskFailed": "Scheduled task failure",
-    "NotificationOptionUserLockedOut": "User locked out",
-    "NotificationOptionVideoPlayback": "Video playback started",
-    "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
+    "MusicVideos": "Musigvideos",
+    "NameInstallFailed": "Installation vo {0} fäugschlage",
+    "NameSeasonNumber": "Staffle {0}",
+    "NameSeasonUnknown": "Staffle unbekannt",
+    "NewVersionIsAvailable": "E nöi Version vo Jellyfin Server esch zom Download parat.",
+    "NotificationOptionApplicationUpdateAvailable": "Aawändigsupdate verfüegbar",
+    "NotificationOptionApplicationUpdateInstalled": "Aawändigsupdate installiert",
+    "NotificationOptionAudioPlayback": "Audiowedergab gstartet",
+    "NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
+    "NotificationOptionCameraImageUploaded": "Foti ueglade",
+    "NotificationOptionInstallationFailed": "Installationsfäuer",
+    "NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
+    "NotificationOptionPluginError": "Plugin-Fäuer",
+    "NotificationOptionPluginInstalled": "Plugin installiert",
+    "NotificationOptionPluginUninstalled": "Plugin deinstalliert",
+    "NotificationOptionPluginUpdateInstalled": "Pluginupdate installiert",
+    "NotificationOptionServerRestartRequired": "Serverneustart notwändig",
+    "NotificationOptionTaskFailed": "Planti Uufgab fäugschlage",
+    "NotificationOptionUserLockedOut": "Benotzer usgschlosse",
+    "NotificationOptionVideoPlayback": "Videowedergab gstartet",
+    "NotificationOptionVideoPlaybackStopped": "Videowedergab gstoppt",
     "Photos": "Fotis",
-    "Playlists": "Abspielliste",
+    "Playlists": "Wedergabeliste",
     "Plugin": "Plugin",
-    "PluginInstalledWithName": "{0} was installed",
-    "PluginUninstalledWithName": "{0} was uninstalled",
-    "PluginUpdatedWithName": "{0} was updated",
-    "ProviderValue": "Provider: {0}",
-    "ScheduledTaskFailedWithName": "{0} failed",
-    "ScheduledTaskStartedWithName": "{0} started",
-    "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
-    "Shows": "Shows",
-    "Songs": "Songs",
-    "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
+    "PluginInstalledWithName": "{0} esch installiert worde",
+    "PluginUninstalledWithName": "{0} esch deinstalliert worde",
+    "PluginUpdatedWithName": "{0} esch updated worde",
+    "ProviderValue": "Aabieter: {0}",
+    "ScheduledTaskFailedWithName": "{0} esch fäugschlage",
+    "ScheduledTaskStartedWithName": "{0} het gstartet",
+    "ServerNameNeedsToBeRestarted": "{0} mues nöi gstartet wärde",
+    "Shows": "Serie",
+    "Songs": "Lieder",
+    "StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.",
     "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
-    "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "Sync": "Sync",
+    "SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde",
+    "SubtitlesDownloadedForItem": "Ondertetle abeglade för {0}",
+    "Sync": "Synchronisation",
     "System": "System",
-    "TvShows": "TV Shows",
-    "User": "User",
-    "UserCreatedWithName": "User {0} has been created",
-    "UserDeletedWithName": "User {0} has been deleted",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
-    "UserLockedOutWithName": "User {0} has been locked out",
-    "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "UserOnlineFromDevice": "{0} is online from {1}",
-    "UserPasswordChangedWithName": "Password has been changed for user {0}",
-    "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
-    "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
-    "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
-    "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
-    "ValueSpecialEpisodeName": "Spezial - {0}",
+    "TvShows": "Färnsehserie",
+    "User": "Benotzer",
+    "UserCreatedWithName": "Benotzer {0} esch erstöut worde",
+    "UserDeletedWithName": "Benotzer {0} esch glösche worde",
+    "UserDownloadingItemWithValues": "{0} ladt {1} abe",
+    "UserLockedOutWithName": "Benotzer {0} esch usgschlosse worde",
+    "UserOfflineFromDevice": "{0} esch vo {1} trennt worde",
+    "UserOnlineFromDevice": "{0} esch online vo {1}",
+    "UserPasswordChangedWithName": "S'Passwort för Benotzer {0} esch gänderet worde",
+    "UserPolicyUpdatedWithName": "Benotzerrechtlinie för {0} esch aktualisiert worde",
+    "UserStartedPlayingItemWithValues": "{0} hed d'Wedergab vo {1} of {2} gstartet",
+    "UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
+    "ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
+    "ValueSpecialEpisodeName": "Extra - {0}",
     "VersionNumber": "Version {0}"
 }

+ 4 - 4
Emby.Server.Implementations/Localization/Core/kk.json

@@ -5,7 +5,7 @@
     "Artists": "Oryndaýshylar",
     "AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy",
     "Books": "Kitaptar",
-    "CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep alyndy",
+    "CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep salyndy",
     "Channels": "Arnalar",
     "ChapterNameValue": "{0}-sahna",
     "Collections": "Jıyntyqtar",
@@ -35,8 +35,8 @@
     "Latest": "Eń keıingi",
     "MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
     "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy",
-    "MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy",
+    "MessageNamedServerConfigurationUpdatedWithValue": "Server konfıgýrasýasynyń {0} bólimi jańartyldy",
+    "MessageServerConfigurationUpdated": "Server konfıgýrasıasy jańartyldy",
     "MixedContent": "Aralas mazmun",
     "Movies": "Fılmder",
     "Music": "Mýzyka",
@@ -49,7 +49,7 @@
     "NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
     "NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
     "NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
-    "NotificationOptionCameraImageUploaded": "Kameradan fotosýret keri qotarylǵan",
+    "NotificationOptionCameraImageUploaded": "Kameradan fotosýret júktep salynǵan",
     "NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
     "NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
     "NotificationOptionPluginError": "Plagın sátsizdigi",

+ 33 - 33
Emby.Server.Implementations/Localization/Core/sl-SI.json

@@ -9,7 +9,7 @@
     "Channels": "Kanali",
     "ChapterNameValue": "Poglavje {0}",
     "Collections": "Zbirke",
-    "DeviceOfflineWithName": "{0} has disconnected",
+    "DeviceOfflineWithName": "{0} je prekinil povezavo",
     "DeviceOnlineWithName": "{0} je povezan",
     "FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}",
     "Favorites": "Priljubljeni",
@@ -33,9 +33,9 @@
     "LabelIpAddressValue": "IP naslov: {0}",
     "LabelRunningTimeValue": "Čas trajanja: {0}",
     "Latest": "Najnovejše",
-    "MessageApplicationUpdated": "Jellyfin strežnik je bil posodobljen",
-    "MessageApplicationUpdatedTo": "Jellyfin strežnik je bil posodobljen na {0}",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
+    "MessageApplicationUpdated": "Jellyfin Server je bil posodobljen",
+    "MessageApplicationUpdatedTo": "Jellyfin Server je bil posodobljen na {0}",
+    "MessageNamedServerConfigurationUpdatedWithValue": "Oddelek nastavitve strežnika {0} je bil posodobljen",
     "MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene",
     "MixedContent": "Razne vsebine",
     "Movies": "Filmi",
@@ -57,41 +57,41 @@
     "NotificationOptionPluginUninstalled": "Dodatek odstranjen",
     "NotificationOptionPluginUpdateInstalled": "Posodobitev dodatka nameščena",
     "NotificationOptionServerRestartRequired": "Potreben je ponovni zagon strežnika",
-    "NotificationOptionTaskFailed": "Scheduled task failure",
-    "NotificationOptionUserLockedOut": "User locked out",
-    "NotificationOptionVideoPlayback": "Video playback started",
-    "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
-    "Photos": "Photos",
-    "Playlists": "Playlists",
+    "NotificationOptionTaskFailed": "Razporejena naloga neuspešna",
+    "NotificationOptionUserLockedOut": "Uporabnik zaklenjen",
+    "NotificationOptionVideoPlayback": "Predvajanje videa se je začelo",
+    "NotificationOptionVideoPlaybackStopped": "Predvajanje videa se je ustavilo",
+    "Photos": "Fotografije",
+    "Playlists": "Seznami predvajanja",
     "Plugin": "Plugin",
-    "PluginInstalledWithName": "{0} was installed",
-    "PluginUninstalledWithName": "{0} was uninstalled",
-    "PluginUpdatedWithName": "{0} was updated",
+    "PluginInstalledWithName": "{0} je bil nameščen",
+    "PluginUninstalledWithName": "{0} je bil odstranjen",
+    "PluginUpdatedWithName": "{0} je bil posodobljen",
     "ProviderValue": "Provider: {0}",
-    "ScheduledTaskFailedWithName": "{0} failed",
-    "ScheduledTaskStartedWithName": "{0} started",
-    "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+    "ScheduledTaskFailedWithName": "{0} ni uspelo",
+    "ScheduledTaskStartedWithName": "{0} začeto",
+    "ServerNameNeedsToBeRestarted": "{0} mora biti ponovno zagnan",
     "Shows": "Serije",
-    "Songs": "Songs",
-    "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
+    "Songs": "Pesmi",
+    "StartupEmbyServerIsLoading": "Jellyfin Server se nalaga. Poskusi ponovno kasneje.",
     "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
-    "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "Sync": "Sync",
+    "SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}",
+    "SubtitlesDownloadedForItem": "Podnapisi preneseni za {0}",
+    "Sync": "Sinhroniziraj",
     "System": "System",
-    "TvShows": "TV Shows",
+    "TvShows": "TV serije",
     "User": "User",
-    "UserCreatedWithName": "User {0} has been created",
-    "UserDeletedWithName": "User {0} has been deleted",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
-    "UserLockedOutWithName": "User {0} has been locked out",
-    "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "UserOnlineFromDevice": "{0} is online from {1}",
-    "UserPasswordChangedWithName": "Password has been changed for user {0}",
-    "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
-    "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
-    "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
-    "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+    "UserCreatedWithName": "Uporabnik {0} je bil ustvarjen",
+    "UserDeletedWithName": "Uporabnik {0} je bil izbrisan",
+    "UserDownloadingItemWithValues": "{0} prenaša {1}",
+    "UserLockedOutWithName": "Uporabnik {0} je bil zaklenjen",
+    "UserOfflineFromDevice": "{0} je prekinil povezavo z {1}",
+    "UserOnlineFromDevice": "{0} je aktiven iz {1}",
+    "UserPasswordChangedWithName": "Geslo za uporabnika {0} je bilo spremenjeno",
+    "UserPolicyUpdatedWithName": "Pravilnik uporabe je bil posodobljen za uporabnika {0}",
+    "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
+    "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
+    "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
     "ValueSpecialEpisodeName": "Special - {0}",
     "VersionNumber": "Version {0}"
 }

+ 93 - 0
Emby.Server.Implementations/Localization/Core/zh-TW.json

@@ -0,0 +1,93 @@
+{
+    "Albums": "專輯",
+    "AppDeviceValues": "應用: {0}, 裝置: {1}",
+    "Application": "應用程式",
+    "Artists": "演出者",
+    "AuthenticationSucceededWithUserName": "{0} 成功授權",
+    "Books": "圖書",
+    "CameraImageUploadedFrom": "{0} 已經成功上傳一張相片",
+    "Channels": "頻道",
+    "ChapterNameValue": "章節 {0}",
+    "Collections": "合輯",
+    "DeviceOfflineWithName": "{0} 已經斷線",
+    "DeviceOnlineWithName": "{0} 已經連線",
+    "FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試",
+    "Favorites": "我的最愛",
+    "Folders": "資料夾",
+    "Genres": "風格",
+    "HeaderAlbumArtists": "專輯演出者",
+    "HeaderCameraUploads": "相機上傳",
+    "HeaderContinueWatching": "繼續觀賞",
+    "HeaderFavoriteAlbums": "最愛專輯",
+    "HeaderFavoriteArtists": "最愛演出者",
+    "HeaderFavoriteEpisodes": "最愛級數",
+    "HeaderFavoriteShows": "最愛節目",
+    "HeaderFavoriteSongs": "最愛歌曲",
+    "HeaderLiveTV": "電視直播",
+    "HeaderNextUp": "接下來",
+    "HomeVideos": "自製影片",
+    "ItemAddedWithName": "{0} 已新增至媒體庫",
+    "ItemRemovedWithName": "{0} 已從媒體庫移除",
+    "LabelIpAddressValue": "IP 位置: {0}",
+    "LabelRunningTimeValue": "運行時間: {0}",
+    "Latest": "最新",
+    "MessageApplicationUpdated": "Jellyfin Server 已經更新",
+    "MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}",
+    "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已經更新",
+    "MessageServerConfigurationUpdated": "伺服器設定已經更新",
+    "MixedContent": "混合內容",
+    "Movies": "電影",
+    "Music": "音樂",
+    "MusicVideos": "音樂MV",
+    "NameInstallFailed": "{0} 安裝失敗",
+    "NameSeasonNumber": "第 {0} 季",
+    "NameSeasonUnknown": "未知季數",
+    "NewVersionIsAvailable": "新版本的Jellyfin Server 軟體已經推出可供下載。",
+    "NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
+    "NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
+    "NotificationOptionAudioPlayback": "音樂開始播放",
+    "NotificationOptionAudioPlaybackStopped": "音樂停止播放",
+    "NotificationOptionCameraImageUploaded": "相機相片已上傳",
+    "NotificationOptionInstallationFailed": "安裝失敗",
+    "NotificationOptionNewLibraryContent": "已新增新內容",
+    "NotificationOptionPluginError": "外掛失敗",
+    "NotificationOptionPluginInstalled": "外掛已安裝",
+    "NotificationOptionPluginUninstalled": "外掛已移除",
+    "NotificationOptionPluginUpdateInstalled": "已更新外掛",
+    "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
+    "NotificationOptionTaskFailed": "排程任務失敗",
+    "NotificationOptionUserLockedOut": "使用者已鎖定",
+    "NotificationOptionVideoPlayback": "影片開始播放",
+    "NotificationOptionVideoPlaybackStopped": "影片停止播放",
+    "Photos": "相片",
+    "Playlists": "播放清單",
+    "Plugin": "外掛",
+    "PluginInstalledWithName": "{0} 已安裝",
+    "PluginUninstalledWithName": "{0} 已移除",
+    "PluginUpdatedWithName": "{0} 已更新",
+    "ProviderValue": "提供商: {0}",
+    "ScheduledTaskFailedWithName": "{0} 已失敗",
+    "ScheduledTaskStartedWithName": "{0} 已開始",
+    "ServerNameNeedsToBeRestarted": "{0} 需要重新啟動",
+    "Shows": "節目",
+    "Songs": "歌曲",
+    "StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
+    "SubtitlesDownloadedForItem": "已為 {0} 下載字幕",
+    "Sync": "同步",
+    "System": "系統",
+    "TvShows": "電視節目",
+    "User": "使用者",
+    "UserCreatedWithName": "使用者 {0} 已建立",
+    "UserDeletedWithName": "使用者 {0} 已移除",
+    "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
+    "UserLockedOutWithName": "使用者 {0} 已鎖定",
+    "UserOfflineFromDevice": "{0} 已從 {1} 斷線",
+    "UserOnlineFromDevice": "{0} 已連線,來自 {1}",
+    "UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
+    "UserPolicyUpdatedWithName": "使用者條約已更新為 {0}",
+    "UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}",
+    "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 播放 {1}",
+    "ValueHasBeenAddedToLibrary": "{0} 已新增至您的媒體庫",
+    "ValueSpecialEpisodeName": "特典 - {0}",
+    "VersionNumber": "版本 {0}"
+}

+ 0 - 2
Emby.Server.Implementations/Udp/UdpServer.cs

@@ -41,8 +41,6 @@ namespace Emby.Server.Implementations.Udp
             _socketFactory = socketFactory;
 
             AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
-            AddMessageResponder("who is EmbyServer?", true, RespondToV2Message);
-            AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
         }
 
         private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)

+ 32 - 6
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -509,6 +509,8 @@ namespace Emby.Server.Implementations.Updates
 
         private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
         {
+            // TODO: Remove the `string target` argument as it is not used any longer
+
             var extension = Path.GetExtension(package.targetFilename);
             var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
 
@@ -518,12 +520,12 @@ namespace Emby.Server.Implementations.Updates
                 return;
             }
 
-            if (target == null)
-            {
-                target = Path.Combine(_appPaths.PluginsPath, Path.GetFileNameWithoutExtension(package.targetFilename));
-            }
+            // Always override the passed-in target (which is a file) and figure it out again
+            target = Path.Combine(_appPaths.PluginsPath, package.name);
+            _logger.LogDebug("Installing plugin to {Filename}.", target);
 
             // Download to temporary file so that, if interrupted, it won't destroy the existing installation
+            _logger.LogDebug("Downloading ZIP.");
             var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
             {
                 Url = package.sourceUrl,
@@ -536,9 +538,17 @@ namespace Emby.Server.Implementations.Updates
 
             // TODO: Validate with a checksum, *properly*
 
+            // Check if the target directory already exists, and remove it if so
+            if (Directory.Exists(target))
+            {
+                _logger.LogDebug("Deleting existing plugin at {Filename}.", target);
+                Directory.Delete(target, true);
+            }
+
             // Success - move it to the real target
             try
             {
+                _logger.LogDebug("Extracting ZIP {TempFile} to {Filename}.", tempFile, target);
                 using (var stream = File.OpenRead(tempFile))
                 {
                     _zipClient.ExtractAllFromZip(stream, target, true);
@@ -552,6 +562,7 @@ namespace Emby.Server.Implementations.Updates
 
             try
             {
+                _logger.LogDebug("Deleting temporary file {Filename}.", tempFile);
                 _fileSystem.DeleteFile(tempFile);
             }
             catch (IOException ex)
@@ -574,7 +585,13 @@ namespace Emby.Server.Implementations.Updates
             _applicationHost.RemovePlugin(plugin);
 
             var path = plugin.AssemblyFilePath;
-            _logger.LogInformation("Deleting plugin file {0}", path);
+            bool isDirectory = false;
+            // Check if we have a plugin directory we should remove too
+            if (Path.GetDirectoryName(plugin.AssemblyFilePath) != _appPaths.PluginsPath)
+            {
+                path = Path.GetDirectoryName(plugin.AssemblyFilePath);
+                isDirectory = true;
+            }
 
             // Make this case-insensitive to account for possible incorrect assembly naming
             var file = _fileSystem.GetFilePaths(Path.GetDirectoryName(path))
@@ -585,7 +602,16 @@ namespace Emby.Server.Implementations.Updates
                 path = file;
             }
 
-            _fileSystem.DeleteFile(path);
+            if (isDirectory)
+            {
+                _logger.LogInformation("Deleting plugin directory {0}", path);
+                Directory.Delete(path, true);
+            }
+            else
+            {
+                _logger.LogInformation("Deleting plugin file {0}", path);
+                _fileSystem.DeleteFile(path);
+            }
 
             var list = _config.Configuration.UninstalledPlugins.ToList();
             var filename = Path.GetFileName(path);

+ 6 - 1
MediaBrowser.Api/UserService.cs

@@ -379,10 +379,15 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException("User not found");
             }
 
+            if (!string.IsNullOrEmpty(request.Password) && string.IsNullOrEmpty(request.Pw))
+            {
+                throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API.");
+            }
+
             return Post(new AuthenticateUserByName
             {
                 Username = user.Name,
-                Password = request.Password,
+                Password = null, // This should always be null
                 Pw = request.Pw
             });
         }

+ 24 - 0
MediaBrowser.Common/Extensions/ResourceNotFoundException.cs

@@ -26,6 +26,30 @@ namespace MediaBrowser.Common.Extensions
         }
     }
 
+    /// <summary>
+    /// Class MethodNotAllowedException
+    /// </summary>
+    public class MethodNotAllowedException : Exception
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+        /// </summary>
+        public MethodNotAllowedException()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public MethodNotAllowedException(string message)
+            : base(message)
+        {
+
+        }
+    }
+
     public class RemoteServiceUnavailableException : Exception
     {
         public RemoteServiceUnavailableException()

+ 5 - 0
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -230,6 +230,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <returns></returns>
         private string ExistsOnSystemPath(string filename)
         {
+            string inJellyfinPath = GetEncoderPathFromDirectory(System.AppContext.BaseDirectory, filename);
+            if (!string.IsNullOrEmpty(inJellyfinPath))
+            {
+                return inJellyfinPath;
+            }
             var values = Environment.GetEnvironmentVariable("PATH");
 
             foreach (var path in values.Split(Path.PathSeparator))

+ 5 - 0
MediaBrowser.Model/System/PublicSystemInfo.cs

@@ -25,6 +25,11 @@ namespace MediaBrowser.Model.System
         /// </summary>
         /// <value>The version.</value>
         public string Version { get; set; }
+        
+        /// <summary>
+        /// The product name. This is the AssemblyProduct name.
+        /// </summary>
+        public string ProductName { get; set; }
 
         /// <summary>
         /// Gets or sets the operating system.

+ 0 - 4
MediaBrowser.Model/System/SystemInfo.cs

@@ -32,10 +32,6 @@ namespace MediaBrowser.Model.System
         /// <value>The display name of the operating system.</value>
         public string OperatingSystemDisplayName { get; set; }
 
-        /// <summary>
-        /// The product name. This is the AssemblyProduct name.
-        /// </summary>
-        public string ProductName { get; set; }
 
         /// <summary>
         /// Get or sets the package name.

+ 1 - 1
MediaBrowser.WebDashboard/jellyfin-web

@@ -1 +1 @@
-Subproject commit ec5a3b6e5efb6041153b92818aee562f20ee994d
+Subproject commit 874b51234ee4e1f01e2e7410980a1003f316d6a2

+ 2 - 2
SharedVersion.cs

@@ -1,4 +1,4 @@
 using System.Reflection;
 
-[assembly: AssemblyVersion("10.2.2")]
-[assembly: AssemblyFileVersion("10.2.2")]
+[assembly: AssemblyVersion("10.3.0")]
+[assembly: AssemblyFileVersion("10.3.0")]

+ 1 - 1
build.yaml

@@ -1,7 +1,7 @@
 ---
 # We just wrap `build` so this is really it
 name: "jellyfin"
-version: "10.2.2"
+version: "10.3.0"
 packages:
   - debian-package-x64
   - debian-package-armhf

+ 15 - 2
bump_version

@@ -54,6 +54,7 @@ old_version="$(
     grep "AssemblyVersion" ${shared_version_file} \
         | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/'
 )"
+echo $old_version
 
 # Set the shared version to the specified new_version
 old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
@@ -62,9 +63,11 @@ sed -i "s/${old_version_sed}/${new_version_sed}/g" ${shared_version_file}
 
 old_version="$(
     grep "version:" ${build_file} \
-        | sed -E 's/version: "([0-9\.]+)"/\1/'
+        | sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/'
 )"
+echo $old_version
 
+# Set the build.yaml version to the specified new_version
 old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
 sed -i "s/${old_version_sed}/${new_version}/g" ${build_file}
 
@@ -74,6 +77,16 @@ else
     new_version_deb="${new_version}-1"
 fi
 
+# Set the Dockerfile web version to the specified new_version
+old_version="$(
+    grep "JELLYFIN_WEB_VERSION=" Dockerfile \
+        | sed -E 's/ARG JELLYFIN_WEB_VERSION=([0-9\.]+[-a-z0-9]*)/\1/'
+)"
+echo $old_version
+
+old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
+sed -i "s/${old_version_sed}/${new_version}/g" Dockerfile*
+
 # Write out a temporary Debian changelog with our new stuff appended and some templated formatting
 debian_changelog_file="deployment/debian-package-x64/pkg-src/changelog"
 debian_changelog_temp="$( mktemp )"
@@ -124,5 +137,5 @@ mv ${fedora_spec_temp} ${fedora_spec_file}
 rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir}
 
 # Stage the changed files for commit
-git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file}
+git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file} Dockerfile*
 git status

+ 6 - 0
deployment/debian-package-x64/pkg-src/changelog

@@ -1,3 +1,9 @@
+jellyfin (10.3.0-1) unstable; urgency=medium
+
+  * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0
+
+ -- Jellyfin Packaging Team <packaging@jellyfin.org>  Fri, 19 Apr 2019 14:24:29 -0400
+
 jellyfin (10.2.2-1) unstable; urgency=medium
 
   * jellyfin:

+ 3 - 1
deployment/fedora-package-x64/pkg-src/jellyfin.spec

@@ -7,7 +7,7 @@
 %endif
 
 Name:           jellyfin
-Version:        10.2.2
+Version:        10.3.0
 Release:        1%{?dist}
 Summary:        The Free Software Media Browser
 License:        GPLv2
@@ -140,6 +140,8 @@ fi
 %systemd_postun_with_restart jellyfin.service
 
 %changelog
+* Fri Apr 19 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
+- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0
 * Thu Feb 28 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
 - jellyfin:
 - PR968 Release 10.2.z copr autobuild

+ 5 - 1
deployment/windows/build-jellyfin.ps1

@@ -26,7 +26,10 @@ function Build-JellyFin {
             Write-Error "arm only supported with Windows 8 or higher"
             exit
         }
-    dotnet publish -c $BuildType -r "$windowsversion-$Architecture" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
+    Write-Verbose "windowsversion-Architecture: $windowsversion-$Architecture"
+    Write-Verbose "InstallLocation: $InstallLocation"
+    Write-Verbose "DotNetVerbosity: $DotNetVerbosity"
+    dotnet publish -c $BuildType -r `"$windowsversion-$Architecture`" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
 }
 
 function Install-FFMPEG {
@@ -73,6 +76,7 @@ function Install-NSSM {
         Write-Warning "NSSM will not be installed"
     }else{
          Write-Verbose "Downloading NSSM"
+         [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
          Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -UseBasicParsing -OutFile "$tempdir/nssm.zip" | Write-Verbose
     }
 

+ 5 - 5
deployment/windows/install-jellyfin.ps1

@@ -58,7 +58,7 @@ function Elevate-Window {
 
 if($Quiet.IsPresent -or $Quiet -eq $true){
     if([string]::IsNullOrEmpty($JellyfinLibraryLocation)){
-        $Script:JellyfinDataDir = "$env:AppData\jellyfin\"
+        $Script:JellyfinDataDir = "$env:LOCALAPPDATA\jellyfin\"
     }else{
         $Script:JellyfinDataDir = $JellyfinLibraryLocation
     }
@@ -82,7 +82,7 @@ if($Quiet.IsPresent -or $Quiet -eq $true){
     }else{
         $Script:InstallServiceAsUser = $true
         $Script:UserCredentials = $ServiceUser
-        $Script:JellyfinDataDir = "C:\Users\$($Script:UserCredentials.UserName)\Appdata\Roaming\jellyfin\"}
+        $Script:JellyfinDataDir = "$env:HOMEDRIVE\Users\$($Script:UserCredentials.UserName)\Appdata\Local\jellyfin\"}
     if($CreateDesktopShorcut.IsPresent -or $CreateDesktopShorcut -eq $true) {$Script:CreateShortcut = $true}else{$Script:CreateShortcut = $false}
     if($MigrateEmbyLibrary.IsPresent -or $MigrateEmbyLibrary -eq $true){$Script:MigrateLibrary = $true}else{$Script:MigrateLibrary = $false}
     if($LaunchJellyfin.IsPresent -or $LaunchJellyfin -eq $true){$Script:StartJellyfin = $true}else{$Script:StartJellyfin = $false}
@@ -131,7 +131,7 @@ if($Quiet.IsPresent -or $Quiet -eq $true){
 Add-Type -AssemblyName System.Windows.Forms
 [System.Windows.Forms.Application]::EnableVisualStyles()
 
-$Script:JellyFinDataDir = "$env:AppData\jellyfin\"
+$Script:JellyFinDataDir = "$env:LOCALAPPDATA\jellyfin\"
 $Script:DefaultJellyfinInstallDirectory = "$env:Appdata\jellyfin\"
 $Script:defaultEmbyDataDir = "$env:Appdata\Emby-Server\"
 $Script:InstallAsService = $False
@@ -392,7 +392,7 @@ $ServiceUserBox.DropDownStyle    = [System.Windows.Forms.ComboBoxStyle]::DropDow
 $GUIElementsCollection += $ServiceUserBox
 
 $MigrateLibraryCheck                       = New-Object system.Windows.Forms.CheckBox
-$MigrateLibraryCheck.text                  = "Import Emby Library"
+$MigrateLibraryCheck.text                  = "Import Emby/Old JF Library"
 $MigrateLibraryCheck.AutoSize              = $false
 $MigrateLibraryCheck.width                 = 160
 $MigrateLibraryCheck.height                = 20
@@ -401,7 +401,7 @@ $MigrateLibraryCheck.Font                  = 'Microsoft Sans Serif,10'
 $GUIElementsCollection += $MigrateLibraryCheck
 
 $LibraryMigrationLabel            = New-Object system.Windows.Forms.Label
-$LibraryMigrationLabel.text       = "Emby Library Path"
+$LibraryMigrationLabel.text       = "Emby/Old JF Library Path"
 $LibraryMigrationLabel.TextAlign  = [System.Drawing.ContentAlignment]::MiddleLeft
 $LibraryMigrationLabel.AutoSize   = $false
 $LibraryMigrationLabel.width      = 120