aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--.github/pull_request_template.md2
-rw-r--r--.github/stale.yml2
-rw-r--r--Dockerfile11
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs2
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs8
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs93
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs41
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json48
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json60
-rw-r--r--Jellyfin.Server/Program.cs6
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs23
-rw-r--r--MediaBrowser.Controller/LiveTv/IListingsProvider.cs5
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs31
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs55
-rw-r--r--README.md12
-rwxr-xr-xbump_version27
-rw-r--r--deployment/centos-package-x64/Dockerfile12
-rwxr-xr-xdeployment/centos-package-x64/docker-build.sh2
-rw-r--r--deployment/windows/build-jellyfin.ps14
21 files changed, 282 insertions, 166 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/.github/pull_request_template.md b/.github/pull_request_template.md
index 967be0fb7..dc93d2c84 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,6 +1,6 @@
<!--
Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y).
-For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/ page.
+For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our documentation.
-->
**Changes**
diff --git a/.github/stale.yml b/.github/stale.yml
index ce9fb01a1..9ea0e3796 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -17,6 +17,6 @@ staleLabel: stale
markComment: >
Issues go stale after 90d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14d of inactivity.
If this issue is safe to close now please do so.
- If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
+ If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
diff --git a/Dockerfile b/Dockerfile
index 69cb5e0dd..a45576868 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,8 +17,14 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
+
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}
-# libfontconfig1 is required for Skia
+COPY --from=ffmpeg / /
+COPY --from=builder /jellyfin /jellyfin
+COPY --from=web-builder /dist /jellyfin/jellyfin-web
+# Install dependencies:
+# libfontconfig1: needed for Skia
+# mesa-va-drivers: needed for VAAPI
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 mesa-va-drivers \
@@ -27,9 +33,6 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
-COPY --from=ffmpeg / /
-COPY --from=builder /jellyfin /jellyfin
-COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index d22f2be81..f36d465dd 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -667,7 +667,7 @@ namespace Emby.Server.Implementations
{
await host.StartAsync().ConfigureAwait(false);
}
- catch (Exception ex)
+ catch
{
Logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again.");
throw;
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/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index a3201f0bc..6c0e32e05 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1099,7 +1099,9 @@ namespace Emby.Server.Implementations.Dto
Series episodeSeries = null;
- if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+ // this block will add the series poster for episodes without a poster
+ // TODO maybe remove the if statement entirely
+ //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
episodeSeries = episodeSeries ?? episode.Series;
if (episodeSeries != null)
@@ -1143,7 +1145,9 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+ // this block will add the series poster for seasons without a poster
+ // TODO maybe remove the if statement entirely
+ //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
series = series ?? season.Series;
if (series != null)
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/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
new file mode 100644
index 000000000..15aa0a8ee
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -0,0 +1,48 @@
+{
+ "HeaderLiveTV": "Netti-TV",
+ "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa",
+ "NameSeasonUnknown": "Tuntematon Kausi",
+ "NameSeasonNumber": "Kausi {0}",
+ "NameInstallFailed": "{0} asennus epäonnistui",
+ "MusicVideos": "Musiikkivideot",
+ "Music": "Musiikki",
+ "Movies": "Elokuvat",
+ "MixedContent": "Sekoitettu sisältö",
+ "MessageServerConfigurationUpdated": "Palvelimen konfiguraatio on päivitetty",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen konfiguraatio-osa {0} on päivitetty",
+ "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty {0}",
+ "MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
+ "Latest": "Viimeisin",
+ "LabelRunningTimeValue": "Kesto: {0}",
+ "LabelIpAddressValue": "IP-osoite: {0}",
+ "ItemRemovedWithName": "{0} poistettiin kirjastosta",
+ "ItemAddedWithName": "{0} lisättiin kirjastoon",
+ "Inherit": "Periä",
+ "HomeVideos": "Kotivideot",
+ "HeaderRecordingGroups": "Äänitysryhmä",
+ "HeaderNextUp": "Seuraavaksi",
+ "HeaderFavoriteSongs": "Lempikappaleet",
+ "HeaderFavoriteShows": "Lempisarjat",
+ "HeaderFavoriteEpisodes": "Lempijaksot",
+ "HeaderCameraUploads": "Kamerasta Ladatut",
+ "HeaderFavoriteArtists": "Lempiartistit",
+ "HeaderFavoriteAlbums": "Lempialbumit",
+ "HeaderContinueWatching": "Jatka Katsomista",
+ "HeaderAlbumArtists": "Albumiartistit",
+ "Genres": "Genret",
+ "Folders": "Kansiot",
+ "Favorites": "Suosikit",
+ "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}",
+ "DeviceOnlineWithName": "{0} on yhdistynyt",
+ "DeviceOfflineWithName": "{0} on katkaissut yhteytensä",
+ "Collections": "Kokoelmat",
+ "ChapterNameValue": "Luku: {0}",
+ "Channels": "Kanavat",
+ "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}",
+ "Books": "Kirjat",
+ "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti",
+ "Artists": "Artistit",
+ "Application": "Sovellus",
+ "AppDeviceValues": "Sovellus: {0}, Laite: {1}",
+ "Albums": "Albumit"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 554cce972..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} 자막을 다운로드했습니다",
@@ -83,15 +83,15 @@
"User": "사용자",
"UserCreatedWithName": "사용자 {0} 생성됨",
"UserDeletedWithName": "사용자 {0} 삭제됨",
- "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",
+ "UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
+ "UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
+ "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.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 3a15c3776..b05e8c949 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -24,6 +25,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Net.Http.Headers;
+using static MediaBrowser.Common.HexHelper;
namespace MediaBrowser.Api.LiveTv
{
@@ -599,6 +601,7 @@ namespace MediaBrowser.Api.LiveTv
{
public bool ValidateLogin { get; set; }
public bool ValidateListings { get; set; }
+ public string Pw { get; set; }
}
[Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
@@ -866,10 +869,30 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Post(AddListingProvider request)
{
+ if (request.Pw != null)
+ {
+ request.Password = GetHashedString(request.Pw);
+ }
+
+ request.Pw = null;
+
var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false);
return ToOptimizedResult(result);
}
+ /// <summary>
+ /// Gets the hashed string.
+ /// </summary>
+ private string GetHashedString(string str)
+ {
+ // SchedulesDirect requires a SHA1 hash of the user's password
+ // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token
+ using (SHA1 sha = SHA1.Create()) {
+ return ToHexString(
+ sha.ComputeHash(Encoding.UTF8.GetBytes(str)));
+ }
+ }
+
public void Delete(DeleteListingProvider request)
{
_liveTvManager.DeleteListingsProvider(request.Id);
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);
}
diff --git a/README.md b/README.md
index 584260d24..07d49fc51 100644
--- a/README.md
+++ b/README.md
@@ -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>
@@ -23,17 +23,17 @@
Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest!
-For further details, please see [our documentation page](https://jellyfin.readthedocs.io). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
+For further details, please see [our documentation page](https://docs.jellyfin.org/). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://docs.jellyfin.org/general/getting-help.html).
-For more information about the project, please see our [about page](https://jellyfin.readthedocs.io/en/latest/about/).
+For more information about the project, please see our [about page](https://docs.jellyfin.org/general/about.html).
<p align="center">
<strong>Want to get started?</strong>
-<em>Choose from <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/installing/">Prebuilt Packages</a> or <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/building/">Build from Source</a>, then see our <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/quick-start/">quick start guide</a>.</em>
+<em>Choose from <a href="https://docs.jellyfin.org/general/administration/installing.html">Prebuilt Packages</a> or <a href="https://docs.jellyfin.org/general/administration/building.html">Build from Source</a>, then see our <a href="https://docs.jellyfin.org/general/administration/quick-start.html">quick start guide</a>.</em>
</p>
<p align="center">
<strong>Want to contribute?</strong>
-<em>Check out <a href="https://jellyfin.readthedocs.io/en/latest/contributor-docs/contributing/">our documentation for guidelines</a>.</em>
+<em>Check out <a href="https://docs.jellyfin.org/general/contributing/index.html">our documentation for guidelines</a>.</em>
</p>
<p align="center">
<strong>New idea or improvement?</strong>
@@ -41,5 +41,5 @@ For more information about the project, please see our [about page](https://jell
</p>
<p align="center">
<strong>Something not working right?</strong>
-<em>Open an <a href="https://jellyfin.readthedocs.io/en/latest/contributor-docs/issues/">Issue</a>.</em>
+<em>Open an <a href="https://docs.jellyfin.org/general/contributing/issues.html">Issue</a>.</em>
</p>
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
diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/centos-package-x64/Dockerfile
index 99f538bc2..855b0a479 100644
--- a/deployment/centos-package-x64/Dockerfile
+++ b/deployment/centos-package-x64/Dockerfile
@@ -13,17 +13,19 @@ RUN yum update -y \
&& yum install -y epel-release
# Install build dependencies
-RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs wget git
+RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel wget git
+
+# Install recent NodeJS and Yarn
+RUN wget -O- https://raw.githubusercontent.com/creationix/nvm/v0.35.0/install.sh | /bin/bash \
+ && source "$HOME/.nvm/nvm.sh" \
+ && nvm install v8 \
+ && npm install -g yarn
# Install DotNET SDK
RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \
&& rpmdev-setuptree \
&& yum install -y dotnet-sdk-${SDK_VERSION}
-# Install yarn package manager
-RUN wget -q -O /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
- && yum install -y yarn
-
# Create symlinks and directories
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
&& mkdir -p ${SOURCE_DIR}/SPECS \
diff --git a/deployment/centos-package-x64/docker-build.sh b/deployment/centos-package-x64/docker-build.sh
index 014f582f0..18e10661c 100755
--- a/deployment/centos-package-x64/docker-build.sh
+++ b/deployment/centos-package-x64/docker-build.sh
@@ -18,6 +18,8 @@ pushd ${web_build_dir}
if [[ -n ${web_branch} ]]; then
checkout -b origin/${web_branch}
fi
+source "$HOME/.nvm/nvm.sh"
+nvm use v8
yarn install
mkdir -p ${web_target}
mv dist/* ${web_target}/
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index 454c89bf6..c4fb4b995 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -51,7 +51,7 @@ function Install-FFMPEG {
Write-Warning "FFMPEG will not be installed"
}elseif($Architecture -eq 'x64'){
Write-Verbose "Downloading 64 bit FFMPEG"
- Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0.2-win64-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
+ Invoke-WebRequest -Uri https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
}else{
Write-Verbose "Downloading 32 bit FFMPEG"
Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
@@ -60,7 +60,7 @@ function Install-FFMPEG {
Expand-Archive "$tempdir/ffmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" -Force | Write-Verbose
if($Architecture -eq 'x64'){
Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.0.2-win64-shared/bin" | ForEach-Object {
+ Get-ChildItem "$tempdir/ffmpeg" | ForEach-Object {
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
}
}else{