Merged with latest master

pull/4125/head
Jim Cartlidge 4 years ago
commit fcd1b2f0e4

@ -11,7 +11,11 @@
"cwd": "${workspaceFolder}/Jellyfin.Server",
"console": "internalConsole",
"stopAtEntry": false,
"internalConsoleOptions": "openOnSessionStart"
"internalConsoleOptions": "openOnSessionStart",
"serverReadyAction": {
"action": "openExternally",
"pattern": "Overriding address\\(es\\) \\'(https?:\\S+)\\'",
}
},
{
"name": ".NET Core Launch (nowebclient)",

@ -103,6 +103,7 @@
- [sl1288](https://github.com/sl1288)
- [sorinyo2004](https://github.com/sorinyo2004)
- [sparky8251](https://github.com/sparky8251)
- [spookbits](https://github.com/spookbits)
- [stanionascu](https://github.com/stanionascu)
- [stevehayles](https://github.com/stevehayles)
- [SuperSandro2000](https://github.com/SuperSandro2000)

@ -881,7 +881,10 @@ namespace Emby.Dlna.PlayTo
return null;
}
mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
if (_mediaSourceManager != null)
{
mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
}
return mediaSource;
}

@ -235,13 +235,13 @@ namespace Emby.Dlna.Server
.Append(SecurityElement.Escape(service.ServiceId ?? string.Empty))
.Append("</serviceId>");
builder.Append("<SCPDURL>")
.Append(BuildUrl(service.ScpdUrl))
.Append(BuildUrl(service.ScpdUrl, true))
.Append("</SCPDURL>");
builder.Append("<controlURL>")
.Append(BuildUrl(service.ControlUrl))
.Append(BuildUrl(service.ControlUrl, true))
.Append("</controlURL>");
builder.Append("<eventSubURL>")
.Append(BuildUrl(service.EventSubUrl))
.Append(BuildUrl(service.EventSubUrl, true))
.Append("</eventSubURL>");
builder.Append("</service>");
@ -250,7 +250,13 @@ namespace Emby.Dlna.Server
builder.Append("</serviceList>");
}
private string BuildUrl(string url)
/// <summary>
/// Builds a valid url for inclusion in the xml.
/// </summary>
/// <param name="url">Url to include.</param>
/// <param name="absoluteUrl">Optional. When set to true, the absolute url is always used.</param>
/// <returns>The url to use for the element.</returns>
private string BuildUrl(string url, bool absoluteUrl = false)
{
if (string.IsNullOrEmpty(url))
{
@ -261,7 +267,7 @@ namespace Emby.Dlna.Server
url = "/dlna/" + _serverUdn + "/" + url;
if (EnableAbsoluteUrls)
if (EnableAbsoluteUrls || absoluteUrl)
{
url = _serverAddress.TrimEnd('/') + url;
}

@ -1,51 +0,0 @@
using System;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Browser
{
/// <summary>
/// Assists in opening application URLs in an external browser.
/// </summary>
public static class BrowserLauncher
{
/// <summary>
/// Opens the home page of the web client.
/// </summary>
/// <param name="appHost">The app host.</param>
public static void OpenWebApp(IServerApplicationHost appHost)
{
TryOpenUrl(appHost, "/web/index.html");
}
/// <summary>
/// Opens the swagger API page.
/// </summary>
/// <param name="appHost">The app host.</param>
public static void OpenSwaggerPage(IServerApplicationHost appHost)
{
TryOpenUrl(appHost, "/api-docs/swagger");
}
/// <summary>
/// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
/// </summary>
/// <param name="appHost">The application host.</param>
/// <param name="relativeUrl">The URL to open, relative to the server base URL.</param>
private static void TryOpenUrl(IServerApplicationHost appHost, string relativeUrl)
{
try
{
string baseUrl = appHost.GetLocalApiUrl("localhost");
appHost.LaunchUrl(baseUrl + relativeUrl);
}
catch (Exception ex)
{
var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
}
}
}
}

@ -2263,7 +2263,6 @@ namespace Emby.Server.Implementations.Data
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
}
private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Series",
@ -3291,7 +3290,6 @@ namespace Emby.Server.Implementations.Data
}
}
var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
var statementTexts = new List<string>();
@ -6006,7 +6004,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
/// <summary>
/// Gets the chapter.
/// </summary>

@ -468,7 +468,6 @@ namespace Emby.Server.Implementations.Dto
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Name = item.Album,
Limit = 1
});
if (parentAlbumIds.Count > 0)
@ -1139,6 +1138,7 @@ namespace Emby.Server.Implementations.Dto
if (episodeSeries != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
AttachPrimaryImageAspectRatio(dto, episodeSeries);
}
}
@ -1185,6 +1185,7 @@ namespace Emby.Server.Implementations.Dto
if (series != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
AttachPrimaryImageAspectRatio(dto, series);
}
}
}
@ -1431,7 +1432,7 @@ namespace Emby.Server.Implementations.Dto
return null;
}
return width / height;
return (double)width / height;
}
}
}

