diff options
| -rw-r--r-- | .gitattributes | 2 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 | ||||
| -rw-r--r-- | Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 93 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs | 41 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Localization/Core/ko.json | 56 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Controller/LiveTv/IListingsProvider.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Model/Configuration/ServerConfiguration.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs | 55 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rwxr-xr-x | bump_version | 27 |
11 files changed, 177 insertions, 143 deletions
diff --git a/.gitattributes b/.gitattributes index d78b0459d..8ae599725 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ * text=auto eol=lf +*.png binary +*.jpg binary CONTRIBUTORS.md merge=union diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2f083dda4..96f90b831 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2724,7 +2724,7 @@ namespace Emby.Server.Implementations.Data if (elapsed >= SlowThreshold) { - Logger.LogWarning( + Logger.LogDebug( "{Method} query time (slow): {ElapsedMs}ms. Query: {Query}", methodName, elapsed, diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index d60f5c055..e4f98acb9 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -39,6 +39,7 @@ namespace Emby.Server.Implementations.HttpServer private readonly IHttpListener _socketListener; private readonly Func<Type, Func<string, object>> _funcParseFn; private readonly string _defaultRedirectPath; + private readonly string _baseUrlPrefix; private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>(); private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); @@ -58,6 +59,7 @@ namespace Emby.Server.Implementations.HttpServer _logger = logger; _config = config; _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; + _baseUrlPrefix = _config.Configuration.BaseUrl; _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; @@ -87,6 +89,20 @@ namespace Emby.Server.Implementations.HttpServer return _appHost.CreateInstance(type); } + private static string NormalizeUrlPath(string path) + { + if (path.StartsWith("/")) + { + // If the path begins with a leading slash, just return it as-is + return path; + } + else + { + // If the path does not begin with a leading slash, append one for consistency + return "/" + path; + } + } + /// <summary> /// Applies the request filters. Returns whether or not the request has been handled /// and no more processing should be done. @@ -208,7 +224,7 @@ namespace Emby.Server.Implementations.HttpServer } } - private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage) + private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace) { try { @@ -218,9 +234,9 @@ namespace Emby.Server.Implementations.HttpServer { _logger.LogError(ex, "Error processing request"); } - else if (logExceptionMessage) + else { - _logger.LogError(ex.Message); + _logger.LogError("Error processing request: {Message}", ex.Message); } var httpRes = httpReq.Response; @@ -233,8 +249,10 @@ namespace Emby.Server.Implementations.HttpServer var statusCode = GetStatusCode(ex); httpRes.StatusCode = statusCode; - httpRes.ContentType = "text/html"; - await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); + var errContent = NormalizeExceptionMessage(ex.Message); + httpRes.ContentType = "text/plain"; + httpRes.ContentLength = errContent.Length; + await httpRes.WriteAsync(errContent).ConfigureAwait(false); } catch (Exception errorEx) { @@ -471,22 +489,15 @@ namespace Emby.Server.Implementations.HttpServer urlToLog = GetUrlToLog(urlString); - if (string.Equals(localPath, "/" + _config.Configuration.BaseUrl + "/", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, "/" + _config.Configuration.BaseUrl, StringComparison.OrdinalIgnoreCase)) - { - httpRes.Redirect("/" + _config.Configuration.BaseUrl + "/" + _defaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(localPath, _baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase) + || string.IsNullOrEmpty(localPath) + || !localPath.StartsWith(_baseUrlPrefix)) { - httpRes.Redirect(_defaultRedirectPath); - return; - } - - if (string.IsNullOrEmpty(localPath)) - { - httpRes.Redirect("/" + _defaultRedirectPath); + // Always redirect back to the default path if the base prefix is invalid or missing + _logger.LogDebug("Normalizing a URL at {0}", localPath); + httpRes.Redirect(_baseUrlPrefix + "/" + _defaultRedirectPath); return; } @@ -509,22 +520,22 @@ namespace Emby.Server.Implementations.HttpServer } else { - await ErrorHandler(new FileNotFoundException(), httpReq, false, false).ConfigureAwait(false); + await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false); } } catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException) { - await ErrorHandler(ex, httpReq, false, false).ConfigureAwait(false); + await ErrorHandler(ex, httpReq, false).ConfigureAwait(false); } catch (SecurityException ex) { - await ErrorHandler(ex, httpReq, false, true).ConfigureAwait(false); + await ErrorHandler(ex, httpReq, false).ConfigureAwait(false); } catch (Exception ex) { var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase); - await ErrorHandler(ex, httpReq, logException, false).ConfigureAwait(false); + await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false); } finally { @@ -596,7 +607,7 @@ namespace Emby.Server.Implementations.HttpServer foreach (var route in clone) { - routes.Add(new RouteAttribute(NormalizeCustomRoutePath(_config.Configuration.BaseUrl, route.Path), route.Verbs) + routes.Add(new RouteAttribute(NormalizeCustomRoutePath(route.Path), route.Verbs) { Notes = route.Notes, Priority = route.Priority, @@ -651,36 +662,22 @@ namespace Emby.Server.Implementations.HttpServer return _socketListener.ProcessWebSocketRequest(context); } - // this method was left for compatibility with third party clients - private static string NormalizeEmbyRoutePath(string path) + private string NormalizeEmbyRoutePath(string path) { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/emby" + path; - } - - return "emby/" + path; + _logger.LogDebug("Normalizing /emby route"); + return _baseUrlPrefix + "/emby" + NormalizeUrlPath(path); } - // this method was left for compatibility with third party clients - private static string NormalizeMediaBrowserRoutePath(string path) + private string NormalizeMediaBrowserRoutePath(string path) { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/mediabrowser" + path; - } - - return "mediabrowser/" + path; + _logger.LogDebug("Normalizing /mediabrowser route"); + return _baseUrlPrefix + "/mediabrowser" + NormalizeUrlPath(path); } - private static string NormalizeCustomRoutePath(string baseUrl, string path) + private string NormalizeCustomRoutePath(string path) { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/" + baseUrl + path; - } - - return baseUrl + "/" + path; + _logger.LogDebug("Normalizing custom route {0}", path); + return _baseUrlPrefix + NormalizeUrlPath(path); } /// <inheritdoc /> diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index d8faceadb..6afcf567a 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -21,14 +21,6 @@ namespace Emby.Server.Implementations.Images public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder where T : BaseItem { - protected virtual IReadOnlyCollection<ImageType> SupportedImages { get; } - = new ImageType[] { ImageType.Primary }; - - protected IFileSystem FileSystem { get; private set; } - protected IProviderManager ProviderManager { get; private set; } - protected IApplicationPaths ApplicationPaths { get; private set; } - protected IImageProcessor ImageProcessor { get; set; } - protected BaseDynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) { ApplicationPaths = applicationPaths; @@ -37,6 +29,24 @@ namespace Emby.Server.Implementations.Images ImageProcessor = imageProcessor; } + protected IFileSystem FileSystem { get; } + + protected IProviderManager ProviderManager { get; } + + protected IApplicationPaths ApplicationPaths { get; } + + protected IImageProcessor ImageProcessor { get; set; } + + protected virtual IReadOnlyCollection<ImageType> SupportedImages { get; } + = new ImageType[] { ImageType.Primary }; + + /// <inheritdoc /> + public string Name => "Dynamic Image Provider"; + + protected virtual int MaxImageAgeDays => 7; + + public int Order => 0; + protected virtual bool Supports(BaseItem _) => true; public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) @@ -85,7 +95,8 @@ namespace Emby.Server.Implementations.Images return FetchToFileInternal(item, items, imageType, cancellationToken); } - protected async Task<ItemUpdateType> FetchToFileInternal(BaseItem item, + protected async Task<ItemUpdateType> FetchToFileInternal( + BaseItem item, IReadOnlyList<BaseItem> itemsWithImages, ImageType imageType, CancellationToken cancellationToken) @@ -181,8 +192,6 @@ namespace Emby.Server.Implementations.Images return outputPath; } - public string Name => "Dynamic Image Provider"; - protected virtual string CreateImage(BaseItem item, IReadOnlyCollection<BaseItem> itemsWithImages, string outputPathWithoutExtension, @@ -214,8 +223,6 @@ namespace Emby.Server.Implementations.Images throw new ArgumentException("Unexpected image type", nameof(imageType)); } - protected virtual int MaxImageAgeDays => 7; - public bool HasChanged(BaseItem item, IDirectoryService directoryServicee) { if (!Supports(item)) @@ -263,15 +270,9 @@ namespace Emby.Server.Implementations.Images protected virtual bool HasChangedByDate(BaseItem item, ItemImageInfo image) { var age = DateTime.UtcNow - image.DateModified; - if (age.TotalDays <= MaxImageAgeDays) - { - return false; - } - return true; + return age.TotalDays > MaxImageAgeDays; } - public int Order => 0; - protected string CreateSingleImage(IEnumerable<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType) { var image = itemsWithImages diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 9bf4d2797..959757a54 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -3,15 +3,15 @@ "AppDeviceValues": "앱: {0}, 디바이스: {1}", "Application": "애플리케이션", "Artists": "아티스트", - "AuthenticationSucceededWithUserName": "{0} 인증에 성공했습니다.", + "AuthenticationSucceededWithUserName": "{0} 인증이 완료되었습니다", "Books": "도서", - "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다.", + "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다", "Channels": "채널", "ChapterNameValue": "챕터 {0}", "Collections": "컬렉션", - "DeviceOfflineWithName": "{0}가 접속이 끊어졌습니다.", - "DeviceOnlineWithName": "{0}가 접속되었습니다.", - "FailedLoginAttemptWithUserName": "{0}에서 로그인이 실패했습니다.", + "DeviceOfflineWithName": "{0} 연결 끊김", + "DeviceOnlineWithName": "{0} 연결됨", + "FailedLoginAttemptWithUserName": "{0}가 로그인에 실패했습니다.", "Favorites": "즐겨찾기", "Folders": "폴더", "Genres": "장르", @@ -23,42 +23,42 @@ "HeaderFavoriteEpisodes": "좋아하는 에피소드", "HeaderFavoriteShows": "즐겨찾는 쇼", "HeaderFavoriteSongs": "좋아하는 노래", - "HeaderLiveTV": "TV 방송", + "HeaderLiveTV": "실시간 TV", "HeaderNextUp": "다음으로", "HeaderRecordingGroups": "녹화 그룹", "HomeVideos": "홈 비디오", "Inherit": "상속", - "ItemAddedWithName": "{0} 라이브러리에 추가됨", - "ItemRemovedWithName": "{0} 라이브러리에서 제거됨", + "ItemAddedWithName": "{0}가 라이브러리에 추가됨", + "ItemRemovedWithName": "{0}가 라이브러리에서 제거됨", "LabelIpAddressValue": "IP 주소: {0}", "LabelRunningTimeValue": "상영 시간: {0}", "Latest": "최근", - "MessageApplicationUpdated": "Jellyfin 서버 업데이트됨", - "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트됨", - "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션 업데이트 됨", - "MessageServerConfigurationUpdated": "서버 환경 설정 업데이드됨", + "MessageApplicationUpdated": "Jellyfin 서버가 업데이트되었습니다", + "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트되었습니다", + "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션이 업데이트되었습니다", + "MessageServerConfigurationUpdated": "서버 환경 설정이 업데이트되었습니다", "MixedContent": "혼합 콘텐츠", "Movies": "영화", "Music": "음악", "MusicVideos": "뮤직 비디오", - "NameInstallFailed": "{0} 설치 실패.", + "NameInstallFailed": "{0} 설치 실패", "NameSeasonNumber": "시즌 {0}", "NameSeasonUnknown": "알 수 없는 시즌", "NewVersionIsAvailable": "새 버전의 Jellyfin 서버를 사용할 수 있습니다.", "NotificationOptionApplicationUpdateAvailable": "애플리케이션 업데이트 사용 가능", "NotificationOptionApplicationUpdateInstalled": "애플리케이션 업데이트가 설치됨", - "NotificationOptionAudioPlayback": "오디오 재생을 시작함", + "NotificationOptionAudioPlayback": "오디오 재생이 시작됨", "NotificationOptionAudioPlaybackStopped": "오디오 재생이 중지됨", "NotificationOptionCameraImageUploaded": "카메라 이미지가 업로드됨", "NotificationOptionInstallationFailed": "설치 실패", - "NotificationOptionNewLibraryContent": "새 콘텐트가 추가됨", + "NotificationOptionNewLibraryContent": "새 콘텐츠가 추가됨", "NotificationOptionPluginError": "플러그인 실패", "NotificationOptionPluginInstalled": "플러그인이 설치됨", "NotificationOptionPluginUninstalled": "플러그인이 설치 제거됨", "NotificationOptionPluginUpdateInstalled": "플러그인 업데이트가 설치됨", - "NotificationOptionServerRestartRequired": "서버를 다시 시작하십시오", + "NotificationOptionServerRestartRequired": "서버 재시작 필요", "NotificationOptionTaskFailed": "예약 작업 실패", - "NotificationOptionUserLockedOut": "사용자가 잠겼습니다", + "NotificationOptionUserLockedOut": "잠긴 사용자", "NotificationOptionVideoPlayback": "비디오 재생을 시작함", "NotificationOptionVideoPlaybackStopped": "비디오 재생이 중지됨", "Photos": "사진", @@ -70,10 +70,10 @@ "ProviderValue": "제공자: {0}", "ScheduledTaskFailedWithName": "{0} 실패", "ScheduledTaskStartedWithName": "{0} 시작", - "ServerNameNeedsToBeRestarted": "{0} 를 재시작하십시오", - "Shows": "프로그램", + "ServerNameNeedsToBeRestarted": "{0}를 재시작해야합니다", + "Shows": "쇼", "Songs": "노래", - "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시후 다시시도 해주세요.", + "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 시도 해주세요.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다", "SubtitlesDownloadedForItem": "{0} 자막을 다운로드했습니다", @@ -85,13 +85,13 @@ "UserDeletedWithName": "사용자 {0} 삭제됨", "UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다", "UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다", - "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", + "UserOfflineFromDevice": "{1}로부터 {0}의 연결이 끊겼습니다", + "UserOnlineFromDevice": "{0}은 {1}에서 온라인 상태입니다", + "UserPasswordChangedWithName": "사용자 {0}의 암호가 변경되었습니다", + "UserPolicyUpdatedWithName": "{0}의 사용자 권한이 업데이트되었습니다", + "UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중", + "UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침", + "ValueHasBeenAddedToLibrary": "{0}이 미디어 라이브러리에 추가되었습니다", "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "버전 {0}" } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 6f1c111c6..3ab19769a 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -84,6 +84,8 @@ namespace Jellyfin.Server private static async Task StartApp(StartupOptions options) { + var stopWatch = new Stopwatch(); + stopWatch.Start(); ServerApplicationPaths appPaths = CreateApplicationPaths(options); // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager @@ -168,6 +170,10 @@ namespace Jellyfin.Server await appHost.RunStartupTasksAsync().ConfigureAwait(false); + stopWatch.Stop(); + + _logger.LogInformation("Startup complete {Time:g}", stopWatch.Elapsed); + // Block main thread until shutdown await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false); } diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs index c719ad81c..2ea0a748e 100644 --- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs +++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs @@ -10,10 +10,15 @@ namespace MediaBrowser.Controller.LiveTv public interface IListingsProvider { string Name { get; } + string Type { get; } + Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken); + Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings); + Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location); + Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 24e771403..b8abe49e3 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Configuration { public const int DefaultHttpPort = 8096; public const int DefaultHttpsPort = 8920; + private string _baseUrl; /// <summary> /// Gets or sets a value indicating whether [enable u pn p]. @@ -162,7 +163,33 @@ namespace MediaBrowser.Model.Configuration public bool SkipDeserializationForBasicTypes { get; set; } public string ServerName { get; set; } - public string BaseUrl { get; set; } + public string BaseUrl + { + get => _baseUrl; + set + { + // Normalize the start of the string + if (string.IsNullOrWhiteSpace(value)) + { + // If baseUrl is empty, set an empty prefix string + value = string.Empty; + } + else if (!value.StartsWith("/")) + { + // If baseUrl was not configured with a leading slash, append one for consistency + value = "/" + value; + } + + // Normalize the end of the string + if (value.EndsWith("/")) + { + // If baseUrl was configured with a trailing slash, remove it for consistency + value = value.Remove(value.Length - 1); + } + + _baseUrl = value; + } + } public string UICulture { get; set; } @@ -243,7 +270,7 @@ namespace MediaBrowser.Model.Configuration SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" }; SortRemoveWords = new[] { "the", "a", "an" }; - BaseUrl = "jellyfin"; + BaseUrl = string.Empty; UICulture = "en-US"; MetadataOptions = new[] diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs index 5cd0a6ab8..4abe6a943 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs @@ -14,11 +14,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB { public class TvDbClientManager { + private const string DefaultLanguage = "en"; + private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); private readonly IMemoryCache _cache; private readonly TvDbClient _tvDbClient; private DateTime _tokenCreatedAt; - private const string DefaultLanguage = "en"; public TvDbClientManager(IMemoryCache memoryCache) { @@ -102,39 +103,50 @@ namespace MediaBrowser.Providers.TV.TheTVDB return episodes; } - public Task<TvDbResponse<SeriesSearchResult[]>> GetSeriesByImdbIdAsync(string imdbId, string language, + public Task<TvDbResponse<SeriesSearchResult[]>> GetSeriesByImdbIdAsync( + string imdbId, + string language, CancellationToken cancellationToken) { var cacheKey = GenerateKey("series", imdbId, language); return TryGetValue(cacheKey, language,() => TvDbClient.Search.SearchSeriesByImdbIdAsync(imdbId, cancellationToken)); } - public Task<TvDbResponse<SeriesSearchResult[]>> GetSeriesByZap2ItIdAsync(string zap2ItId, string language, + public Task<TvDbResponse<SeriesSearchResult[]>> GetSeriesByZap2ItIdAsync( + string zap2ItId, + string language, CancellationToken cancellationToken) { var cacheKey = GenerateKey("series", zap2ItId, language); - return TryGetValue( cacheKey, language,() => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); + return TryGetValue(cacheKey, language, () => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); } - public Task<TvDbResponse<Actor[]>> GetActorsAsync(int tvdbId, string language, + public Task<TvDbResponse<Actor[]>> GetActorsAsync( + int tvdbId, + string language, CancellationToken cancellationToken) { var cacheKey = GenerateKey("actors", tvdbId, language); - return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); + return TryGetValue(cacheKey, language, () => TvDbClient.Series.GetActorsAsync(tvdbId, cancellationToken)); } - public Task<TvDbResponse<Image[]>> GetImagesAsync(int tvdbId, ImagesQuery imageQuery, string language, + public Task<TvDbResponse<Image[]>> GetImagesAsync( + int tvdbId, + ImagesQuery imageQuery, + string language, CancellationToken cancellationToken) { var cacheKey = GenerateKey("images", tvdbId, language, imageQuery); - return TryGetValue(cacheKey, language,() => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); + return TryGetValue(cacheKey, language, () => TvDbClient.Series.GetImagesAsync(tvdbId, imageQuery, cancellationToken)); } public Task<TvDbResponse<Language[]>> GetLanguagesAsync(CancellationToken cancellationToken) { - return TryGetValue("languages", null,() => TvDbClient.Languages.GetAllAsync(cancellationToken)); + return TryGetValue("languages", null, () => TvDbClient.Languages.GetAllAsync(cancellationToken)); } - public Task<TvDbResponse<EpisodesSummary>> GetSeriesEpisodeSummaryAsync(int tvdbId, string language, + public Task<TvDbResponse<EpisodesSummary>> GetSeriesEpisodeSummaryAsync( + int tvdbId, + string language, CancellationToken cancellationToken) { var cacheKey = GenerateKey("seriesepisodesummary", tvdbId, language); @@ -142,8 +154,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB () => TvDbClient.Series.GetEpisodesSummaryAsync(tvdbId, cancellationToken)); } - public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync(int tvdbId, int page, EpisodeQuery episodeQuery, - string language, CancellationToken cancellationToken) + public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync( + int tvdbId, + int page, + EpisodeQuery episodeQuery, + string language, + CancellationToken cancellationToken) { var cacheKey = GenerateKey(language, tvdbId, episodeQuery); @@ -151,7 +167,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB () => TvDbClient.Series.GetEpisodesAsync(tvdbId, page, episodeQuery, cancellationToken)); } - public Task<string> GetEpisodeTvdbId(EpisodeInfo searchInfo, string language, + public Task<string> GetEpisodeTvdbId( + EpisodeInfo searchInfo, + string language, CancellationToken cancellationToken) { searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), @@ -187,7 +205,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); } - public async Task<string> GetEpisodeTvdbId(int seriesTvdbId, EpisodeQuery episodeQuery, + public async Task<string> GetEpisodeTvdbId( + int seriesTvdbId, + EpisodeQuery episodeQuery, string language, CancellationToken cancellationToken) { @@ -197,8 +217,11 @@ namespace MediaBrowser.Providers.TV.TheTVDB return episodePage.Data.FirstOrDefault()?.Id.ToString(); } - public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync(int tvdbId, EpisodeQuery episodeQuery, - string language, CancellationToken cancellationToken) + public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync( + int tvdbId, + EpisodeQuery episodeQuery, + string language, + CancellationToken cancellationToken) { return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, language, cancellationToken); } @@ -8,7 +8,7 @@ <br/><br/> <a href="https://github.com/jellyfin/jellyfin"><img alt="GPL 2.0 License" src="https://img.shields.io/github/license/jellyfin/jellyfin.svg"/></a> <a href="https://github.com/jellyfin/jellyfin/releases"><img alt="Current Release" src="https://img.shields.io/github/release/jellyfin/jellyfin.svg"/></a> -<a href="https://translate.jellyfin.org/projects/jellyfin?utm_source=widget"><img alt="Translations" src="https://translate.jellyfin.org/widgets/jellyfin/-/svg-badge.svg"/></a> +<a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/?utm_source=widget"><img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-core/svg-badge.svg" alt="Translation status" /></a> <a href="https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=1"><img alt="Azure DevOps builds" src="https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/Jellyfin%20CI"></a> <a href="https://hub.docker.com/r/jellyfin/jellyfin"><img alt="Docker Pull Count" src="https://img.shields.io/docker/pulls/jellyfin/jellyfin.svg"/></a> </br> diff --git a/bump_version b/bump_version index 590020864..106dd7a78 100755 --- a/bump_version +++ b/bump_version @@ -24,33 +24,6 @@ fi shared_version_file="./SharedVersion.cs" build_file="./build.yaml" -if [[ -z $2 ]]; then - web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )" -else - web_branch="$2" -fi - -# Initialize submodules -git submodule update --init --recursive - -# configure branch -pushd MediaBrowser.WebDashboard/jellyfin-web - -if ! git diff-index --quiet HEAD --; then - popd - echo - echo "ERROR: Your 'jellyfin-web' submodule working directory is not clean!" - echo "This script will overwrite your unstaged and unpushed changes." - echo "Please do development on 'jellyfin-web' outside of the submodule." - exit 1 -fi - -git fetch --all -git checkout origin/${web_branch} -popd - -git add MediaBrowser.WebDashboard/jellyfin-web - new_version="$1" # Parse the version from the AssemblyVersion |