@ -1,83 +0,0 @@
using System.Threading.Tasks;
using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Plugins;
using Microsoft.Extensions.Configuration;
namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class StartupWizard.
/// </summary>
public sealed class StartupWizard : IServerEntryPoint
{
private readonly IServerApplicationHost _appHost;
private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _config;
private readonly IStartupOptions _startupOptions;
/// <summary>
/// Initializes a new instance of the <see cref="StartupWizard"/> class.
/// </summary>
/// <param name="appHost">The application host.</param>
/// <param name="appConfig">The application configuration.</param>
/// <param name="config">The configuration manager.</param>
/// <param name="startupOptions">The application startup options.</param>
public StartupWizard(
IServerApplicationHost appHost,
IConfiguration appConfig,
IServerConfigurationManager config,
IStartupOptions startupOptions)
{
_appHost = appHost;
_appConfig = appConfig;
_config = config;
_startupOptions = startupOptions;
}
/// <inheritdoc />
public Task RunAsync()
{
Run();
return Task.CompletedTask;
}
private void Run()
{
if (!_appHost.CanLaunchWebBrowser)
{
return;
}
// Always launch the startup wizard if possible when it has not been completed
if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
{
BrowserLauncher.OpenWebApp(_appHost);
return;
}
// Do nothing if the web app is configured to not run automatically
if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
{
return;
}
// Launch the swagger page if the web client is not hosted, otherwise open the web client
if (_appConfig.HostWebClient())
{
BrowserLauncher.OpenWebApp(_appHost);
}
else
{
BrowserLauncher.OpenSwaggerPage(_appHost);
}
}
/// <inheritdoc />
public void Dispose()
{
}
}
}

@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly object _syncLock = new object();
private Timer _updateTimer;
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager)
{
_userDataManager = userDataManager;

@ -16,11 +16,6 @@ namespace Emby.Server.Implementations
/// </summary>
bool IsService { get; }
/// <summary>
/// Gets the value of the --noautorunwebapp command line option.
/// </summary>
bool NoAutoRunWebApp { get; }
/// <summary>
/// Gets the value of the --package-name command line option.
/// </summary>

@ -874,7 +874,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; }
}
public class Headends
{
public string headend { get; set; }
@ -886,8 +885,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; }
}
public class Map
{
public string stationID { get; set; }
@ -971,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<string> date { get; set; }
}
public class Rating
{
public string body { get; set; }
@ -1017,8 +1011,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public string isPremiereOrFinale { get; set; }
}
public class MetadataSchedule
{
public string modified { get; set; }

@ -2135,6 +2135,7 @@ namespace Emby.Server.Implementations.LiveTv
}
private bool _disposed = false;
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>

@ -563,6 +563,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
var tunerCount = info.TunerCount;
if (tunerCount > 0)
{
var tunerHostId = info.Id;
var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase));
if (liveStreams.Count() >= tunerCount)
{
throw new LiveTvConflictException("HDHomeRun simultaneous stream limit has been reached.");
}
}
var profile = streamId.Split('_')[0];
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);

@ -1,19 +1,19 @@
{
"Artists": "Kunstenare",
"Channels": "Kanale",
"Folders": "Fouers",
"Favorites": "Gunstelinge",
"Folders": "Lêergidse",
"Favorites": "Gunstellinge",
"HeaderFavoriteShows": "Gunsteling Vertonings",
"ValueSpecialEpisodeName": "Spesiale - {0}",
"HeaderAlbumArtists": "Album Kunstenaars",
"Books": "Boeke",
"HeaderNextUp": "Volgende",
"Movies": "Rolprente",
"Shows": "Program",
"HeaderContinueWatching": "Hou Aan Kyk",
"Movies": "Flieks",
"Shows": "Televisie Reekse",
"HeaderContinueWatching": "Kyk Verder",
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
"Photos": "Fotos",
"Playlists": "Speellysse",
"Playlists": "Snitlyste",
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
"HeaderFavoriteAlbums": "Gunsteling Albums",
"Sync": "Sinkroniseer",
@ -23,7 +23,7 @@
"DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings",
"Inherit": "Ontvang",
"HeaderLiveTV": "Live TV",
"HeaderLiveTV": "Lewendige TV",
"Application": "Program",
"AppDeviceValues": "App: {0}, Toestel: {1}",
"VersionNumber": "Weergawe {0}",
@ -95,5 +95,23 @@
"TasksChannelsCategory": "Internet kanale",
"TasksApplicationCategory": "aansoek",
"TasksLibraryCategory": "biblioteek",
"TasksMaintenanceCategory": "onderhoud"
"TasksMaintenanceCategory": "onderhoud",
"TaskCleanCacheDescription": "Vee kasregister lêers uit wat nie meer deur die stelsel benodig word nie.",
"TaskCleanCache": "Reinig Kasgeheue Lêergids",
"TaskDownloadMissingSubtitlesDescription": "Soek aanlyn vir vermiste onderskrifte gebasseer op metadata verstellings.",
"TaskDownloadMissingSubtitles": "Laai vermiste onderskrifte af",
"TaskRefreshChannelsDescription": "Vervris internet kanaal inligting.",
"TaskRefreshChannels": "Vervris Kanale",
"TaskCleanTranscodeDescription": "Vee transkodering lêers uit wat ouer is as een dag.",
"TaskCleanTranscode": "Reinig Transkoderings Leêrbinder",
"TaskUpdatePluginsDescription": "Laai opgedateerde inprop-sagteware af en installeer inprop-sagteware wat verstel is om outomaties op te dateer.",
"TaskUpdatePlugins": "Dateer Inprop-Sagteware Op",
"TaskRefreshPeopleDescription": "Vervris metadata oor akteurs en regisseurs in u media versameling.",
"TaskRefreshPeople": "Vervris Mense",
"TaskCleanLogsDescription": "Vee loglêers wat ouer as {0} dae is uit.",
"TaskCleanLogs": "Reinig Loglêer Lêervouer",
"TaskRefreshLibraryDescription": "Skandeer u media versameling vir nuwe lêers en verfris metadata.",
"TaskRefreshLibrary": "Skandeer Media Versameling",
"TaskRefreshChapterImagesDescription": "Maak kleinkiekeis (fotos) vir films wat hoofstukke het.",
"TaskRefreshChapterImages": "Verkry Hoofstuk Beelde"
}

@ -20,8 +20,8 @@
"NotificationOptionCameraImageUploaded": "อัปโหลดภาพถ่ายแล้ว",
"NotificationOptionAudioPlaybackStopped": "หยุดเล่นเสียง",
"NotificationOptionAudioPlayback": "เริ่มเล่นเสียง",
"NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอพลิเคชันแล้ว",
"NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอพลิเคชัน",
"NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอพลิเคชันแล้ว",
"NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอพลิเคชัน",
"NewVersionIsAvailable": "เวอร์ชันใหม่ของเซิร์ฟเวอร์ Jellyfin พร้อมให้ดาวน์โหลดแล้ว",
"NameSeasonUnknown": "ไม่ทราบซีซัน",
"NameSeasonNumber": "ซีซัน {0}",
@ -65,8 +65,8 @@
"Books": "หนังสือ",
"AuthenticationSucceededWithUserName": "{0} ยืนยันตัวสำเร็จแล้ว",
"Artists": "ศิลปิน",
"Application": "แอพลิเคชัน",
"AppDeviceValues": "แอ: {0}, อุปกรณ์: {1}",
"Application": "แอพลิเคชัน",
"AppDeviceValues": "แอ: {0}, อุปกรณ์: {1}",
"Albums": "อัลบั้ม",
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
"ScheduledTaskFailedWithName": "{0} ล้มเหลว",
@ -92,7 +92,7 @@
"TaskCleanCacheDescription": "ลบไฟล์แคชที่ระบบไม่ต้องการ",
"TaskCleanCache": "ล้างไดเรกทอรีแคช",
"TasksChannelsCategory": "ช่องอินเทอร์เน็ต",
"TasksApplicationCategory": "แอพลิเคชัน",
"TasksApplicationCategory": "แอพลิเคชัน",
"TasksLibraryCategory": "ไลบรารี",
"TasksMaintenanceCategory": "ปิดซ่อมบำรุง",
"VersionNumber": "เวอร์ชัน {0}",

@ -15,7 +15,7 @@
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
"Albums": "Albums",
"Artists": "Các Nghệ Sĩ",
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình thông tin chi tiết.",
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
"TaskDownloadMissingSubtitles": "Tải xuống phụ đề bị thiếu",
"TaskRefreshChannelsDescription": "Làm mới thông tin kênh internet.",
"TaskRefreshChannels": "Làm Mới Kênh",

@ -155,7 +155,12 @@ namespace Emby.Server.Implementations.Updates
var result = new List<PackageInfo>();
foreach (RepositoryInfo repository in _config.Configuration.PluginRepositories)
{
result.AddRange(await GetPackages(repository.Url, cancellationToken).ConfigureAwait(true));
foreach (var package in await GetPackages(repository.Url, cancellationToken).ConfigureAwait(true))
{
package.repositoryName = repository.Name;
package.repositoryUrl = repository.Url;
result.Add(package);
}
}
return result;
@ -393,6 +398,7 @@ namespace Emby.Server.Implementations.Updates
// Ignore any exceptions.
}
}
stream.Position = 0;
_zipClient.ExtractAllFromZip(stream, targetDir, true);

@ -292,7 +292,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="NoContentResult"/>.
/// </returns>
[HttpPost("Items/RemoteSearch/Apply/{id}")]
[HttpPost("Items/RemoteSearch/Apply/{itemId}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> ApplySearchCriteria(

@ -77,6 +77,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="name">Package name.</param>
/// <param name="assemblyGuid">GUID of the associated assembly.</param>
/// <param name="version">Optional version. Defaults to latest version.</param>
/// <param name="repositoryUrl">Optional. Specify the repository to install from.</param>
/// <response code="204">Package found.</response>
/// <response code="404">Package not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the package could not be found.</returns>
@ -87,9 +88,16 @@ namespace Jellyfin.Api.Controllers
public async Task<ActionResult> InstallPackage(
[FromRoute, Required] string name,
[FromQuery] string? assemblyGuid,
[FromQuery] string? version)
[FromQuery] string? version,
[FromQuery] string? repositoryUrl)
{
var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
if (!string.IsNullOrEmpty(repositoryUrl))
{
packages = packages.Where(p => p.repositoryUrl.Equals(repositoryUrl, StringComparison.OrdinalIgnoreCase))
.ToList();
}
var package = _installationManager.GetCompatibleVersions(
packages,
name,

@ -505,17 +505,17 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Initiates the forgot password process for a local user.
/// </summary>
/// <param name="enteredUsername">The entered username.</param>
/// <param name="forgotPasswordRequest">The forgot password request containing the entered username.</param>
/// <response code="200">Password reset process started.</response>
/// <returns>A <see cref="Task"/> containing a <see cref="ForgotPasswordResult"/>.</returns>
[HttpPost("ForgotPassword")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ForgotPasswordResult>> ForgotPassword([FromBody] string? enteredUsername)
public async Task<ActionResult<ForgotPasswordResult>> ForgotPassword([FromBody, Required] ForgotPasswordDto forgotPasswordRequest)
{
var isLocal = HttpContext.IsLocal()
|| _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp());
var result = await _userManager.StartForgotPasswordProcess(enteredUsername, isLocal).ConfigureAwait(false);
var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false);
return result;
}

@ -17,8 +17,8 @@
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="5.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="5.6.3" />
</ItemGroup>
<ItemGroup>

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.UserDtos
{
/// <summary>
/// Forgot Password request body DTO.
/// </summary>
public class ForgotPasswordDto
{
/// <summary>
/// Gets or sets the entered username to have its password reset.
/// </summary>
[Required]
public string? EnteredUsername { get; set; }
}
}

@ -18,8 +18,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BlurHashSharp" Version="1.1.0" />
<PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.1.0" />
<PackageReference Include="BlurHashSharp" Version="1.1.1" />
<PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.1.1" />
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.2" />
</ItemGroup>

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
@ -13,6 +13,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
</PropertyGroup>
<ItemGroup>
@ -23,10 +24,6 @@
<EmbeddedResource Include="Resources/Configuration/*" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
@ -53,7 +50,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.3" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.1" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.4" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
</ItemGroup>

@ -2,6 +2,8 @@
"profiles": {
"Jellyfin.Server": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:8096",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@ -12,6 +14,16 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"commandLineArgs": "--nowebclient"
},
"Jellyfin.Server (API Docs)": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api-docs/swagger",
"applicationUrl": "http://localhost:8096",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"commandLineArgs": "--nowebclient"
}
}
}

@ -63,10 +63,6 @@ namespace Jellyfin.Server
[Option("service", Required = false, HelpText = "Run as headless service.")]
public bool IsService { get; set; }
/// <inheritdoc />
[Option("noautorunwebapp", Required = false, HelpText = "Run headless if startup wizard is complete.")]
public bool NoAutoRunWebApp { get; set; }
/// <inheritdoc />
[Option("package-name", Required = false, HelpText = "Used when packaging Jellyfin (example, synology).")]
public string? PackageName { get; set; }

@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.Channels
/// Indicates if a sort ascending/descending toggle is supported or not.
/// </summary>
public bool SupportsSortOrderToggle { get; set; }
/// <summary>
/// Gets or sets the automatic refresh levels.
/// </summary>
@ -53,6 +54,7 @@ namespace MediaBrowser.Controller.Channels
/// </summary>
/// <value>The daily download limit.</value>
public int? DailyDownloadLimit { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [supports downloading].
/// </summary>

@ -90,7 +90,6 @@ namespace MediaBrowser.Controller.Entities.Audio
var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
if (ParentIndexNumber.HasValue)
{
songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;

@ -197,6 +197,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool SupportsRemoteImageDownloading => true;
private string _name;
/// <summary>
/// Gets or sets the name.
/// </summary>
@ -661,6 +662,7 @@ namespace MediaBrowser.Controller.Entities
}
private string _forcedSortName;
/// <summary>
/// Gets or sets the name of the forced sort.
/// </summary>

@ -1386,7 +1386,6 @@ namespace MediaBrowser.Controller.Entities
}
}
/// <summary>
/// Gets the linked children.
/// </summary>

@ -21,7 +21,5 @@ namespace MediaBrowser.Controller.Entities
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
List<MediaStream> GetMediaStreams();
}
}

@ -16,7 +16,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override Folder LatestItemsIndexContainer => AlbumEntity;
[JsonIgnore]
public PhotoAlbum AlbumEntity
{

@ -450,7 +450,6 @@ namespace MediaBrowser.Controller.Entities.TV
});
}
protected override bool GetBlockUnratedValue(User user)
{
return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series.ToString());

@ -258,7 +258,6 @@ namespace MediaBrowser.Controller.Entities
IncludeItemTypes = new[] { typeof(Movie).Name },
Recursive = true,
EnableTotalRecordCount = false
}).Items
.SelectMany(i => i.Genres)
.DistinctNames()

@ -111,5 +111,4 @@ namespace MediaBrowser.Controller.IO
return returnResult;
}
}
}

@ -77,6 +77,7 @@ namespace MediaBrowser.Controller.Library
MusicArtist GetArtist(string name);
MusicArtist GetArtist(string name, DtoOptions options);
/// <summary>
/// Gets a Studio.
/// </summary>
@ -234,6 +235,7 @@ namespace MediaBrowser.Controller.Library
/// Occurs when [item updated].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemUpdated;
/// <summary>
/// Occurs when [item removed].
/// </summary>

@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Library
/// <param name="itemId">The item identifier.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
List<MediaStream> GetMediaStreams(Guid itemId);
/// <summary>
/// Gets the media streams.
/// </summary>
/// <param name="mediaSourceId">The media source identifier.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
List<MediaStream> GetMediaStreams(string mediaSourceId);
/// <summary>
/// Gets the media streams.
/// </summary>

@ -156,6 +156,7 @@ namespace MediaBrowser.Controller.Library
}
// REVIEW: @bond
/// <summary>
/// Gets the physical locations.
/// </summary>

@ -231,6 +231,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Saves the tuner host.
/// </summary>
Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
/// <summary>
/// Saves the listing provider.
/// </summary>

@ -56,7 +56,6 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken);
}
public interface IConfigurableTunerHost

@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The tuners.</value>
public List<LiveTvTunerInfo> Tuners { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is visible.
/// </summary>

@ -35,6 +35,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The overview.</value>
public string Overview { get; set; }
/// <summary>
/// Gets or sets the short overview.
/// </summary>
@ -169,31 +170,37 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The production year.</value>
public int? ProductionYear { get; set; }
/// <summary>
/// Gets or sets the home page URL.
/// </summary>
/// <value>The home page URL.</value>
public string HomePageUrl { get; set; }
/// <summary>
/// Gets or sets the series identifier.
/// </summary>
/// <value>The series identifier.</value>
public string SeriesId { get; set; }
/// <summary>
/// Gets or sets the show identifier.
/// </summary>
/// <value>The show identifier.</value>
public string ShowId { get; set; }
/// <summary>
/// Gets or sets the season number.
/// </summary>
/// <value>The season number.</value>
public int? SeasonNumber { get; set; }
/// <summary>
/// Gets or sets the episode number.
/// </summary>
/// <value>The episode number.</value>
public int? EpisodeNumber { get; set; }
/// <summary>
/// Gets or sets the etag.
/// </summary>

@ -187,6 +187,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
public bool? HasImage { get; set; }
/// <summary>
/// Gets or sets the show identifier.
/// </summary>

@ -113,6 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
// Program properties
public int? SeasonNumber { get; set; }
/// <summary>
/// Gets or sets the episode number.
/// </summary>

@ -109,7 +109,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
return _mediaEncoder.SupportsHwaccel("vaapi");
}
/// <summary>
@ -508,6 +507,7 @@ namespace MediaBrowser.Controller.MediaEncoding
arg.Append("-hwaccel qsv ");
}
}
// While using SW decoder
else
{
@ -1441,7 +1441,6 @@ namespace MediaBrowser.Controller.MediaEncoding
var codec = outputAudioCodec ?? string.Empty;
int? transcoderChannelLimit;
if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
{
@ -2511,7 +2510,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return inputModifier;
}
public void AttachMediaSourceInfo(
EncodingJobInfo state,
MediaSourceInfo mediaSource,

@ -697,10 +697,12 @@ namespace MediaBrowser.Controller.MediaEncoding
/// The progressive.
/// </summary>
Progressive,
/// <summary>
/// The HLS.
/// </summary>
Hls,
/// <summary>
/// The dash.
/// </summary>

@ -100,6 +100,7 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;Guid&gt;.</returns>
QueryResult<Guid> GetItemIds(InternalItemsQuery query);
/// <summary>
/// Gets the items.
/// </summary>

@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.IO;
@ -11,11 +12,11 @@ namespace MediaBrowser.Controller.Providers
{
private readonly IFileSystem _fileSystem;
private readonly Dictionary<string, FileSystemMetadata[]> _cache = new Dictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, FileSystemMetadata[]> _cache = new ConcurrentDictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, FileSystemMetadata> _fileCache = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, FileSystemMetadata> _fileCache = new ConcurrentDictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, List<string>> _filePathCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, List<string>> _filePathCache = new ConcurrentDictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
public DirectoryService(IFileSystem fileSystem)
{
@ -24,14 +25,7 @@ namespace MediaBrowser.Controller.Providers
public FileSystemMetadata[] GetFileSystemEntries(string path)
{
if (!_cache.TryGetValue(path, out FileSystemMetadata[] entries))
{
entries = _fileSystem.GetFileSystemEntries(path).ToArray();
_cache[path] = entries;
}
return entries;
return _cache.GetOrAdd(path, p => _fileSystem.GetFileSystemEntries(p).ToArray());
}
public List<FileSystemMetadata> GetFiles(string path)
@ -51,21 +45,19 @@ namespace MediaBrowser.Controller.Providers
public FileSystemMetadata GetFile(string path)
{
if (!_fileCache.TryGetValue(path, out FileSystemMetadata file))
var result = _fileCache.GetOrAdd(path, p =>
{
file = _fileSystem.GetFileInfo(path);
var file = _fileSystem.GetFileInfo(p);
return file != null && file.Exists ? file : null;
});
if (file != null && file.Exists)
{
_fileCache[path] = file;
}
else
{
return null;
}
if (result == null)
{
// lets not store null results in the cache
_fileCache.TryRemove(path, out _);
}
return file;
return result;
}
public IReadOnlyList<string> GetFilePaths(string path)
@ -73,14 +65,12 @@ namespace MediaBrowser.Controller.Providers
public IReadOnlyList<string> GetFilePaths(string path, bool clearCache)
{
if (clearCache || !_filePathCache.TryGetValue(path, out List<string> result))
if (clearCache)
{
result = _fileSystem.GetFilePaths(path).ToList();
_filePathCache[path] = result;
_filePathCache.TryRemove(path, out _);
}
return result;
return _filePathCache.GetOrAdd(path, p => _fileSystem.GetFilePaths(p).ToList());
}
}
}

@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Resolvers
/// <param name="args">The args.</param>
/// <returns>BaseItem.</returns>
BaseItem ResolvePath(ItemResolveArgs args);
/// <summary>
/// Gets the priority.
/// </summary>

@ -22,7 +22,6 @@ namespace MediaBrowser.Controller.Session
private readonly ISessionManager _sessionManager;
private readonly ILogger _logger;
private readonly object _progressLock = new object();
private Timer _progressTimer;
private PlaybackProgressInfo _lastProgressInfo;

@ -498,7 +498,6 @@ namespace MediaBrowser.Model.Dlna
}
}
if (playMethods.Count > 0)
{
transcodeReasons.Clear();
@ -1431,6 +1430,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.AudioChannels:
{
if (string.IsNullOrEmpty(qualifier))
@ -1466,6 +1466,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.IsAvc:
{
if (!enableNonQualifiedConditions)
@ -1487,6 +1488,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.IsAnamorphic:
{
if (!enableNonQualifiedConditions)
@ -1508,6 +1510,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.IsInterlaced:
{
if (string.IsNullOrEmpty(qualifier))
@ -1539,6 +1542,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength:
@ -1550,6 +1554,7 @@ namespace MediaBrowser.Model.Dlna
// Not supported yet
break;
}
case ProfileConditionValue.RefFrames:
{
if (string.IsNullOrEmpty(qualifier))
@ -1585,6 +1590,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.VideoBitDepth:
{
if (string.IsNullOrEmpty(qualifier))
@ -1620,6 +1626,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.VideoProfile:
{
if (string.IsNullOrEmpty(qualifier))
@ -1643,6 +1650,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.Height:
{
if (!enableNonQualifiedConditions)
@ -1668,6 +1676,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.VideoBitrate:
{
if (!enableNonQualifiedConditions)
@ -1693,6 +1702,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.VideoFramerate:
{
if (!enableNonQualifiedConditions)
@ -1718,6 +1728,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.VideoLevel:
{
if (string.IsNullOrEmpty(qualifier))
@ -1743,6 +1754,7 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.Width:
{
if (!enableNonQualifiedConditions)

@ -276,7 +276,6 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
if (!item.IsDirectStream)
{
if (item.RequireNonAnamorphic)

@ -429,6 +429,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The album id.</value>
public Guid AlbumId { get; set; }
/// <summary>
/// Gets or sets the album image tag.
/// </summary>
@ -599,11 +600,13 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The trailer count.</value>
public int? TrailerCount { get; set; }
/// <summary>
/// Gets or sets the movie count.
/// </summary>
/// <value>The movie count.</value>
public int? MovieCount { get; set; }
/// <summary>
/// Gets or sets the series count.
/// </summary>
@ -611,16 +614,19 @@ namespace MediaBrowser.Model.Dto
public int? SeriesCount { get; set; }
public int? ProgramCount { get; set; }
/// <summary>
/// Gets or sets the episode count.
/// </summary>
/// <value>The episode count.</value>
public int? EpisodeCount { get; set; }
/// <summary>
/// Gets or sets the song count.
/// </summary>
/// <value>The song count.</value>
public int? SongCount { get; set; }
/// <summary>
/// Gets or sets the album count.
/// </summary>
@ -628,6 +634,7 @@ namespace MediaBrowser.Model.Dto
public int? AlbumCount { get; set; }
public int? ArtistCount { get; set; }
/// <summary>
/// Gets or sets the music video count.
/// </summary>
@ -768,6 +775,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The timer identifier.</value>
public string TimerId { get; set; }
/// <summary>
/// Gets or sets the current program.
/// </summary>

@ -451,11 +451,13 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value>The method.</value>
public SubtitleDeliveryMethod? DeliveryMethod { get; set; }
/// <summary>
/// Gets or sets the delivery URL.
/// </summary>
/// <value>The delivery URL.</value>
public string DeliveryUrl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is external URL.
/// </summary>

@ -11,18 +11,22 @@ namespace MediaBrowser.Model.Entities
/// The imdb.
/// </summary>
Imdb = 2,
/// <summary>
/// The TMDB.
/// </summary>
Tmdb = 3,
/// <summary>
/// The TVDB.
/// </summary>
Tvdb = 4,
/// <summary>
/// The tvcom.
/// </summary>
Tvcom = 5,
/// <summary>
/// Tmdb Collection Id.
/// </summary>

@ -84,6 +84,7 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value>
public bool? IsKids { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is sports.
/// </summary>

@ -68,5 +68,4 @@ namespace MediaBrowser.Model.Providers
/// <value>The type of the rating.</value>
public RatingType RatingType { get; set; }
}
}

@ -50,6 +50,5 @@ namespace MediaBrowser.Model.Providers
public RemoteSearchResult AlbumArtist { get; set; }
public RemoteSearchResult[] Artists { get; set; }
}
}

@ -168,6 +168,7 @@ namespace MediaBrowser.Model.Querying
Studios,
BasicSyncInfo,
/// <summary>
/// The synchronize information.
/// </summary>

@ -37,16 +37,19 @@ namespace MediaBrowser.Model.Querying
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable images].
/// </summary>
/// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
public bool? EnableImages { get; set; }
/// <summary>
/// Gets or sets the image type limit.
/// </summary>
/// <value>The image type limit.</value>
public int? ImageTypeLimit { get; set; }
/// <summary>
/// Gets or sets the enable image types.
/// </summary>

@ -88,16 +88,19 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The play method.</value>
public PlayMethod PlayMethod { get; set; }
/// <summary>
/// Gets or sets the live stream identifier.
/// </summary>
/// <value>The live stream identifier.</value>
public string LiveStreamId { get; set; }
/// <summary>
/// Gets or sets the play session identifier.
/// </summary>
/// <value>The play session identifier.</value>
public string PlaySessionId { get; set; }
/// <summary>
/// Gets or sets the repeat mode.
/// </summary>

@ -8,10 +8,12 @@ namespace MediaBrowser.Model.Sync
/// The latest.
/// </summary>
Latest = 0,
/// <summary>
/// The next up.
/// </summary>
NextUp = 1,
/// <summary>
/// The resume.
/// </summary>

@ -14,13 +14,16 @@ namespace MediaBrowser.Model.System
{
/// <summary>No path to FFmpeg found.</summary>
NotFound,
/// <summary>Path supplied via command line using switch --ffmpeg.</summary>
SetByArgument,
/// <summary>User has supplied path via Transcoding UI page.</summary>
Custom,
/// <summary>FFmpeg tool found on system $PATH.</summary>
System
};
}
/// <summary>
/// Class SystemInfo.

@ -9,6 +9,7 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
bool IsHidden { get; }
/// <summary>
/// Gets a value indicating whether this instance is enabled.
/// </summary>

@ -52,6 +52,16 @@ namespace MediaBrowser.Model.Updates
/// <value>The versions.</value>
public IReadOnlyList<VersionInfo> versions { get; set; }
/// <summary>
/// Gets or sets the repository name.
/// </summary>
public string repositoryName { get; set; }
/// <summary>
/// Gets or sets the repository url.
/// </summary>
public string repositoryUrl { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="PackageInfo"/> class.
/// </summary>

@ -21,7 +21,7 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.8" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.1.2" />
<PackageReference Include="TvDbSharper" Version="3.2.1" />
<PackageReference Include="TvDbSharper" Version="3.2.2" />
</ItemGroup>
<PropertyGroup>

@ -198,6 +198,7 @@ namespace MediaBrowser.Providers.Music
result.Name = reader.ReadElementContentAsString();
break;
}
case "annotation":
{
result.Overview = reader.ReadElementContentAsString();

@ -444,6 +444,7 @@ namespace MediaBrowser.Providers.Music
result.Title = reader.ReadElementContentAsString();
break;
}
case "date":
{
var val = reader.ReadElementContentAsString();
@ -454,17 +455,20 @@ namespace MediaBrowser.Providers.Music
break;
}
case "annotation":
{
result.Overview = reader.ReadElementContentAsString();
break;
}
case "release-group":
{
result.ReleaseGroupId = reader.GetAttribute("id");
reader.Skip();
break;
}
case "artist-credit":
{
using (var subReader = reader.ReadSubtree())

@ -57,21 +57,28 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
// Process images
try
{
var episodeInfo = new EpisodeInfo
string episodeTvdbId = null;
if (episode.IndexNumber.HasValue && episode.ParentIndexNumber.HasValue)
{
IndexNumber = episode.IndexNumber.Value,
ParentIndexNumber = episode.ParentIndexNumber.Value,
SeriesProviderIds = series.ProviderIds,
SeriesDisplayOrder = series.DisplayOrder
};
string episodeTvdbId = await _tvdbClientManager
.GetEpisodeTvdbId(episodeInfo, language, cancellationToken).ConfigureAwait(false);
var episodeInfo = new EpisodeInfo
{
IndexNumber = episode.IndexNumber.Value,
ParentIndexNumber = episode.ParentIndexNumber.Value,
SeriesProviderIds = series.ProviderIds,
SeriesDisplayOrder = series.DisplayOrder
};
episodeTvdbId = await _tvdbClientManager
.GetEpisodeTvdbId(episodeInfo, language, cancellationToken).ConfigureAwait(false);
}
if (string.IsNullOrEmpty(episodeTvdbId))
{
_logger.LogError(
"Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
episodeInfo.ParentIndexNumber,
episodeInfo.IndexNumber,
episode.ParentIndexNumber,
episode.IndexNumber,
series.GetProviderId(MetadataProvider.Tvdb));
return imageResult;
}

@ -302,7 +302,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source),
Name = i.Name
}).ToArray();
}
}

@ -217,7 +217,6 @@ namespace MediaBrowser.Providers.TV
new DeleteOptions
{
DeleteFileLocation = true
},
false);

@ -53,18 +53,19 @@ Jellyfin is a Free Software Media System that puts you in control of managing an
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](https://docs.jellyfin.org/general/getting-help.html). For more information about the project, please see our [about page](https://docs.jellyfin.org/general/about.html).
<strong>Want to get started?</strong><br/>
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/quick-start.html">quick start guide</a>.<br/>
Check out our <a href="https://jellyfin.org/downloads">downloads page</a> or our <a href="https://docs.jellyfin.org/general/administration/installing.html">installation guide</a>, then see our <a href="https://docs.jellyfin.org/general/quick-start.html">quick start guide</a>. You can also <a href="https://docs.jellyfin.org/general/administration/building.html">build from source</a>.<br/>
<strong>Something not working right?</strong><br/>
Open an <a href="https://docs.jellyfin.org/general/contributing/issues.html">Issue</a> on GitHub.<br/>
<strong>Want to contribute?</strong><br/>
Check out <a href="https://docs.jellyfin.org/general/contributing/index.html">our documentation for guidelines</a>.<br/>
Check out our <a href="https://jellyfin.org/contribute">contributing choose-your-own-adventure</a> to see where you can help, then see our <a href="https://docs.jellyfin.org/general/contributing/index.html">contributing guide</a> and our <a href="https://jellyfin.org/docs/general/community-standards">community standards</a>.<br/>
<strong>New idea or improvement?</strong><br/>
Check out our <a href="https://features.jellyfin.org/?view=most-wanted">feature request hub</a>.<br/>
Most of the translations can be found in the web client but we have several other clients that have missing strings. Translations can be improved very easily from our <a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-core">Weblate</a> instance. Look through the following graphic to see if your native language could use some work!
<strong>Don't see Jellyfin in your language?</strong><br/>
Check out our <a href="https://translate.jellyfin.org">Weblate instance</a> to help translate Jellyfin and its subprojects.<br/>
<a href="https://translate.jellyfin.org/engage/jellyfin/?utm_source=widget">
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-web/multi-auto.svg" alt="Detailed Translation Status"/>

@ -45,8 +45,7 @@ namespace Jellyfin.Api.Tests
// Specify the startup command line options
var commandLineOpts = new StartupOptions
{
NoWebClient = true,
NoAutoRunWebApp = true
NoWebClient = true
};
// Use a temporary directory for the application paths

Loading…
Cancel
Save