mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-07 03:48:06 +02:00
Merge branch 'api-migration' into api-syncplay
# Conflicts: # MediaBrowser.Api/SyncPlay/SyncPlayService.cs
This commit is contained in:
commit
be8cf1e9ef
698 changed files with 7852 additions and 5012 deletions
163
.ci/azure-pipelines-package.yml
Normal file
163
.ci/azure-pipelines-package.yml
Normal file
|
@ -0,0 +1,163 @@
|
|||
jobs:
|
||||
- job: BuildPackage
|
||||
displayName: 'Build Packages'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
CentOS.amd64:
|
||||
BuildConfiguration: centos.amd64
|
||||
Fedora.amd64:
|
||||
BuildConfiguration: fedora.amd64
|
||||
Debian.amd64:
|
||||
BuildConfiguration: debian.amd64
|
||||
Debian.arm64:
|
||||
BuildConfiguration: debian.arm64
|
||||
Debian.armhf:
|
||||
BuildConfiguration: debian.armhf
|
||||
Ubuntu.amd64:
|
||||
BuildConfiguration: ubuntu.amd64
|
||||
Ubuntu.arm64:
|
||||
BuildConfiguration: ubuntu.arm64
|
||||
Ubuntu.armhf:
|
||||
BuildConfiguration: ubuntu.armhf
|
||||
Linux.amd64:
|
||||
BuildConfiguration: linux.amd64
|
||||
Windows.amd64:
|
||||
BuildConfiguration: windows.amd64
|
||||
MacOS:
|
||||
BuildConfiguration: macos
|
||||
Portable:
|
||||
BuildConfiguration: portable
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
|
||||
displayName: 'Build Dockerfile'
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (unstable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (stable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish Release'
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
artifactName: 'jellyfin-server-$(BuildConfiguration)'
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Create target directory on repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
contents: '**'
|
||||
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- job: BuildDocker
|
||||
displayName: 'Build Docker'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
amd64:
|
||||
BuildConfiguration: amd64
|
||||
arm64:
|
||||
BuildConfiguration: arm64
|
||||
armhf:
|
||||
BuildConfiguration: armhf
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: Docker@2
|
||||
displayName: 'Push Unstable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-server'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
unstable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||
unstable-$(BuildConfiguration)
|
||||
|
||||
- task: Docker@2
|
||||
displayName: 'Push Stable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-server'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
stable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||
stable-$(BuildConfiguration)
|
||||
|
||||
- job: CollectArtifacts
|
||||
displayName: 'Collect Artifacts'
|
||||
dependsOn:
|
||||
- BuildPackage
|
||||
- BuildDocker
|
||||
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: SSH@0
|
||||
displayName: 'Update Unstable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: |
|
||||
sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable
|
||||
rm $0
|
||||
exit
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Update Stable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: |
|
||||
sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)
|
||||
rm $0
|
||||
exit
|
||||
|
||||
- job: PublishNuget
|
||||
displayName: 'Publish NuGet packages'
|
||||
dependsOn:
|
||||
- BuildPackage
|
||||
condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj
|
||||
packDestination: '$(Build.ArtifactStagingDirectory)'
|
||||
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
|
||||
includeNugetOrg: 'true'
|
|
@ -15,11 +15,13 @@ trigger:
|
|||
batch: true
|
||||
|
||||
jobs:
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-main.yml
|
||||
parameters:
|
||||
LinuxImage: 'ubuntu-latest'
|
||||
RestoreBuildProjects: $(RestoreBuildProjects)
|
||||
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-test.yml
|
||||
parameters:
|
||||
ImageNames:
|
||||
|
@ -27,6 +29,7 @@ jobs:
|
|||
Windows: 'windows-latest'
|
||||
macOS: 'macos-latest'
|
||||
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-abi.yml
|
||||
parameters:
|
||||
Packages:
|
||||
|
@ -43,3 +46,6 @@ jobs:
|
|||
NugetPackageName: Jellyfin.Common
|
||||
AssemblyFileName: MediaBrowser.Common.dll
|
||||
LinuxImage: 'ubuntu-latest'
|
||||
|
||||
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
|
||||
- template: azure-pipelines-package.yml
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -39,7 +39,6 @@ ProgramData*/
|
|||
CorePlugins*/
|
||||
ProgramData-Server*/
|
||||
ProgramData-UI*/
|
||||
MediaBrowser.WebDashboard/jellyfin-web/**
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
|
@ -276,4 +275,4 @@ BenchmarkDotNet.Artifacts
|
|||
# Ignore web artifacts from native builds
|
||||
web/
|
||||
web-src.*
|
||||
MediaBrowser.WebDashboard/jellyfin-web/
|
||||
MediaBrowser.WebDashboard/jellyfin-web
|
||||
|
|
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
|
@ -1,7 +1,4 @@
|
|||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
|
@ -24,5 +21,8 @@
|
|||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
,]
|
||||
],
|
||||
"env": {
|
||||
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||
}
|
||||
}
|
||||
|
|
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
|
@ -21,5 +21,10 @@
|
|||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
],
|
||||
"options": {
|
||||
"env": {
|
||||
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace DvdLib.Ifo
|
|||
public class Cell
|
||||
{
|
||||
public CellPlaybackInfo PlaybackInfo { get; private set; }
|
||||
|
||||
public CellPositionInfo PositionInfo { get; private set; }
|
||||
|
||||
internal void ParsePlayback(BinaryReader br)
|
||||
|
|
|
@ -5,7 +5,9 @@ namespace DvdLib.Ifo
|
|||
public class Chapter
|
||||
{
|
||||
public ushort ProgramChainNumber { get; private set; }
|
||||
|
||||
public ushort ProgramNumber { get; private set; }
|
||||
|
||||
public uint ChapterNumber { get; private set; }
|
||||
|
||||
public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
|
||||
|
|
|
@ -117,12 +117,19 @@ namespace DvdLib.Ifo
|
|||
uint chapNum = 1;
|
||||
vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
|
||||
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
|
||||
if (t == null) continue;
|
||||
if (t == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
|
||||
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
|
||||
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
chapNum++;
|
||||
}
|
||||
while (vtsFs.Position < (baseAddr + endaddr));
|
||||
|
@ -147,7 +154,10 @@ namespace DvdLib.Ifo
|
|||
uint vtsPgcOffset = vtsRead.ReadUInt32();
|
||||
|
||||
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
|
||||
if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
|
||||
if (t != null)
|
||||
{
|
||||
t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,14 @@ namespace DvdLib.Ifo
|
|||
Second = GetBCDValue(data[2]);
|
||||
Frames = GetBCDValue((byte)(data[3] & 0x3F));
|
||||
|
||||
if ((data[3] & 0x80) != 0) FrameRate = 30;
|
||||
else if ((data[3] & 0x40) != 0) FrameRate = 25;
|
||||
if ((data[3] & 0x80) != 0)
|
||||
{
|
||||
FrameRate = 30;
|
||||
}
|
||||
else if ((data[3] & 0x40) != 0)
|
||||
{
|
||||
FrameRate = 25;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte GetBCDValue(byte data)
|
||||
|
|
|
@ -22,7 +22,9 @@ namespace DvdLib.Ifo
|
|||
public readonly List<Cell> Cells;
|
||||
|
||||
public DvdTime PlaybackTime { get; private set; }
|
||||
|
||||
public UserOperation ProhibitedUserOperations { get; private set; }
|
||||
|
||||
public byte[] AudioStreamControl { get; private set; } // 8*2 entries
|
||||
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
|
||||
|
||||
|
@ -33,9 +35,11 @@ namespace DvdLib.Ifo
|
|||
private ushort _goupProgramNumber;
|
||||
|
||||
public ProgramPlaybackMode PlaybackMode { get; private set; }
|
||||
|
||||
public uint ProgramCount { get; private set; }
|
||||
|
||||
public byte StillTime { get; private set; }
|
||||
|
||||
public byte[] Palette { get; private set; } // 16*4 entries
|
||||
|
||||
private ushort _commandTableOffset;
|
||||
|
@ -71,8 +75,15 @@ namespace DvdLib.Ifo
|
|||
|
||||
StillTime = br.ReadByte();
|
||||
byte pbMode = br.ReadByte();
|
||||
if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
|
||||
else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
|
||||
if (pbMode == 0)
|
||||
{
|
||||
PlaybackMode = ProgramPlaybackMode.Sequential;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
|
||||
}
|
||||
|
||||
ProgramCount = (uint)(pbMode & 0x7F);
|
||||
|
||||
Palette = br.ReadBytes(64);
|
||||
|
|
|
@ -8,8 +8,11 @@ namespace DvdLib.Ifo
|
|||
public class Title
|
||||
{
|
||||
public uint TitleNumber { get; private set; }
|
||||
|
||||
public uint AngleCount { get; private set; }
|
||||
|
||||
public ushort ChapterCount { get; private set; }
|
||||
|
||||
public byte VideoTitleSetNumber { get; private set; }
|
||||
|
||||
private ushort _parentalManagementMask;
|
||||
|
@ -17,6 +20,7 @@ namespace DvdLib.Ifo
|
|||
private uint _vtsStartSector; // relative to start of entire disk
|
||||
|
||||
public ProgramChain EntryProgramChain { get; private set; }
|
||||
|
||||
public readonly List<ProgramChain> ProgramChains;
|
||||
|
||||
public readonly List<Chapter> Chapters;
|
||||
|
@ -55,7 +59,10 @@ namespace DvdLib.Ifo
|
|||
var pgc = new ProgramChain(pgcNum);
|
||||
pgc.ParseHeader(br);
|
||||
ProgramChains.Add(pgc);
|
||||
if (entryPgc) EntryProgramChain = pgc;
|
||||
if (entryPgc)
|
||||
{
|
||||
EntryProgramChain = pgc;
|
||||
}
|
||||
|
||||
br.BaseStream.Seek(curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
|
|
@ -1357,6 +1357,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||
internal class ServerItem
|
||||
{
|
||||
public BaseItem Item { get; set; }
|
||||
|
||||
public StubType? StubType { get; set; }
|
||||
|
||||
public ServerItem(BaseItem item)
|
||||
|
|
|
@ -705,7 +705,7 @@ namespace Emby.Dlna.Didl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds fields used by both items and folders
|
||||
/// Adds fields used by both items and folders.
|
||||
/// </summary>
|
||||
private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
|
||||
{
|
||||
|
@ -765,6 +765,7 @@ namespace Emby.Dlna.Didl
|
|||
{
|
||||
AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
|
||||
}
|
||||
|
||||
if (filter.Contains("upnp:rating"))
|
||||
{
|
||||
AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
|
||||
|
@ -1052,10 +1053,12 @@ namespace Emby.Dlna.Didl
|
|||
{
|
||||
return GetImageInfo(item, ImageType.Primary);
|
||||
}
|
||||
|
||||
if (item.HasImage(ImageType.Thumb))
|
||||
{
|
||||
return GetImageInfo(item, ImageType.Thumb);
|
||||
}
|
||||
|
||||
if (item.HasImage(ImageType.Backdrop))
|
||||
{
|
||||
if (item is Channel)
|
||||
|
@ -1139,7 +1142,6 @@ namespace Emby.Dlna.Didl
|
|||
width = null;
|
||||
height = null;
|
||||
}
|
||||
|
||||
else if (width == -1 || height == -1)
|
||||
{
|
||||
width = null;
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace Emby.Dlna.Didl
|
|||
public Filter()
|
||||
: this("*")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Filter(string filter)
|
||||
|
|
|
@ -88,7 +88,6 @@ namespace Emby.Dlna
|
|||
.Select(i => i.Item2)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DeviceProfile GetDefaultProfile()
|
||||
|
@ -141,56 +140,74 @@ namespace Emby.Dlna
|
|||
if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
|
||||
{
|
||||
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
|
||||
{
|
||||
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
|
||||
{
|
||||
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
|
||||
{
|
||||
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
|
||||
{
|
||||
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelName))
|
||||
{
|
||||
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
|
||||
{
|
||||
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
|
||||
{
|
||||
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
|
||||
{
|
||||
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -439,6 +456,7 @@ namespace Emby.Dlna
|
|||
{
|
||||
throw new ArgumentException("Profile is missing Id");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(profile.Name))
|
||||
{
|
||||
throw new ArgumentException("Profile is missing Name");
|
||||
|
@ -464,6 +482,7 @@ namespace Emby.Dlna
|
|||
{
|
||||
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
|
||||
}
|
||||
|
||||
SerializeToXml(profile, path);
|
||||
}
|
||||
|
||||
|
@ -474,7 +493,7 @@ namespace Emby.Dlna
|
|||
|
||||
/// <summary>
|
||||
/// Recreates the object using serialization, to ensure it's not a subclass.
|
||||
/// If it's a subclass it may not serlialize properly to xml (different root element tag name)
|
||||
/// If it's a subclass it may not serlialize properly to xml (different root element tag name).
|
||||
/// </summary>
|
||||
/// <param name="profile"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -493,6 +512,7 @@ namespace Emby.Dlna
|
|||
class InternalProfileInfo
|
||||
{
|
||||
internal DeviceProfileInfo Info { get; set; }
|
||||
|
||||
internal string Path { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ namespace Emby.Dlna.Eventing
|
|||
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||
{
|
||||
var subscription = GetSubscription(subscriptionId, false);
|
||||
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
|
||||
int timeoutSeconds = subscription.TimeoutSeconds;
|
||||
subscription.SubscriptionTime = DateTime.UtcNow;
|
||||
|
@ -45,6 +46,13 @@ namespace Emby.Dlna.Eventing
|
|||
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
|
||||
}
|
||||
|
||||
return new EventSubscriptionResponse
|
||||
{
|
||||
Content = string.Empty,
|
||||
ContentType = "text/plain"
|
||||
};
|
||||
}
|
||||
|
||||
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||
{
|
||||
var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
|
||||
|
@ -150,6 +158,7 @@ namespace Emby.Dlna.Eventing
|
|||
builder.Append("</" + key + ">");
|
||||
builder.Append("</e:property>");
|
||||
}
|
||||
|
||||
builder.Append("</e:propertyset>");
|
||||
|
||||
var options = new HttpRequestOptions
|
||||
|
@ -169,7 +178,6 @@ namespace Emby.Dlna.Eventing
|
|||
{
|
||||
using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
|
|
@ -7,10 +7,13 @@ namespace Emby.Dlna.Eventing
|
|||
public class EventSubscription
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string CallbackUrl { get; set; }
|
||||
|
||||
public string NotificationType { get; set; }
|
||||
|
||||
public DateTime SubscriptionTime { get; set; }
|
||||
|
||||
public int TimeoutSeconds { get; set; }
|
||||
|
||||
public long TriggerCount { get; set; }
|
||||
|
|
|
@ -35,8 +35,6 @@ namespace Emby.Dlna.Main
|
|||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger<DlnaEntryPoint> _logger;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
private PlayToManager _manager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
@ -47,14 +45,13 @@ namespace Emby.Dlna.Main
|
|||
private readonly ILocalizationManager _localization;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
|
||||
private SsdpDevicePublisher _Publisher;
|
||||
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
private readonly INetworkManager _networkManager;
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private PlayToManager _manager;
|
||||
private SsdpDevicePublisher _publisher;
|
||||
private ISsdpCommunicationsServer _communicationsServer;
|
||||
|
||||
internal IContentDirectory ContentDirectory { get; private set; }
|
||||
|
@ -181,7 +178,7 @@ namespace Emby.Dlna.Main
|
|||
var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
|
||||
OperatingSystem.Id == OperatingSystemId.Linux;
|
||||
|
||||
_communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
||||
_communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
||||
{
|
||||
IsShared = true
|
||||
};
|
||||
|
@ -232,20 +229,22 @@ namespace Emby.Dlna.Main
|
|||
return;
|
||||
}
|
||||
|
||||
if (_Publisher != null)
|
||||
if (_publisher != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost);
|
||||
_Publisher.LogFunction = LogMessage;
|
||||
_Publisher.SupportPnpRootDevice = false;
|
||||
_publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost)
|
||||
{
|
||||
LogFunction = LogMessage,
|
||||
SupportPnpRootDevice = false
|
||||
};
|
||||
|
||||
await RegisterServerEndpoints().ConfigureAwait(false);
|
||||
|
||||
_Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
|
||||
_publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -267,6 +266,12 @@ namespace Emby.Dlna.Main
|
|||
continue;
|
||||
}
|
||||
|
||||
// Limit to LAN addresses only
|
||||
if (!_networkManager.IsAddressInSubnets(address, true, true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||
|
||||
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
|
||||
|
@ -288,7 +293,7 @@ namespace Emby.Dlna.Main
|
|||
};
|
||||
|
||||
SetProperies(device, fullService);
|
||||
_Publisher.AddDevice(device);
|
||||
_publisher.AddDevice(device);
|
||||
|
||||
var embeddedDevices = new[]
|
||||
{
|
||||
|
@ -320,12 +325,13 @@ namespace Emby.Dlna.Main
|
|||
{
|
||||
guid = text.GetMD5();
|
||||
}
|
||||
|
||||
return guid.ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private void SetProperies(SsdpDevice device, string fullDeviceType)
|
||||
{
|
||||
var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty);
|
||||
var service = fullDeviceType.Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var serviceParts = service.Split(':');
|
||||
|
||||
|
@ -336,7 +342,6 @@ namespace Emby.Dlna.Main
|
|||
device.DeviceType = serviceParts[2];
|
||||
}
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private void StartPlayToManager()
|
||||
{
|
||||
lock (_syncLock)
|
||||
|
@ -388,6 +393,7 @@ namespace Emby.Dlna.Main
|
|||
{
|
||||
_logger.LogError(ex, "Error disposing PlayTo manager");
|
||||
}
|
||||
|
||||
_manager = null;
|
||||
}
|
||||
}
|
||||
|
@ -414,11 +420,11 @@ namespace Emby.Dlna.Main
|
|||
|
||||
public void DisposeDevicePublisher()
|
||||
{
|
||||
if (_Publisher != null)
|
||||
if (_publisher != null)
|
||||
{
|
||||
_logger.LogInformation("Disposing SsdpDevicePublisher");
|
||||
_Publisher.Dispose();
|
||||
_Publisher = null;
|
||||
_publisher.Dispose();
|
||||
_publisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ namespace Emby.Dlna.PlayTo
|
|||
{
|
||||
public class Device : IDisposable
|
||||
{
|
||||
#region Fields & Properties
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
public DeviceInfo Properties { get; set; }
|
||||
|
@ -37,6 +35,7 @@ namespace Emby.Dlna.PlayTo
|
|||
RefreshVolumeIfNeeded().GetAwaiter().GetResult();
|
||||
return _volume;
|
||||
}
|
||||
|
||||
set => _volume = value;
|
||||
}
|
||||
|
||||
|
@ -52,10 +51,10 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED;
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public Action OnDeviceUnavailable { get; set; }
|
||||
|
@ -141,8 +140,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
}
|
||||
|
||||
#region Commanding
|
||||
|
||||
public Task VolumeDown(CancellationToken cancellationToken)
|
||||
{
|
||||
var sendVolume = Math.Max(Volume - 5, 0);
|
||||
|
@ -211,7 +208,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
|
||||
if (command == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
|
@ -232,7 +231,7 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets volume on a scale of 0-100
|
||||
/// Sets volume on a scale of 0-100.
|
||||
/// </summary>
|
||||
public async Task SetVolume(int value, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -240,7 +239,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
|
@ -263,7 +264,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
|
@ -288,7 +291,9 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, string>
|
||||
{
|
||||
|
@ -401,11 +406,8 @@ namespace Emby.Dlna.PlayTo
|
|||
RestartTimer(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get data
|
||||
|
||||
private int _connectFailureCount;
|
||||
|
||||
private async void TimerCallback(object sender)
|
||||
{
|
||||
if (_disposed)
|
||||
|
@ -458,7 +460,9 @@ namespace Emby.Dlna.PlayTo
|
|||
_connectFailureCount = 0;
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
|
||||
if (transportState.Value == TRANSPORTSTATE.STOPPED)
|
||||
|
@ -478,7 +482,9 @@ namespace Emby.Dlna.PlayTo
|
|||
catch (Exception ex)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogError(ex, "Error updating device info for {DeviceName}", Properties.Name);
|
||||
|
||||
|
@ -494,6 +500,7 @@ namespace Emby.Dlna.PlayTo
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RestartTimerInactive();
|
||||
}
|
||||
}
|
||||
|
@ -578,7 +585,9 @@ namespace Emby.Dlna.PlayTo
|
|||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
|
||||
.Select(i => i.Element("CurrentMute"))
|
||||
|
@ -794,7 +803,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// first try to add a root node with a dlna namesapce
|
||||
|
@ -806,7 +814,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// some devices send back invalid xml
|
||||
|
@ -816,7 +823,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -871,10 +877,6 @@ namespace Emby.Dlna.PlayTo
|
|||
return new string[4];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region From XML
|
||||
|
||||
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AvCommands != null)
|
||||
|
@ -1069,8 +1071,6 @@ namespace Emby.Dlna.PlayTo
|
|||
return new Device(deviceProperties, httpClient, logger, config);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
private static DeviceIcon CreateIcon(XElement element)
|
||||
{
|
||||
|
@ -1194,8 +1194,6 @@ namespace Emby.Dlna.PlayTo
|
|||
});
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
|
@ -1222,8 +1220,6 @@ namespace Emby.Dlna.PlayTo
|
|||
_disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
|
||||
|
|
|
@ -425,6 +425,7 @@ namespace Emby.Dlna.PlayTo
|
|||
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -713,6 +714,7 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
throw new ArgumentException("Volume argument cannot be null");
|
||||
}
|
||||
|
||||
default:
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
@ -798,12 +800,15 @@ namespace Emby.Dlna.PlayTo
|
|||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
public string DeviceProfileId { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
public BaseItem Item { get; set; }
|
||||
|
||||
private MediaSourceInfo MediaSource;
|
||||
|
||||
private IMediaSourceManager _mediaSourceManager;
|
||||
|
|
|
@ -78,9 +78,15 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
var info = e.Argument;
|
||||
|
||||
if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
|
||||
if (!info.Headers.TryGetValue("USN", out string usn))
|
||||
{
|
||||
usn = string.Empty;
|
||||
}
|
||||
|
||||
if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
|
||||
if (!info.Headers.TryGetValue("NT", out string nt))
|
||||
{
|
||||
nt = string.Empty;
|
||||
}
|
||||
|
||||
string location = info.Location.ToString();
|
||||
|
||||
|
@ -112,7 +118,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -133,6 +138,7 @@ namespace Emby.Dlna.PlayTo
|
|||
usn = usn.Substring(index);
|
||||
found = true;
|
||||
}
|
||||
|
||||
index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
|
@ -243,7 +249,6 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
_sessionLock.Dispose();
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Emby.Dlna.PlayTo
|
|||
public class MediaChangedEventArgs : EventArgs
|
||||
{
|
||||
public uBaseObject OldMediaInfo { get; set; }
|
||||
|
||||
public uBaseObject NewMediaInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,6 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,10 +44,12 @@ namespace Emby.Dlna.PlayTo
|
|||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Audio;
|
||||
}
|
||||
|
||||
if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
|
||||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Video;
|
||||
}
|
||||
|
||||
if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
|
||||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Photo;
|
||||
|
|
|
@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
|
|||
|
||||
public void AddXmlRootAttribute(string name, string value)
|
||||
{
|
||||
var atts = XmlRootAttributes ?? new XmlAttribute[] { };
|
||||
var atts = XmlRootAttributes ?? System.Array.Empty<XmlAttribute>();
|
||||
var list = atts.ToList();
|
||||
|
||||
list.Add(new XmlAttribute
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Emby.Dlna.Profiles
|
|||
},
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Emby.Dlna.Profiles
|
|||
},
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
|
@ -37,7 +38,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
|
@ -223,7 +224,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
|
@ -223,7 +224,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
|
@ -211,7 +212,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
|
@ -211,7 +212,7 @@ namespace Emby.Dlna.Profiles
|
|||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,6 +134,7 @@ namespace Emby.Dlna.Server
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return c.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
|
@ -157,18 +158,22 @@ namespace Emby.Dlna.Server
|
|||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (stringBuilder == null)
|
||||
{
|
||||
stringBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
stringBuilder.Append(str, num, num2 - num);
|
||||
stringBuilder.Append(GetEscapeSequence(str[num2]));
|
||||
num = num2 + 1;
|
||||
}
|
||||
|
||||
if (stringBuilder == null)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
stringBuilder.Append(str, num, length - num);
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Emby.Dlna.Service
|
|||
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
||||
|
||||
protected IServerConfigurationManager Config { get; }
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
|
||||
|
@ -135,6 +136,7 @@ namespace Emby.Dlna.Service
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
await reader.SkipAsync().ConfigureAwait(false);
|
||||
|
@ -211,7 +213,9 @@ namespace Emby.Dlna.Service
|
|||
private class ControlRequestInfo
|
||||
{
|
||||
public string LocalName { get; set; }
|
||||
|
||||
public string NamespaceURI { get; set; }
|
||||
|
||||
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace Emby.Dlna.Service
|
|||
{
|
||||
builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>");
|
||||
}
|
||||
|
||||
builder.Append("</allowedValueList>");
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace Emby.Naming.AudioBook
|
|||
{
|
||||
result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
|
||||
}
|
||||
|
||||
if (matches.Count > 1)
|
||||
{
|
||||
result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);
|
||||
|
|
|
@ -5,17 +5,17 @@ namespace Emby.Naming.Common
|
|||
public enum MediaType
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio
|
||||
/// The audio.
|
||||
/// </summary>
|
||||
Audio = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The photo
|
||||
/// The photo.
|
||||
/// </summary>
|
||||
Photo = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The video
|
||||
/// The video.
|
||||
/// </summary>
|
||||
Video = 2
|
||||
}
|
||||
|
|
|
@ -43,9 +43,9 @@ using Emby.Server.Implementations.Security;
|
|||
using Emby.Server.Implementations.Serialization;
|
||||
using Emby.Server.Implementations.Services;
|
||||
using Emby.Server.Implementations.Session;
|
||||
using Emby.Server.Implementations.SyncPlay;
|
||||
using Emby.Server.Implementations.TV;
|
||||
using Emby.Server.Implementations.Updates;
|
||||
using Emby.Server.Implementations.SyncPlay;
|
||||
using MediaBrowser.Api;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
@ -78,8 +78,8 @@ using MediaBrowser.Controller.Security;
|
|||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Controller.TV;
|
||||
using MediaBrowser.Controller.SyncPlay;
|
||||
using MediaBrowser.Controller.TV;
|
||||
using MediaBrowser.LocalMetadata.Savers;
|
||||
using MediaBrowser.MediaEncoding.BdInfo;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
@ -483,12 +483,10 @@ namespace Emby.Server.Implementations
|
|||
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
pluginBuilder.AppendLine(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} {1}",
|
||||
plugin.Name,
|
||||
plugin.Version));
|
||||
pluginBuilder.Append(plugin.Name)
|
||||
.Append(' ')
|
||||
.Append(plugin.Version)
|
||||
.AppendLine();
|
||||
}
|
||||
|
||||
Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString());
|
||||
|
@ -565,10 +563,8 @@ namespace Emby.Server.Implementations
|
|||
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
|
||||
|
||||
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
||||
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
|
||||
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
|
||||
serviceCollection.AddSingleton<IMediaEncoder>(provider =>
|
||||
ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
|
||||
serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
|
||||
|
||||
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
|
||||
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
|
||||
|
@ -872,6 +868,11 @@ namespace Emby.Server.Implementations
|
|||
Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
|
||||
continue;
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Type type in exportedTypes)
|
||||
{
|
||||
|
@ -955,7 +956,7 @@ namespace Emby.Server.Implementations
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that the kernel that a change has been made that requires a restart
|
||||
/// Notifies that the kernel that a change has been made that requires a restart.
|
||||
/// </summary>
|
||||
public void NotifyPendingRestart()
|
||||
{
|
||||
|
@ -1151,7 +1152,7 @@ namespace Emby.Server.Implementations
|
|||
return null;
|
||||
}
|
||||
|
||||
return GetLocalApiUrl(addresses.First());
|
||||
return GetLocalApiUrl(addresses[0]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1224,13 +1225,13 @@ namespace Emby.Server.Implementations
|
|||
var addresses = ServerConfigurationManager
|
||||
.Configuration
|
||||
.LocalNetworkAddresses
|
||||
.Select(NormalizeConfiguredLocalAddress)
|
||||
.Select(x => NormalizeConfiguredLocalAddress(x))
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
|
||||
if (addresses.Count == 0)
|
||||
{
|
||||
addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
|
||||
addresses.AddRange(_networkManager.GetLocalIpAddresses());
|
||||
}
|
||||
|
||||
var resultList = new List<IPAddress>();
|
||||
|
@ -1245,8 +1246,7 @@ namespace Emby.Server.Implementations
|
|||
}
|
||||
}
|
||||
|
||||
var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
|
||||
if (valid)
|
||||
if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
resultList.Add(address);
|
||||
|
||||
|
@ -1260,13 +1260,12 @@ namespace Emby.Server.Implementations
|
|||
return resultList;
|
||||
}
|
||||
|
||||
public IPAddress NormalizeConfiguredLocalAddress(string address)
|
||||
public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan<char> address)
|
||||
{
|
||||
var index = address.Trim('/').IndexOf('/');
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
address = address.Substring(index + 1);
|
||||
address = address.Slice(index + 1);
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))
|
||||
|
|
|
@ -363,60 +363,4 @@ namespace Emby.Server.Implementations.Collections
|
|||
return results.Values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection manager entry point.
|
||||
/// </summary>
|
||||
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly CollectionManager _collectionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger<CollectionManagerEntryPoint> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
|
||||
/// </summary>
|
||||
/// <param name="collectionManager">The collection manager.</param>
|
||||
/// <param name="config">The server configuration manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public CollectionManagerEntryPoint(
|
||||
ICollectionManager collectionManager,
|
||||
IServerConfigurationManager config,
|
||||
ILogger<CollectionManagerEntryPoint> logger)
|
||||
{
|
||||
_collectionManager = (CollectionManager)collectionManager;
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RunAsync()
|
||||
{
|
||||
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||
{
|
||||
var path = _collectionManager.GetCollectionsFolderPath();
|
||||
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating camera uploads library");
|
||||
}
|
||||
|
||||
_config.Configuration.CollectionsUpgraded = true;
|
||||
_config.SaveConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// Nothing to dispose
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ namespace Emby.Server.Implementations.Configuration
|
|||
if (!string.IsNullOrWhiteSpace(newPath)
|
||||
&& !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
|
||||
{
|
||||
// Validate
|
||||
if (!File.Exists(newPath))
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
|
@ -133,7 +132,6 @@ namespace Emby.Server.Implementations.Configuration
|
|||
if (!string.IsNullOrWhiteSpace(newPath)
|
||||
&& !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
|
||||
{
|
||||
// Validate
|
||||
if (!Directory.Exists(newPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException(
|
||||
|
@ -146,60 +144,5 @@ namespace Emby.Server.Implementations.Configuration
|
|||
EnsureWriteAccess(newPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all configuration values to their optimal values.
|
||||
/// </summary>
|
||||
/// <returns>If the configuration changed.</returns>
|
||||
public bool SetOptimalValues()
|
||||
{
|
||||
var config = Configuration;
|
||||
|
||||
var changed = false;
|
||||
|
||||
if (!config.EnableCaseSensitiveItemIds)
|
||||
{
|
||||
config.EnableCaseSensitiveItemIds = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.SkipDeserializationForBasicTypes)
|
||||
{
|
||||
config.SkipDeserializationForBasicTypes = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.EnableSimpleArtistDetection)
|
||||
{
|
||||
config.EnableSimpleArtistDetection = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.EnableNormalizedItemByNameIds)
|
||||
{
|
||||
config.EnableNormalizedItemByNameIds = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.DisableLiveTvChannelUserDataName)
|
||||
{
|
||||
config.DisableLiveTvChannelUserDataName = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.EnableNewOmdbSupport)
|
||||
{
|
||||
config.EnableNewOmdbSupport = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!config.CollectionsUpgraded)
|
||||
{
|
||||
config.CollectionsUpgraded = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace Emby.Server.Implementations
|
|||
{
|
||||
{ HostWebClientKey, bool.TrueString },
|
||||
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" },
|
||||
{ InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
|
||||
{ FfmpegProbeSizeKey, "1G" },
|
||||
{ FfmpegAnalyzeDurationKey, "200M" },
|
||||
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
|
||||
|
|
|
@ -162,7 +162,6 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
return false;
|
||||
|
||||
}, ReadTransactionMode);
|
||||
}
|
||||
|
||||
|
@ -248,12 +247,12 @@ namespace Emby.Server.Implementations.Data
|
|||
public enum SynchronousMode
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite continues without syncing as soon as it has handed data off to the operating system
|
||||
/// SQLite continues without syncing as soon as it has handed data off to the operating system.
|
||||
/// </summary>
|
||||
Off = 0,
|
||||
|
||||
/// <summary>
|
||||
/// SQLite database engine will still sync at the most critical moments
|
||||
/// SQLite database engine will still sync at the most critical moments.
|
||||
/// </summary>
|
||||
Normal = 1,
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Data
|
|||
_libraryManager.DeleteItem(item, new DeleteOptions
|
||||
{
|
||||
DeleteFileLocation = false
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the connection to the database
|
||||
/// Opens the connection to the database.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
private void InitializeInternal()
|
||||
|
@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the display preferences associated with an item in the repo
|
||||
/// Save the display preferences associated with an item in the repo.
|
||||
/// </summary>
|
||||
/// <param name="displayPreferences">The display preferences.</param>
|
||||
/// <param name="userId">The user id.</param>
|
||||
|
@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save all display preferences associated with a user in the repo
|
||||
/// Save all display preferences associated with a user in the repo.
|
||||
/// </summary>
|
||||
/// <param name="displayPreferences">The display preferences.</param>
|
||||
/// <param name="userId">The user id.</param>
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Data
|
|||
protected override TempStoreMode TempStore => TempStoreMode.Memory;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the connection to the database
|
||||
/// Opens the connection to the database.
|
||||
/// </summary>
|
||||
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
|
||||
{
|
||||
|
@ -321,7 +321,6 @@ namespace Emby.Server.Implementations.Data
|
|||
AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames);
|
||||
AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames);
|
||||
AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames);
|
||||
|
||||
}, TransactionMode);
|
||||
|
||||
connection.RunQueries(postQueries);
|
||||
|
@ -549,7 +548,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save a standard item in the repo
|
||||
/// Save a standard item in the repo.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
|
@ -794,6 +793,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
saveItemStatement.TryBindNull("@Width");
|
||||
}
|
||||
|
||||
if (item.Height > 0)
|
||||
{
|
||||
saveItemStatement.TryBind("@Height", item.Height);
|
||||
|
@ -933,6 +933,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
saveItemStatement.TryBindNull("@SeriesName");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userDataKey))
|
||||
{
|
||||
saveItemStatement.TryBindNull("@UserDataKey");
|
||||
|
@ -1008,6 +1009,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
artists = string.Join("|", hasArtists.Artists);
|
||||
}
|
||||
|
||||
saveItemStatement.TryBind("@Artists", artists);
|
||||
|
||||
string albumArtists = null;
|
||||
|
@ -1107,6 +1109,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
str.Append(ToValueString(i) + "|");
|
||||
}
|
||||
|
||||
|
@ -1205,7 +1208,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal retrieve from items or users table
|
||||
/// Internal retrieve from items or users table.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
/// <returns>BaseItem.</returns>
|
||||
|
@ -1367,6 +1370,7 @@ namespace Emby.Server.Implementations.Data
|
|||
hasStartDate.StartDate = reader[index].ReadDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1374,12 +1378,14 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.EndDate = reader[index].TryReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.ChannelId = new Guid(reader.GetString(index));
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (enableProgramAttributes)
|
||||
|
@ -1390,24 +1396,28 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
hasProgramAttributes.IsMovie = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
hasProgramAttributes.IsSeries = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
hasProgramAttributes.EpisodeTitle = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
else
|
||||
|
@ -1420,6 +1430,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.CommunityRating = reader.GetFloat(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.CustomRating))
|
||||
|
@ -1428,6 +1439,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.CustomRating = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1435,6 +1447,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.IndexNumber = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.Settings))
|
||||
|
@ -1443,18 +1456,21 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.IsLocked = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.PreferredMetadataLanguage = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.PreferredMetadataCountryCode = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1464,6 +1480,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Width = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1473,6 +1490,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Height = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1482,6 +1500,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.DateLastRefreshed = reader[index].ReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1489,18 +1508,21 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Name = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.Path = RestorePath(reader.GetString(index));
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.PremiereDate = reader[index].TryReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.Overview))
|
||||
|
@ -1509,6 +1531,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Overview = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1516,18 +1539,21 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ParentIndexNumber = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.ProductionYear = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.OfficialRating = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.SortName))
|
||||
|
@ -1536,6 +1562,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ForcedSortName = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1543,12 +1570,14 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.RunTimeTicks = reader.GetInt64(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.Size = reader.GetInt64(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.DateCreated))
|
||||
|
@ -1557,6 +1586,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.DateCreated = reader[index].ReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1564,6 +1594,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.DateModified = reader[index].ReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
item.Id = reader.GetGuid(index);
|
||||
|
@ -1575,6 +1606,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Genres = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1582,6 +1614,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ParentId = reader.GetGuid(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
|
@ -1591,6 +1624,7 @@ namespace Emby.Server.Implementations.Data
|
|||
item.Audio = audio;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
// TODO: Even if not needed by apps, the server needs it internally
|
||||
|
@ -1604,6 +1638,7 @@ namespace Emby.Server.Implementations.Data
|
|||
liveTvChannel.ServiceName = reader.GetString(index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1611,6 +1646,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.IsInMixedFolder = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.DateLastSaved))
|
||||
|
@ -1619,6 +1655,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.DateLastSaved = reader[index].ReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1636,8 +1673,10 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.LockedFields = GetLockedFields(reader.GetString(index)).ToArray();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1647,6 +1686,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Studios = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1656,6 +1696,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Tags = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1675,9 +1716,11 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
trailer.TrailerTypes = GetTrailerTypes(reader.GetString(index)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1687,6 +1730,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.OriginalTitle = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1697,6 +1741,7 @@ namespace Emby.Server.Implementations.Data
|
|||
video.PrimaryVersionId = reader.GetString(index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.DateLastMediaAdded))
|
||||
|
@ -1705,6 +1750,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
folder.DateLastMediaAdded = reader[index].TryReadDateTime();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1712,18 +1758,21 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Album = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.CriticRating = reader.GetFloat(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.IsVirtualItem = reader.GetBoolean(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (item is IHasSeries hasSeriesName)
|
||||
|
@ -1733,6 +1782,7 @@ namespace Emby.Server.Implementations.Data
|
|||
hasSeriesName.SeriesName = reader.GetString(index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (hasEpisodeAttributes)
|
||||
|
@ -1743,6 +1793,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
episode.SeasonName = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
|
@ -1753,6 +1804,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1766,6 +1818,7 @@ namespace Emby.Server.Implementations.Data
|
|||
hasSeries.SeriesId = reader.GetGuid(index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1775,6 +1828,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.PresentationUniqueKey = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1784,6 +1838,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.InheritedParentalRatingValue = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1793,6 +1848,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ExternalSeriesId = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1802,6 +1858,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.Tagline = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1809,6 +1866,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
DeserializeProviderIds(reader.GetString(index), item);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (query.DtoOptions.EnableImages)
|
||||
|
@ -1817,6 +1875,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
DeserializeImages(reader.GetString(index), item);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1826,6 +1885,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1835,6 +1895,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ExtraIds = SplitToGuids(reader.GetString(index));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1842,6 +1903,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.TotalBitrate = reader.GetInt32(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
|
@ -1851,6 +1913,7 @@ namespace Emby.Server.Implementations.Data
|
|||
item.ExtraType = extraType;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (hasArtistFields)
|
||||
|
@ -1859,12 +1922,14 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
hasArtists.Artists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (item is IHasAlbumArtist hasAlbumArtists && !reader.IsDBNull(index))
|
||||
{
|
||||
hasAlbumArtists.AlbumArtists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1872,6 +1937,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.ExternalId = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (HasField(query, ItemFields.SeriesPresentationUniqueKey))
|
||||
|
@ -1883,6 +1949,7 @@ namespace Emby.Server.Implementations.Data
|
|||
hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1892,6 +1959,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
program.ShowId = reader.GetString(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -1899,6 +1967,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
item.OwnerId = reader.GetGuid(index);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
return item;
|
||||
|
@ -1919,7 +1988,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets chapters for an item
|
||||
/// Gets chapters for an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>IEnumerable{ChapterInfo}.</returns>
|
||||
|
@ -1947,7 +2016,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a single chapter for an item
|
||||
/// Gets a single chapter for an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
|
@ -2044,7 +2113,6 @@ namespace Emby.Server.Implementations.Data
|
|||
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
|
||||
|
||||
InsertChapters(idBlob, chapters, db);
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
@ -2475,6 +2543,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
|
||||
}
|
||||
|
||||
if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
statement.TryBind("@SearchTermContains", "%" + searchTerm + "%");
|
||||
|
@ -2706,22 +2775,85 @@ namespace Emby.Server.Implementations.Data
|
|||
|
||||
private string FixUnicodeChars(string buffer)
|
||||
{
|
||||
if (buffer.IndexOf('\u2013') > -1) buffer = buffer.Replace('\u2013', '-'); // en dash
|
||||
if (buffer.IndexOf('\u2014') > -1) buffer = buffer.Replace('\u2014', '-'); // em dash
|
||||
if (buffer.IndexOf('\u2015') > -1) buffer = buffer.Replace('\u2015', '-'); // horizontal bar
|
||||
if (buffer.IndexOf('\u2017') > -1) buffer = buffer.Replace('\u2017', '_'); // double low line
|
||||
if (buffer.IndexOf('\u2018') > -1) buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
|
||||
if (buffer.IndexOf('\u2019') > -1) buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
|
||||
if (buffer.IndexOf('\u201a') > -1) buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
|
||||
if (buffer.IndexOf('\u201b') > -1) buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
|
||||
if (buffer.IndexOf('\u201c') > -1) buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
|
||||
if (buffer.IndexOf('\u201d') > -1) buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
|
||||
if (buffer.IndexOf('\u201e') > -1) buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
|
||||
if (buffer.IndexOf('\u2026') > -1) buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
|
||||
if (buffer.IndexOf('\u2032') > -1) buffer = buffer.Replace('\u2032', '\''); // prime
|
||||
if (buffer.IndexOf('\u2033') > -1) buffer = buffer.Replace('\u2033', '\"'); // double prime
|
||||
if (buffer.IndexOf('\u0060') > -1) buffer = buffer.Replace('\u0060', '\''); // grave accent
|
||||
if (buffer.IndexOf('\u00B4') > -1) buffer = buffer.Replace('\u00B4', '\''); // acute accent
|
||||
if (buffer.IndexOf('\u2013') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2013', '-'); // en dash
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2014') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2014', '-'); // em dash
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2015') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2015', '-'); // horizontal bar
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2017') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2017', '_'); // double low line
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2018') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2019') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u201a') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u201b') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u201c') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u201d') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u201e') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2026') > -1)
|
||||
{
|
||||
buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2032') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2032', '\''); // prime
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u2033') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u2033', '\"'); // double prime
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u0060') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u0060', '\''); // grave accent
|
||||
}
|
||||
|
||||
if (buffer.IndexOf('\u00B4') > -1)
|
||||
{
|
||||
buffer = buffer.Replace('\u00B4', '\''); // acute accent
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
@ -2745,6 +2877,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
items[i] = newItem;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2837,6 +2970,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statementTexts.Add(commandText);
|
||||
}
|
||||
|
||||
if (query.EnableTotalRecordCount)
|
||||
{
|
||||
commandText = string.Empty;
|
||||
|
@ -3241,6 +3375,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statementTexts.Add(commandText);
|
||||
}
|
||||
|
||||
if (query.EnableTotalRecordCount)
|
||||
{
|
||||
commandText = string.Empty;
|
||||
|
@ -3594,11 +3729,13 @@ namespace Emby.Server.Implementations.Data
|
|||
whereClauses.Add("IndexNumber=@IndexNumber");
|
||||
statement?.TryBind("@IndexNumber", query.IndexNumber.Value);
|
||||
}
|
||||
|
||||
if (query.ParentIndexNumber.HasValue)
|
||||
{
|
||||
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
|
||||
statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
|
||||
}
|
||||
|
||||
if (query.ParentIndexNumberNotEquals.HasValue)
|
||||
{
|
||||
whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
|
||||
|
@ -3884,6 +4021,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, artistId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -3904,6 +4042,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, artistId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -3924,8 +4063,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, artistId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -3943,8 +4084,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, albumId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -3962,8 +4105,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, artistId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -3981,8 +4126,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, genreId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -3998,8 +4145,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@Genre" + index, GetCleanValue(item));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -4015,8 +4164,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@Tag" + index, GetCleanValue(item));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -4032,8 +4183,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@ExcludeTag" + index, GetCleanValue(item));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -4052,8 +4205,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, studioId.ToByteArray());
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -4069,8 +4224,10 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@OfficialRating" + index, item);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
var clause = "(" + string.Join(" OR ", clauses) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
@ -4245,6 +4402,7 @@ namespace Emby.Server.Implementations.Data
|
|||
statement.TryBind("@IsVirtualItem", isVirtualItem.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IsSpecialSeason.HasValue)
|
||||
{
|
||||
if (query.IsSpecialSeason.Value)
|
||||
|
@ -4256,6 +4414,7 @@ namespace Emby.Server.Implementations.Data
|
|||
whereClauses.Add("IndexNumber <> 0");
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IsUnaired.HasValue)
|
||||
{
|
||||
if (query.IsUnaired.Value)
|
||||
|
@ -4267,6 +4426,7 @@ namespace Emby.Server.Implementations.Data
|
|||
whereClauses.Add("PremiereDate < DATETIME('now')");
|
||||
}
|
||||
}
|
||||
|
||||
var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray();
|
||||
if (queryMediaTypes.Length == 1)
|
||||
{
|
||||
|
@ -4282,6 +4442,7 @@ namespace Emby.Server.Implementations.Data
|
|||
|
||||
whereClauses.Add("MediaType in (" + val + ")");
|
||||
}
|
||||
|
||||
if (query.ItemIds.Length > 0)
|
||||
{
|
||||
var includeIds = new List<string>();
|
||||
|
@ -4294,11 +4455,13 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@IncludeId" + index, id);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
whereClauses.Add("(" + string.Join(" OR ", includeIds) + ")");
|
||||
}
|
||||
|
||||
if (query.ExcludeItemIds.Length > 0)
|
||||
{
|
||||
var excludeIds = new List<string>();
|
||||
|
@ -4311,6 +4474,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind("@ExcludeId" + index, id);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -4335,6 +4499,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
break;
|
||||
|
@ -4377,6 +4542,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
break;
|
||||
|
@ -4427,6 +4593,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
whereClauses.Add("(TopParentId=@TopParentId)");
|
||||
}
|
||||
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
|
||||
|
@ -4464,11 +4631,13 @@ namespace Emby.Server.Implementations.Data
|
|||
statement.TryBind("@AncestorId", query.AncestorIds[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (query.AncestorIds.Length > 1)
|
||||
{
|
||||
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
||||
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
|
||||
{
|
||||
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
|
||||
|
@ -4497,6 +4666,7 @@ namespace Emby.Server.Implementations.Data
|
|||
statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (query.BlockUnratedItems.Length > 1)
|
||||
{
|
||||
var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
|
||||
|
@ -4789,7 +4959,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
connection.RunInTransaction(db =>
|
||||
{
|
||||
connection.ExecuteAll(sql);
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
@ -4972,6 +5141,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
statement.TryBind("@ItemId", query.ItemId.ToByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (!query.AppearsInItemId.Equals(Guid.Empty))
|
||||
{
|
||||
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
|
||||
|
@ -4980,6 +5150,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
|
||||
|
||||
if (queryPersonTypes.Count == 1)
|
||||
|
@ -4996,6 +5167,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
|
||||
whereClauses.Add("PersonType in (" + val + ")");
|
||||
}
|
||||
|
||||
var queryExcludePersonTypes = query.ExcludePersonTypes.Where(IsValidPersonType).ToList();
|
||||
|
||||
if (queryExcludePersonTypes.Count == 1)
|
||||
|
@ -5012,6 +5184,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
|
||||
whereClauses.Add("PersonType not in (" + val + ")");
|
||||
}
|
||||
|
||||
if (query.MaxListOrder.HasValue)
|
||||
{
|
||||
whereClauses.Add("ListOrder<=@MaxListOrder");
|
||||
|
@ -5020,6 +5193,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
statement.TryBind("@MaxListOrder", query.MaxListOrder.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query.NameContains))
|
||||
{
|
||||
whereClauses.Add("Name like @NameContains");
|
||||
|
@ -5159,6 +5333,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'"));
|
||||
commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
|
||||
}
|
||||
|
||||
if (excludeItemTypes.Count > 0)
|
||||
{
|
||||
var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'"));
|
||||
|
@ -5180,7 +5355,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogQueryTime("GetItemValueNames", commandText, now);
|
||||
|
@ -5631,7 +5805,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
|
||||
|
||||
InsertPeople(itemIdBlob, people, db);
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
@ -5788,7 +5961,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
|
||||
|
||||
InsertMediaStreams(itemIdBlob, streams, db);
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
@ -6134,7 +6306,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
|
||||
|
||||
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
@ -6200,7 +6371,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
/// Gets the attachment.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader.</param>
|
||||
/// <returns>MediaAttachment</returns>
|
||||
/// <returns>MediaAttachment.</returns>
|
||||
private MediaAttachment GetMediaAttachment(IReadOnlyList<IResultSetValue> reader)
|
||||
{
|
||||
var item = new MediaAttachment
|
||||
|
|
|
@ -135,10 +135,12 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
throw new ArgumentNullException(nameof(userData));
|
||||
}
|
||||
|
||||
if (internalUserId <= 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(internalUserId));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
|
@ -153,6 +155,7 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
throw new ArgumentNullException(nameof(userData));
|
||||
}
|
||||
|
||||
if (internalUserId <= 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(internalUserId));
|
||||
|
@ -235,7 +238,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persist all user data for the specified user
|
||||
/// Persist all user data for the specified user.
|
||||
/// </summary>
|
||||
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -309,7 +312,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return all user-data associated with the given user
|
||||
/// Return all user-data associated with the given user.
|
||||
/// </summary>
|
||||
/// <param name="internalUserId"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -339,7 +342,7 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a row from the specified reader into the provided userData object
|
||||
/// Read a row from the specified reader into the provided userData object.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
|
||||
|
|
|
@ -169,6 +169,7 @@ namespace Emby.Server.Implementations.Devices
|
|||
{
|
||||
throw new ArgumentException("user not found");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(deviceId))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(deviceId));
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a BaseItem to a DTOBaseItem
|
||||
/// Converts a BaseItem to a DTOBaseItem.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="fields">The fields.</param>
|
||||
|
@ -277,6 +277,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.EpisodeTitle = dto.Name;
|
||||
dto.Name = dto.SeriesName;
|
||||
}
|
||||
|
||||
liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
|
||||
}
|
||||
|
||||
|
@ -292,6 +293,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var containers = container.Split(new[] { ',' });
|
||||
if (containers.Length < 2)
|
||||
{
|
||||
|
@ -406,7 +408,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.DateLastMediaAdded = folder.DateLastMediaAdded;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (options.EnableUserData)
|
||||
|
@ -443,7 +444,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets client-side Id of a server-side BaseItem
|
||||
/// Gets client-side Id of a server-side BaseItem.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
|
@ -457,6 +458,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
{
|
||||
dto.SeriesName = item.SeriesName;
|
||||
}
|
||||
|
||||
private static void SetPhotoProperties(BaseItemDto dto, Photo item)
|
||||
{
|
||||
dto.CameraMake = item.CameraMake;
|
||||
|
@ -538,7 +540,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches People DTO's to a DTOBaseItem
|
||||
/// Attaches People DTO's to a DTOBaseItem.
|
||||
/// </summary>
|
||||
/// <param name="dto">The dto.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
|
@ -555,22 +557,27 @@ namespace Emby.Server.Implementations.Dto
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i.IsType(PersonType.GuestStar))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (i.IsType(PersonType.Director))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (i.IsType(PersonType.Writer))
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (i.IsType(PersonType.Producer))
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (i.IsType(PersonType.Composer))
|
||||
{
|
||||
return 4;
|
||||
|
@ -594,7 +601,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
_logger.LogError(ex, "Error getting person {Name}", c);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).Where(i => i != null)
|
||||
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(x => x.First())
|
||||
|
@ -615,6 +621,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
{
|
||||
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
|
||||
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
baseItemPerson.ImageBlurHashes = dto.ImageBlurHashes;
|
||||
list.Add(baseItemPerson);
|
||||
}
|
||||
}
|
||||
|
@ -727,7 +734,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets simple property values on a DTOBaseItem
|
||||
/// Sets simple property values on a DTOBaseItem.
|
||||
/// </summary>
|
||||
/// <param name="dto">The dto.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
|
@ -1001,7 +1008,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
return null;
|
||||
|
||||
}).Where(i => i != null).ToArray();
|
||||
}
|
||||
|
||||
|
@ -1052,7 +1058,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
return null;
|
||||
|
||||
}).Where(i => i != null).ToArray();
|
||||
}
|
||||
|
||||
|
@ -1350,6 +1355,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
|
||||
}
|
||||
}
|
||||
|
||||
if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
|
||||
{
|
||||
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
|
||||
|
@ -1360,6 +1366,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
|
||||
}
|
||||
}
|
||||
|
||||
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
|
||||
{
|
||||
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
|
||||
|
@ -1370,6 +1377,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
|
||||
}
|
||||
}
|
||||
|
||||
if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
|
||||
{
|
||||
var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IPNetwork2" Version="2.4.0.126" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.211" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.0-pre1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
|
||||
|
@ -33,14 +33,14 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Mono.Nat" Version="2.0.1" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
|
||||
<PackageReference Include="sharpcompress" Version="0.25.0" />
|
||||
<PackageReference Include="sharpcompress" Version="0.25.1" />
|
||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
||||
<PackageReference Include="DotNet.Glob" Version="3.0.9" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -132,7 +132,6 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,6 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +174,6 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Udp;
|
||||
|
@ -43,14 +44,21 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
_logger = logger;
|
||||
_appHost = appHost;
|
||||
_config = configuration;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpServer = new UdpServer(_logger, _appHost, _config);
|
||||
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||
=> SendAsync(options, HttpMethod.Get);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GET request and returns the resulting stream
|
||||
/// Performs a GET request and returns the resulting stream.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
|
|
|
@ -32,12 +32,12 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// The _options
|
||||
/// The _options.
|
||||
/// </summary>
|
||||
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// The _requested ranges
|
||||
/// The _requested ranges.
|
||||
/// </summary>
|
||||
private List<KeyValuePair<long, long?>> _requestedRanges;
|
||||
|
||||
|
|
|
@ -453,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
{
|
||||
httpRes.Headers.Add(key, value);
|
||||
}
|
||||
|
||||
httpRes.ContentType = "text/plain";
|
||||
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
|
@ -591,7 +592,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default CORS headers
|
||||
/// Get the default CORS headers.
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
|
|
|
@ -426,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
/// </summary>
|
||||
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
|
||||
{
|
||||
bool noCache = (requestContext.Headers[HeaderNames.CacheControl].ToString()).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
bool noCache = requestContext.Headers[HeaderNames.CacheControl].ToString().IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
|
||||
|
||||
if (!noCache)
|
||||
|
@ -580,13 +580,12 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
|
||||
{
|
||||
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
|
||||
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest)
|
||||
{
|
||||
OnComplete = options.OnComplete
|
||||
};
|
||||
|
@ -623,8 +622,11 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
/// <summary>
|
||||
/// Adds the caching responseHeaders.
|
||||
/// </summary>
|
||||
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
|
||||
bool noCache, DateTime? lastModifiedDate)
|
||||
private void AddCachingHeaders(
|
||||
IDictionary<string, string> responseHeaders,
|
||||
TimeSpan? cacheDuration,
|
||||
bool noCache,
|
||||
DateTime? lastModifiedDate)
|
||||
{
|
||||
if (noCache)
|
||||
{
|
||||
|
@ -693,7 +695,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that
|
||||
/// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that.
|
||||
/// </summary>
|
||||
/// <param name="date">The date.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
@ -8,13 +9,45 @@ using System.Net;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Emby.Server.Implementations.HttpServer
|
||||
{
|
||||
public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
|
||||
{
|
||||
private const int BufferSize = 81920;
|
||||
|
||||
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
|
||||
|
||||
private List<KeyValuePair<long, long?>> _requestedRanges;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
|
||||
/// </summary>
|
||||
/// <param name="rangeHeader">The range header.</param>
|
||||
/// <param name="contentLength">The content length.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
||||
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contentType));
|
||||
}
|
||||
|
||||
RangeHeader = rangeHeader;
|
||||
SourceStream = source;
|
||||
IsHeadRequest = isHeadRequest;
|
||||
|
||||
ContentType = contentType;
|
||||
Headers[HeaderNames.ContentType] = contentType;
|
||||
Headers[HeaderNames.AcceptRanges] = "bytes";
|
||||
StatusCode = HttpStatusCode.PartialContent;
|
||||
|
||||
SetRangeValues(contentLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source stream.
|
||||
/// </summary>
|
||||
|
@ -29,19 +62,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
private long TotalContentLength { get; set; }
|
||||
|
||||
public Action OnComplete { get; set; }
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private const int BufferSize = 81920;
|
||||
|
||||
/// <summary>
|
||||
/// The _options
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// The us culture
|
||||
/// </summary>
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
/// <summary>
|
||||
/// Additional HTTP Headers
|
||||
|
@ -50,32 +70,57 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
public IDictionary<string, string> Headers => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
|
||||
/// Gets the requested ranges.
|
||||
/// </summary>
|
||||
/// <param name="rangeHeader">The range header.</param>
|
||||
/// <param name="contentLength">The content length.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
||||
/// <param name="logger">The logger instance.</param>
|
||||
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
|
||||
/// <value>The requested ranges.</value>
|
||||
protected List<KeyValuePair<long, long?>> RequestedRanges
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
get
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contentType));
|
||||
if (_requestedRanges == null)
|
||||
{
|
||||
_requestedRanges = new List<KeyValuePair<long, long?>>();
|
||||
|
||||
// Example: bytes=0-,32-63
|
||||
var ranges = RangeHeader.Split('=')[1].Split(',');
|
||||
|
||||
foreach (var range in ranges)
|
||||
{
|
||||
var vals = range.Split('-');
|
||||
|
||||
long start = 0;
|
||||
long? end = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(vals[0]))
|
||||
{
|
||||
start = long.Parse(vals[0], CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
RangeHeader = rangeHeader;
|
||||
SourceStream = source;
|
||||
IsHeadRequest = isHeadRequest;
|
||||
this._logger = logger;
|
||||
if (!string.IsNullOrEmpty(vals[1]))
|
||||
{
|
||||
end = long.Parse(vals[1], CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
ContentType = contentType;
|
||||
Headers[HeaderNames.ContentType] = contentType;
|
||||
Headers[HeaderNames.AcceptRanges] = "bytes";
|
||||
StatusCode = HttpStatusCode.PartialContent;
|
||||
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
|
||||
}
|
||||
}
|
||||
|
||||
SetRangeValues(contentLength);
|
||||
return _requestedRanges;
|
||||
}
|
||||
}
|
||||
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public IRequest RequestContext { get; set; }
|
||||
|
||||
public object Response { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
|
||||
public HttpStatusCode StatusCode
|
||||
{
|
||||
get => (HttpStatusCode)Status;
|
||||
set => Status = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -109,49 +154,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _requested ranges
|
||||
/// </summary>
|
||||
private List<KeyValuePair<long, long?>> _requestedRanges;
|
||||
/// <summary>
|
||||
/// Gets the requested ranges.
|
||||
/// </summary>
|
||||
/// <value>The requested ranges.</value>
|
||||
protected List<KeyValuePair<long, long?>> RequestedRanges
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_requestedRanges == null)
|
||||
{
|
||||
_requestedRanges = new List<KeyValuePair<long, long?>>();
|
||||
|
||||
// Example: bytes=0-,32-63
|
||||
var ranges = RangeHeader.Split('=')[1].Split(',');
|
||||
|
||||
foreach (var range in ranges)
|
||||
{
|
||||
var vals = range.Split('-');
|
||||
|
||||
long start = 0;
|
||||
long? end = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(vals[0]))
|
||||
{
|
||||
start = long.Parse(vals[0], UsCulture);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(vals[1]))
|
||||
{
|
||||
end = long.Parse(vals[1], UsCulture);
|
||||
}
|
||||
|
||||
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
|
||||
}
|
||||
}
|
||||
|
||||
return _requestedRanges;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
|
@ -167,37 +169,31 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
// If the requested range is "0-", we can optimize by just doing a stream copy
|
||||
if (RangeEnd >= TotalContentLength - 1)
|
||||
{
|
||||
await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
|
||||
await source.CopyToAsync(responseStream, BufferSize, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false);
|
||||
await CopyToInternalAsync(source, responseStream, RangeLength, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (OnComplete != null)
|
||||
{
|
||||
OnComplete();
|
||||
}
|
||||
OnComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength)
|
||||
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
|
||||
{
|
||||
var array = ArrayPool<byte>.Shared.Rent(BufferSize);
|
||||
try
|
||||
{
|
||||
var array = new byte[BufferSize];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
|
||||
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var bytesToCopy = Math.Min(bytesRead, copyLength);
|
||||
|
||||
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false);
|
||||
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
copyLength -= bytesToCopy;
|
||||
|
||||
|
@ -207,19 +203,10 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public IRequest RequestContext { get; set; }
|
||||
|
||||
public object Response { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
|
||||
public HttpStatusCode StatusCode
|
||||
finally
|
||||
{
|
||||
get => (HttpStatusCode)Status;
|
||||
set => Status = (int)value;
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,11 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
res.Headers.Add(key, value);
|
||||
}
|
||||
// Try to prevent compatibility view
|
||||
res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " +
|
||||
res.Headers["Access-Control-Allow-Headers"] = "Accept, Accept-Language, Authorization, Cache-Control, " +
|
||||
"Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " +
|
||||
"Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " +
|
||||
"Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " +
|
||||
"X-Emby-Authorization");
|
||||
"X-Emby-Authorization";
|
||||
|
||||
if (dto is Exception exception)
|
||||
{
|
||||
|
|
|
@ -266,7 +266,6 @@ namespace Emby.Server.Implementations.IO
|
|||
{
|
||||
DisposeWatcher(newWatcher, false);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -393,7 +392,6 @@ namespace Emby.Server.Implementations.IO
|
|||
}
|
||||
|
||||
return false;
|
||||
|
||||
}))
|
||||
{
|
||||
monitorPath = false;
|
||||
|
|
|
@ -245,6 +245,16 @@ namespace Emby.Server.Implementations.IO
|
|||
if (info is FileInfo fileInfo)
|
||||
{
|
||||
result.Length = fileInfo.Length;
|
||||
|
||||
// Issue #2354 get the size of files behind symbolic links
|
||||
if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
|
||||
{
|
||||
result.Length = thisFileStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
result.DirectoryName = fileInfo.DirectoryName;
|
||||
}
|
||||
|
||||
|
@ -628,6 +638,7 @@ namespace Emby.Server.Implementations.IO
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
|
||||
});
|
||||
}
|
||||
|
@ -682,6 +693,7 @@ namespace Emby.Server.Implementations.IO
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,11 +36,6 @@ namespace Emby.Server.Implementations
|
|||
/// </summary>
|
||||
string RestartArgs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --plugin-manifest-url command line option.
|
||||
/// </summary>
|
||||
string PluginManifestUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --published-server-url command line option.
|
||||
/// </summary>
|
||||
|
|
|
@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.Images
|
|||
new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
|
||||
},
|
||||
IncludeItemTypes = includeItemTypes
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ namespace Emby.Server.Implementations.Images
|
|||
}
|
||||
|
||||
return i;
|
||||
|
||||
}).GroupBy(x => x.Id)
|
||||
.Select(x => x.First());
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
|
@ -8,24 +9,33 @@ using MediaBrowser.Model.IO;
|
|||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the core resolver ignore rules
|
||||
/// Provides the core resolver ignore rules.
|
||||
/// </summary>
|
||||
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IServerApplicationPaths _serverApplicationPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
|
||||
/// <param name="serverApplicationPaths">The server application paths.</param>
|
||||
public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_serverApplicationPaths = serverApplicationPaths;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
|
||||
{
|
||||
// Don't ignore application folders
|
||||
if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't ignore top level folders
|
||||
if (fileInfo.IsDirectory && parent is AggregateFolder)
|
||||
{
|
||||
|
|
|
@ -12,11 +12,13 @@ namespace Emby.Server.Implementations.Library
|
|||
public class ExclusiveLiveStream : ILiveStream
|
||||
{
|
||||
public int ConsumerCount { get; set; }
|
||||
|
||||
public string OriginalStreamId { get; set; }
|
||||
|
||||
public string TunerHostId => null;
|
||||
|
||||
public bool EnableStreamSharing { get; set; }
|
||||
|
||||
public MediaSourceInfo MediaSource { get; set; }
|
||||
|
||||
public string UniqueId { get; private set; }
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DotNet.Globbing;
|
||||
|
||||
namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Glob patterns for files to ignore
|
||||
/// Glob patterns for files to ignore.
|
||||
/// </summary>
|
||||
public static class IgnorePatterns
|
||||
{
|
||||
/// <summary>
|
||||
/// Files matching these glob patterns will be ignored
|
||||
/// Files matching these glob patterns will be ignored.
|
||||
/// </summary>
|
||||
public static readonly string[] Patterns = new string[]
|
||||
private static readonly string[] _patterns =
|
||||
{
|
||||
"**/small.jpg",
|
||||
"**/albumart.jpg",
|
||||
|
@ -19,32 +22,51 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
// Directories
|
||||
"**/metadata/**",
|
||||
"**/metadata",
|
||||
"**/ps3_update/**",
|
||||
"**/ps3_update",
|
||||
"**/ps3_vprm/**",
|
||||
"**/ps3_vprm",
|
||||
"**/extrafanart/**",
|
||||
"**/extrafanart",
|
||||
"**/extrathumbs/**",
|
||||
"**/extrathumbs",
|
||||
"**/.actors/**",
|
||||
"**/.actors",
|
||||
"**/.wd_tv/**",
|
||||
"**/.wd_tv",
|
||||
"**/lost+found/**",
|
||||
"**/lost+found",
|
||||
|
||||
// WMC temp recording directories that will constantly be written to
|
||||
"**/TempRec/**",
|
||||
"**/TempRec",
|
||||
"**/TempSBE/**",
|
||||
"**/TempSBE",
|
||||
|
||||
// Synology
|
||||
"**/eaDir/**",
|
||||
"**/eaDir",
|
||||
"**/@eaDir/**",
|
||||
"**/@eaDir",
|
||||
"**/#recycle/**",
|
||||
"**/#recycle",
|
||||
|
||||
// Qnap
|
||||
"**/@Recycle/**",
|
||||
"**/@Recycle",
|
||||
"**/.@__thumb/**",
|
||||
"**/.@__thumb",
|
||||
"**/$RECYCLE.BIN/**",
|
||||
"**/$RECYCLE.BIN",
|
||||
"**/System Volume Information/**",
|
||||
"**/System Volume Information",
|
||||
"**/.grab/**",
|
||||
"**/.grab",
|
||||
|
||||
// Unix hidden files and directories
|
||||
"**/.*/**",
|
||||
"**/.*",
|
||||
|
||||
// thumbs.db
|
||||
"**/thumbs.db",
|
||||
|
@ -56,19 +78,31 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
private static readonly GlobOptions _globOptions = new GlobOptions
|
||||
{
|
||||
Evaluation = {
|
||||
Evaluation =
|
||||
{
|
||||
CaseInsensitive = true
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
|
||||
private static readonly Glob[] _globs = _patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the supplied path should be ignored
|
||||
/// Returns true if the supplied path should be ignored.
|
||||
/// </summary>
|
||||
public static bool ShouldIgnore(string path)
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>Whether to ignore the path.</returns>
|
||||
public static bool ShouldIgnore(ReadOnlySpan<char> path)
|
||||
{
|
||||
return _globs.Any(g => g.IsMatch(path));
|
||||
int len = _globs.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (_globs[i].IsMatch(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,13 +97,13 @@ namespace Emby.Server.Implementations.Library
|
|||
private IIntroProvider[] IntroProviders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of entity resolution ignore rules
|
||||
/// Gets or sets the list of entity resolution ignore rules.
|
||||
/// </summary>
|
||||
/// <value>The entity resolution ignore rules.</value>
|
||||
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of currently registered entity resolvers
|
||||
/// Gets or sets the list of currently registered entity resolvers.
|
||||
/// </summary>
|
||||
/// <value>The entity resolvers enumerable.</value>
|
||||
private IItemResolver[] EntityResolvers { get; set; }
|
||||
|
@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.Library
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host</param>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="taskManager">The task manager.</param>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
|
@ -209,12 +209,12 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _root folder
|
||||
/// The _root folder.
|
||||
/// </summary>
|
||||
private volatile AggregateFolder _rootFolder;
|
||||
|
||||
/// <summary>
|
||||
/// The _root folder sync lock
|
||||
/// The _root folder sync lock.
|
||||
/// </summary>
|
||||
private readonly object _rootFolderSyncLock = new object();
|
||||
|
||||
|
@ -341,7 +341,7 @@ namespace Emby.Server.Implementations.Library
|
|||
if (item is LiveTvProgram)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
item.Path ?? string.Empty,
|
||||
|
@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library
|
|||
else
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
item.Path ?? string.Empty,
|
||||
|
@ -368,7 +368,12 @@ namespace Emby.Server.Implementations.Library
|
|||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
|
||||
_logger.LogDebug(
|
||||
"Deleting metadata path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
metadataPath,
|
||||
item.Id);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -392,7 +397,13 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Deleting path {path}", fileSystemInfo.FullName);
|
||||
_logger.LogInformation(
|
||||
"Deleting item path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
fileSystemInfo.FullName,
|
||||
item.Id);
|
||||
|
||||
if (fileSystemInfo.IsDirectory)
|
||||
{
|
||||
Directory.Delete(fileSystemInfo.FullName, true);
|
||||
|
@ -514,8 +525,8 @@ namespace Emby.Server.Implementations.Library
|
|||
return key.GetMD5();
|
||||
}
|
||||
|
||||
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
|
||||
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
|
||||
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
|
||||
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
|
||||
|
||||
private BaseItem ResolvePath(
|
||||
FileSystemMetadata fileInfo,
|
||||
|
@ -523,8 +534,7 @@ namespace Emby.Server.Implementations.Library
|
|||
IItemResolver[] resolvers,
|
||||
Folder parent = null,
|
||||
string collectionType = null,
|
||||
LibraryOptions libraryOptions = null,
|
||||
bool allowIgnorePath = true)
|
||||
LibraryOptions libraryOptions = null)
|
||||
{
|
||||
if (fileInfo == null)
|
||||
{
|
||||
|
@ -548,7 +558,7 @@ namespace Emby.Server.Implementations.Library
|
|||
};
|
||||
|
||||
// Return null if ignore rules deem that we should do so
|
||||
if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
|
||||
if (IgnoreFile(args.FileInfo, args.Parent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -627,7 +637,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a path should be ignored based on its contents - called after the contents have been read
|
||||
/// Determines whether a path should be ignored based on its contents - called after the contents have been read.
|
||||
/// </summary>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
|
@ -713,7 +723,7 @@ namespace Emby.Server.Implementations.Library
|
|||
Directory.CreateDirectory(rootFolderPath);
|
||||
|
||||
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
|
||||
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
|
||||
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
|
||||
.DeepCopy<Folder, AggregateFolder>();
|
||||
|
||||
// In case program data folder was moved
|
||||
|
@ -795,7 +805,7 @@ namespace Emby.Server.Implementations.Library
|
|||
if (tmpItem == null)
|
||||
{
|
||||
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
|
||||
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
|
||||
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
|
||||
}
|
||||
|
||||
// In case program data folder was moved
|
||||
|
@ -909,7 +919,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Genre
|
||||
/// Gets a Genre.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{Genre}.</returns>
|
||||
|
@ -990,7 +1000,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the root media folder
|
||||
/// Reloads the root media folder.
|
||||
/// </summary>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
|
@ -1793,7 +1803,7 @@ namespace Emby.Server.Implementations.Library
|
|||
/// Creates the items.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="parent">The parent item</param>
|
||||
/// <param name="parent">The parent item.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -1894,9 +1904,19 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
|
||||
image.Width = size.Width;
|
||||
image.Height = size.Height;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
|
||||
image.Width = 0;
|
||||
image.Height = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -2784,10 +2804,12 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(from))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(from));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(to))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(to));
|
||||
|
@ -2861,7 +2883,6 @@ namespace Emby.Server.Implementations.Library
|
|||
_logger.LogError(ex, "Error getting person");
|
||||
return null;
|
||||
}
|
||||
|
||||
}).Where(i => i != null).ToList();
|
||||
}
|
||||
|
||||
|
@ -2896,7 +2917,8 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
|
||||
if (ex.StatusCode.HasValue
|
||||
&& (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -2989,21 +3011,6 @@ namespace Emby.Server.Implementations.Library
|
|||
});
|
||||
}
|
||||
|
||||
private static bool ValidateNetworkPath(string path)
|
||||
{
|
||||
//if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
//{
|
||||
// // We can't validate protocol-based paths, so just allow them
|
||||
// if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
// {
|
||||
// return Directory.Exists(path);
|
||||
// }
|
||||
//}
|
||||
|
||||
// Without native support for unc, we cannot validate this when running under mono
|
||||
return true;
|
||||
}
|
||||
|
||||
private const string ShortcutFileExtension = ".mblink";
|
||||
|
||||
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
||||
|
@ -3030,11 +3037,6 @@ namespace Emby.Server.Implementations.Library
|
|||
throw new FileNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new FileNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
|
@ -3073,11 +3075,6 @@ namespace Emby.Server.Implementations.Library
|
|||
throw new ArgumentNullException(nameof(pathInfo));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new FileNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
|
|
|
@ -148,17 +148,14 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
videoStream.BitRate = 30000000;
|
||||
}
|
||||
|
||||
else if (width >= 1900)
|
||||
{
|
||||
videoStream.BitRate = 20000000;
|
||||
}
|
||||
|
||||
else if (width >= 1200)
|
||||
{
|
||||
videoStream.BitRate = 8000000;
|
||||
}
|
||||
|
||||
else if (width >= 700)
|
||||
{
|
||||
videoStream.BitRate = 2000000;
|
||||
|
|
|
@ -205,22 +205,27 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
return MediaProtocol.Rtsp;
|
||||
}
|
||||
|
||||
if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return MediaProtocol.Rtmp;
|
||||
}
|
||||
|
||||
if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return MediaProtocol.Http;
|
||||
}
|
||||
|
||||
if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return MediaProtocol.Rtp;
|
||||
}
|
||||
|
||||
if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return MediaProtocol.Ftp;
|
||||
}
|
||||
|
||||
if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return MediaProtocol.Udp;
|
||||
|
@ -436,7 +441,6 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
|
||||
.ThenByDescending(i =>
|
||||
{
|
||||
|
@ -620,7 +624,6 @@ namespace Emby.Server.Implementations.Library
|
|||
MediaSource = mediaSource,
|
||||
ExtractChapters = false,
|
||||
MediaType = DlnaProfileType.Video
|
||||
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
mediaSource.MediaStreams = info.MediaStreams;
|
||||
|
@ -748,17 +751,14 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
videoStream.BitRate = 30000000;
|
||||
}
|
||||
|
||||
else if (width >= 1900)
|
||||
{
|
||||
videoStream.BitRate = 20000000;
|
||||
}
|
||||
|
||||
else if (width >= 1200)
|
||||
{
|
||||
videoStream.BitRate = 8000000;
|
||||
}
|
||||
|
||||
else if (width >= 700)
|
||||
{
|
||||
videoStream.BitRate = 2000000;
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures DateCreated and DateModified have values
|
||||
/// Ensures DateCreated and DateModified have values.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
|
|
|
@ -19,7 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
|
|||
|
||||
// Only process items that are in a collection folder containing books
|
||||
if (!string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.IsDirectory)
|
||||
{
|
||||
|
@ -55,7 +57,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
|
|||
|
||||
// Don't return a Book if there is more (or less) than one document in the directory
|
||||
if (bookFiles.Count != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Book
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
public virtual ResolverPriority Priority => ResolverPriority.First;
|
||||
|
||||
/// <summary>
|
||||
/// Sets initial values on the newly resolved item
|
||||
/// Sets initial values on the newly resolved item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
|
|
|
@ -41,10 +41,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
{
|
||||
return new AggregateFolder();
|
||||
}
|
||||
|
||||
if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new UserRootFolder(); // if we got here and still a root - must be user root
|
||||
}
|
||||
|
||||
if (args.IsVf)
|
||||
{
|
||||
return new CollectionFolder
|
||||
|
@ -73,7 +75,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
})
|
||||
.Select(i => _fileSystem.GetFileNameWithoutExtension(i))
|
||||
.FirstOrDefault();
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
|||
episode.SeriesId = series.Id;
|
||||
episode.SeriesName = series.Name;
|
||||
}
|
||||
|
||||
if (season != null)
|
||||
{
|
||||
episode.SeasonId = season.Id;
|
||||
|
|
|
@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
|||
/// </summary>
|
||||
/// <param name="config">The config.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <param name="localization">The localization</param>
|
||||
/// <param name="logger">The logger</param>
|
||||
/// <param name="localization">The localization.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public SeasonResolver(
|
||||
IServerConfigurationManager config,
|
||||
ILibraryManager libraryManager,
|
||||
|
@ -94,7 +94,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
|||
_localization.GetLocalizedString("NameSeasonNumber"),
|
||||
seasonNumber,
|
||||
args.GetLibraryOptions().PreferredMetadataLanguage);
|
||||
|
||||
}
|
||||
|
||||
return season;
|
||||
|
|
|
@ -194,6 +194,7 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
searchQuery.AncestorIds = new[] { searchQuery.ParentId };
|
||||
}
|
||||
|
||||
searchQuery.ParentId = Guid.Empty;
|
||||
searchQuery.IncludeItemsByName = true;
|
||||
searchQuery.IncludeItemTypes = Array.Empty<string>();
|
||||
|
@ -207,7 +208,6 @@ namespace Emby.Server.Implementations.Library
|
|||
return mediaItems.Select(i => new SearchHintInfo
|
||||
{
|
||||
Item = i
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all user data for the given user
|
||||
/// Retrieve all user data for the given user.
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a UserItemData to a DTOUserItemData
|
||||
/// Converts a UserItemData to a DTOUserItemData.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>DtoUserItemData.</returns>
|
||||
|
|
|
@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Library.Validators
|
|||
_libraryManager.DeleteItem(item, new DeleteOptions
|
||||
{
|
||||
DeleteFileLocation = false
|
||||
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,6 @@ namespace Emby.Server.Implementations.Library.Validators
|
|||
_libraryManager.DeleteItem(item, new DeleteOptions
|
||||
{
|
||||
DeleteFileLocation = false
|
||||
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -1547,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
IsFolder = false,
|
||||
Recursive = true,
|
||||
DtoOptions = new DtoOptions(true)
|
||||
|
||||
})
|
||||
.Where(i => i.IsFileProtocol && File.Exists(i.Path))
|
||||
.Skip(seriesTimer.KeepUpTo - 1)
|
||||
|
|
|
@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
name += " " + info.EpisodeTitle;
|
||||
}
|
||||
}
|
||||
|
||||
else if (info.IsMovie && info.ProductionYear != null)
|
||||
{
|
||||
name += " (" + info.ProductionYear + ")";
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
{
|
||||
channelNumber = map.channel;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(channelNumber))
|
||||
{
|
||||
channelNumber = map.atscMajor + "." + map.atscMinor;
|
||||
|
@ -400,6 +401,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
{
|
||||
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
|
@ -622,6 +624,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
_lastErrorResponse = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
|
@ -805,11 +808,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
{
|
||||
throw new ArgumentException("Username is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(info.Password))
|
||||
{
|
||||
throw new ArgumentException("Password is required");
|
||||
}
|
||||
}
|
||||
|
||||
if (validateListings)
|
||||
{
|
||||
if (string.IsNullOrEmpty(info.ListingsId))
|
||||
|
@ -932,24 +937,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class Token
|
||||
{
|
||||
public int code { get; set; }
|
||||
|
||||
public string message { get; set; }
|
||||
|
||||
public string serverID { get; set; }
|
||||
|
||||
public string token { get; set; }
|
||||
}
|
||||
|
||||
public class Lineup
|
||||
{
|
||||
public string lineup { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
|
||||
public string location { get; set; }
|
||||
|
||||
public string uri { get; set; }
|
||||
}
|
||||
|
||||
public class Lineups
|
||||
{
|
||||
public int code { get; set; }
|
||||
|
||||
public string serverID { get; set; }
|
||||
|
||||
public string datetime { get; set; }
|
||||
|
||||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
|
@ -957,8 +973,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class Headends
|
||||
{
|
||||
public string headend { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
|
||||
public string location { get; set; }
|
||||
|
||||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
|
@ -967,59 +986,83 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class Map
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public string channel { get; set; }
|
||||
|
||||
public string logicalChannelNumber { get; set; }
|
||||
|
||||
public int uhfVhf { get; set; }
|
||||
|
||||
public int atscMajor { get; set; }
|
||||
|
||||
public int atscMinor { get; set; }
|
||||
}
|
||||
|
||||
public class Broadcaster
|
||||
{
|
||||
public string city { get; set; }
|
||||
|
||||
public string state { get; set; }
|
||||
|
||||
public string postalcode { get; set; }
|
||||
|
||||
public string country { get; set; }
|
||||
}
|
||||
|
||||
public class Logo
|
||||
{
|
||||
public string URL { get; set; }
|
||||
|
||||
public int height { get; set; }
|
||||
|
||||
public int width { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
}
|
||||
|
||||
public class Station
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string callsign { get; set; }
|
||||
|
||||
public List<string> broadcastLanguage { get; set; }
|
||||
|
||||
public List<string> descriptionLanguage { get; set; }
|
||||
|
||||
public Broadcaster broadcaster { get; set; }
|
||||
|
||||
public string affiliate { get; set; }
|
||||
|
||||
public Logo logo { get; set; }
|
||||
|
||||
public bool? isCommercialFree { get; set; }
|
||||
}
|
||||
|
||||
public class Metadata
|
||||
{
|
||||
public string lineup { get; set; }
|
||||
|
||||
public string modified { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
}
|
||||
|
||||
public class Channel
|
||||
{
|
||||
public List<Map> map { get; set; }
|
||||
|
||||
public List<Station> stations { get; set; }
|
||||
|
||||
public Metadata metadata { get; set; }
|
||||
}
|
||||
|
||||
public class RequestScheduleForChannel
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public List<string> date { get; set; }
|
||||
}
|
||||
|
||||
|
@ -1029,29 +1072,43 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class Rating
|
||||
{
|
||||
public string body { get; set; }
|
||||
|
||||
public string code { get; set; }
|
||||
}
|
||||
|
||||
public class Multipart
|
||||
{
|
||||
public int partNumber { get; set; }
|
||||
|
||||
public int totalParts { get; set; }
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public string airDateTime { get; set; }
|
||||
|
||||
public int duration { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public List<string> audioProperties { get; set; }
|
||||
|
||||
public List<string> videoProperties { get; set; }
|
||||
|
||||
public List<Rating> ratings { get; set; }
|
||||
|
||||
public bool? @new { get; set; }
|
||||
|
||||
public Multipart multipart { get; set; }
|
||||
|
||||
public string liveTapeDelay { get; set; }
|
||||
|
||||
public bool premiere { get; set; }
|
||||
|
||||
public bool repeat { get; set; }
|
||||
|
||||
public string isPremiereOrFinale { get; set; }
|
||||
}
|
||||
|
||||
|
@ -1060,16 +1117,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class MetadataSchedule
|
||||
{
|
||||
public string modified { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public string startDate { get; set; }
|
||||
|
||||
public string endDate { get; set; }
|
||||
|
||||
public int days { get; set; }
|
||||
}
|
||||
|
||||
public class Day
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public List<Program> programs { get; set; }
|
||||
|
||||
public MetadataSchedule metadata { get; set; }
|
||||
|
||||
public Day()
|
||||
|
@ -1092,24 +1155,28 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class Description100
|
||||
{
|
||||
public string descriptionLanguage { get; set; }
|
||||
|
||||
public string description { get; set; }
|
||||
}
|
||||
|
||||
public class Description1000
|
||||
{
|
||||
public string descriptionLanguage { get; set; }
|
||||
|
||||
public string description { get; set; }
|
||||
}
|
||||
|
||||
public class DescriptionsProgram
|
||||
{
|
||||
public List<Description100> description100 { get; set; }
|
||||
|
||||
public List<Description1000> description1000 { get; set; }
|
||||
}
|
||||
|
||||
public class Gracenote
|
||||
{
|
||||
public int season { get; set; }
|
||||
|
||||
public int episode { get; set; }
|
||||
}
|
||||
|
||||
|
@ -1121,104 +1188,154 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public class ContentRating
|
||||
{
|
||||
public string body { get; set; }
|
||||
|
||||
public string code { get; set; }
|
||||
}
|
||||
|
||||
public class Cast
|
||||
{
|
||||
public string billingOrder { get; set; }
|
||||
|
||||
public string role { get; set; }
|
||||
|
||||
public string nameId { get; set; }
|
||||
|
||||
public string personId { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string characterName { get; set; }
|
||||
}
|
||||
|
||||
public class Crew
|
||||
{
|
||||
public string billingOrder { get; set; }
|
||||
|
||||
public string role { get; set; }
|
||||
|
||||
public string nameId { get; set; }
|
||||
|
||||
public string personId { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class QualityRating
|
||||
{
|
||||
public string ratingsBody { get; set; }
|
||||
|
||||
public string rating { get; set; }
|
||||
|
||||
public string minRating { get; set; }
|
||||
|
||||
public string maxRating { get; set; }
|
||||
|
||||
public string increment { get; set; }
|
||||
}
|
||||
|
||||
public class Movie
|
||||
{
|
||||
public string year { get; set; }
|
||||
|
||||
public int duration { get; set; }
|
||||
|
||||
public List<QualityRating> qualityRating { get; set; }
|
||||
}
|
||||
|
||||
public class Recommendation
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public string title120 { get; set; }
|
||||
}
|
||||
|
||||
public class ProgramDetails
|
||||
{
|
||||
public string audience { get; set; }
|
||||
|
||||
public string programID { get; set; }
|
||||
|
||||
public List<Title> titles { get; set; }
|
||||
|
||||
public EventDetails eventDetails { get; set; }
|
||||
|
||||
public DescriptionsProgram descriptions { get; set; }
|
||||
|
||||
public string originalAirDate { get; set; }
|
||||
|
||||
public List<string> genres { get; set; }
|
||||
|
||||
public string episodeTitle150 { get; set; }
|
||||
|
||||
public List<MetadataPrograms> metadata { get; set; }
|
||||
|
||||
public List<ContentRating> contentRating { get; set; }
|
||||
|
||||
public List<Cast> cast { get; set; }
|
||||
|
||||
public List<Crew> crew { get; set; }
|
||||
|
||||
public string entityType { get; set; }
|
||||
|
||||
public string showType { get; set; }
|
||||
|
||||
public bool hasImageArtwork { get; set; }
|
||||
|
||||
public string primaryImage { get; set; }
|
||||
|
||||
public string thumbImage { get; set; }
|
||||
|
||||
public string backdropImage { get; set; }
|
||||
|
||||
public string bannerImage { get; set; }
|
||||
|
||||
public string imageID { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public List<string> contentAdvisory { get; set; }
|
||||
|
||||
public Movie movie { get; set; }
|
||||
|
||||
public List<Recommendation> recommendations { get; set; }
|
||||
}
|
||||
|
||||
public class Caption
|
||||
{
|
||||
public string content { get; set; }
|
||||
|
||||
public string lang { get; set; }
|
||||
}
|
||||
|
||||
public class ImageData
|
||||
{
|
||||
public string width { get; set; }
|
||||
|
||||
public string height { get; set; }
|
||||
|
||||
public string uri { get; set; }
|
||||
|
||||
public string size { get; set; }
|
||||
|
||||
public string aspect { get; set; }
|
||||
|
||||
public string category { get; set; }
|
||||
|
||||
public string text { get; set; }
|
||||
|
||||
public string primary { get; set; }
|
||||
|
||||
public string tier { get; set; }
|
||||
|
||||
public Caption caption { get; set; }
|
||||
}
|
||||
|
||||
public class ShowImages
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public List<ImageData> data { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
{
|
||||
uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (programInfo.EpisodeNumber.HasValue)
|
||||
{
|
||||
uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
|
|
@ -556,6 +556,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
item.ParentId = channel.Id;
|
||||
|
||||
// item.ChannelType = channelType;
|
||||
|
@ -575,6 +576,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
item.ExternalSeriesId = seriesId;
|
||||
|
||||
var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
|
||||
|
@ -589,30 +591,37 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
tags.Add("Live");
|
||||
}
|
||||
|
||||
if (info.IsPremiere)
|
||||
{
|
||||
tags.Add("Premiere");
|
||||
}
|
||||
|
||||
if (info.IsNews)
|
||||
{
|
||||
tags.Add("News");
|
||||
}
|
||||
|
||||
if (info.IsSports)
|
||||
{
|
||||
tags.Add("Sports");
|
||||
}
|
||||
|
||||
if (info.IsKids)
|
||||
{
|
||||
tags.Add("Kids");
|
||||
}
|
||||
|
||||
if (info.IsRepeat)
|
||||
{
|
||||
tags.Add("Repeat");
|
||||
}
|
||||
|
||||
if (info.IsMovie)
|
||||
{
|
||||
tags.Add("Movie");
|
||||
}
|
||||
|
||||
if (isSeries)
|
||||
{
|
||||
tags.Add("Series");
|
||||
|
@ -635,6 +644,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
item.IsSeries = isSeries;
|
||||
|
||||
item.Name = info.Name;
|
||||
|
@ -652,12 +662,14 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
{
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
item.StartDate = info.StartDate;
|
||||
|
||||
if (item.EndDate != info.EndDate)
|
||||
{
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
item.EndDate = info.EndDate;
|
||||
|
||||
item.ProductionYear = info.ProductionYear;
|
||||
|
@ -1168,7 +1180,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
|
||||
ChannelIds = new Guid[] { currentChannel.Id },
|
||||
DtoOptions = new DtoOptions(true)
|
||||
|
||||
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
|
||||
|
||||
var newPrograms = new List<LiveTvProgram>();
|
||||
|
@ -1738,7 +1749,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
var results = await GetTimers(new TimerQuery
|
||||
{
|
||||
Id = id
|
||||
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
|
@ -1790,7 +1800,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
.Select(i =>
|
||||
{
|
||||
return i.Item1;
|
||||
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
|
@ -1845,7 +1854,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
}
|
||||
|
||||
return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
|
||||
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
|
@ -1878,7 +1886,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
|
||||
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
|
||||
DtoOptions = options
|
||||
|
||||
}) : new List<BaseItem>();
|
||||
|
||||
RemoveFields(options);
|
||||
|
@ -2456,7 +2463,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
UserId = user.Id,
|
||||
IsRecordingsFolder = true,
|
||||
RefreshLatestChannelItems = refreshChannels
|
||||
|
||||
}).Items);
|
||||
|
||||
return folders.Cast<BaseItem>().ToList();
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the triggers that define when the task will run
|
||||
/// Creates the triggers that define when the task will run.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
|
|
|
@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +115,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
ChannelType = ChannelType.TV,
|
||||
IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase),
|
||||
Path = i.URL
|
||||
|
||||
}).Cast<ChannelInfo>().ToList();
|
||||
}
|
||||
|
||||
|
@ -171,6 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
_modelCache[cacheKey] = response;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
|
||||
var name = line.Substring(0, index - 1);
|
||||
var currentChannel = line.Substring(index + 7);
|
||||
if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
|
||||
if (currentChannel != "none")
|
||||
{
|
||||
status = LiveTvTunerStatus.LiveTv;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = LiveTvTunerStatus.Available;
|
||||
}
|
||||
|
||||
tuners.Add(new LiveTvTunerInfo
|
||||
{
|
||||
Name = name,
|
||||
|
@ -230,11 +238,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
inside = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (let == '>')
|
||||
{
|
||||
inside = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inside)
|
||||
{
|
||||
buffer[bufferIndex] = let;
|
||||
|
@ -332,12 +342,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
private class Channels
|
||||
{
|
||||
public string GuideNumber { get; set; }
|
||||
|
||||
public string GuideName { get; set; }
|
||||
|
||||
public string VideoCodec { get; set; }
|
||||
|
||||
public string AudioCodec { get; set; }
|
||||
|
||||
public string URL { get; set; }
|
||||
|
||||
public bool Favorite { get; set; }
|
||||
|
||||
public bool DRM { get; set; }
|
||||
|
||||
public int HD { get; set; }
|
||||
}
|
||||
|
||||
|
@ -481,7 +498,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
Height = height,
|
||||
BitRate = videoBitrate,
|
||||
NalLengthSize = nal
|
||||
|
||||
},
|
||||
new MediaStream
|
||||
{
|
||||
|
@ -659,13 +675,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
public class DiscoverResponse
|
||||
{
|
||||
public string FriendlyName { get; set; }
|
||||
|
||||
public string ModelNumber { get; set; }
|
||||
|
||||
public string FirmwareName { get; set; }
|
||||
|
||||
public string FirmwareVersion { get; set; }
|
||||
|
||||
public string DeviceID { get; set; }
|
||||
|
||||
public string DeviceAuth { get; set; }
|
||||
|
||||
public string BaseURL { get; set; }
|
||||
|
||||
public string LineupURL { get; set; }
|
||||
|
||||
public int TunerCount { get; set; }
|
||||
|
||||
public bool SupportsTranscoding
|
||||
|
@ -674,7 +698,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
{
|
||||
var model = ModelNumber ?? string.Empty;
|
||||
|
||||
if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
|
||||
if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -722,7 +746,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
|
|
@ -58,12 +58,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
protected virtual int EmptyReadLimit => 1000;
|
||||
|
||||
public MediaSourceInfo OriginalMediaSource { get; set; }
|
||||
|
||||
public MediaSourceInfo MediaSource { get; set; }
|
||||
|
||||
public int ConsumerCount { get; set; }
|
||||
|
||||
public string OriginalStreamId { get; set; }
|
||||
|
||||
public bool EnableStreamSharing { get; set; }
|
||||
|
||||
public string UniqueId { get; }
|
||||
|
||||
public string TunerHostId { get; }
|
||||
|
@ -220,11 +223,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -127,7 +127,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
{
|
||||
using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!IsValidChannelNumber(numberString))
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
"Sync": "Sinkroniseer",
|
||||
"HeaderFavoriteSongs": "Gunsteling Liedjies",
|
||||
"Songs": "Liedjies",
|
||||
"DeviceOnlineWithName": "{0} is verbind",
|
||||
"DeviceOfflineWithName": "{0} het afgesluit",
|
||||
"DeviceOnlineWithName": "{0} gekoppel is",
|
||||
"DeviceOfflineWithName": "{0} is ontkoppel",
|
||||
"Collections": "Versamelings",
|
||||
"Inherit": "Ontvang",
|
||||
"HeaderLiveTV": "Live TV",
|
||||
|
@ -91,5 +91,9 @@
|
|||
"ChapterNameValue": "Hoofstuk",
|
||||
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
|
||||
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
|
||||
"Albums": "Albums"
|
||||
"Albums": "Albums",
|
||||
"TasksChannelsCategory": "Internet kanale",
|
||||
"TasksApplicationCategory": "aansoek",
|
||||
"TasksLibraryCategory": "biblioteek",
|
||||
"TasksMaintenanceCategory": "onderhoud"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"Albums": "ألبومات",
|
||||
"Albums": "البومات",
|
||||
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
||||
"Application": "تطبيق",
|
||||
"Artists": "الفنانين",
|
||||
|
@ -14,7 +14,7 @@
|
|||
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
||||
"Favorites": "المفضلة",
|
||||
"Folders": "المجلدات",
|
||||
"Genres": "الأنواع",
|
||||
"Genres": "التضنيفات",
|
||||
"HeaderAlbumArtists": "فناني الألبومات",
|
||||
"HeaderCameraUploads": "تحميلات الكاميرا",
|
||||
"HeaderContinueWatching": "استئناف",
|
||||
|
@ -50,7 +50,7 @@
|
|||
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
|
||||
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
|
||||
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
|
||||
"NotificationOptionInstallationFailed": "فشل في التثبيت",
|
||||
"NotificationOptionInstallationFailed": "فشل التثبيت",
|
||||
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
|
||||
"NotificationOptionPluginError": "فشل في البرنامج المضاف",
|
||||
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
|
||||
|
|
|
@ -62,13 +62,13 @@
|
|||
"NotificationOptionPluginInstalled": "প্লাগিন ইন্সটল করা হয়েছে",
|
||||
"NotificationOptionPluginError": "প্লাগিন ব্যর্থ",
|
||||
"NotificationOptionNewLibraryContent": "নতুন কন্টেন্ট যোগ করা হয়েছে",
|
||||
"NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ",
|
||||
"NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ হয়েছে",
|
||||
"NotificationOptionCameraImageUploaded": "ক্যামেরার ছবি আপলোড হয়েছে",
|
||||
"NotificationOptionAudioPlaybackStopped": "গান বাজা বন্ধ হয়েছে",
|
||||
"NotificationOptionAudioPlayback": "গান বাজা শুরু হয়েছে",
|
||||
"NotificationOptionApplicationUpdateInstalled": "এপ্লিকেশনের আপডেট ইনস্টল করা হয়েছে",
|
||||
"NotificationOptionApplicationUpdateAvailable": "এপ্লিকেশনের আপডেট রয়েছে",
|
||||
"NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী",
|
||||
"NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী।",
|
||||
"NameSeasonUnknown": "সিজন অজানা",
|
||||
"NameSeasonNumber": "সিজন {0}",
|
||||
"NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
|
||||
|
@ -100,5 +100,18 @@
|
|||
"TaskCleanCacheDescription": "সিস্টেমে আর প্রয়োজন নেই ক্যাশ, ফাইলগুলি মুছে ফেলুন।",
|
||||
"TaskCleanCache": "ক্লিন ক্যাশ ডিরেক্টরি",
|
||||
"TasksChannelsCategory": "ইন্টারনেট চ্যানেল",
|
||||
"TasksApplicationCategory": "আবেদন"
|
||||
"TasksApplicationCategory": "আবেদন",
|
||||
"TaskDownloadMissingSubtitlesDescription": "মেটাডেটা কনফিগারেশনের উপর ভিত্তি করে অনুপস্থিত সাবটাইটেলগুলির জন্য ইন্টারনেট অনুসন্ধান করে।",
|
||||
"TaskDownloadMissingSubtitles": "অনুপস্থিত সাবটাইটেলগুলি ডাউনলোড করুন",
|
||||
"TaskRefreshChannelsDescription": "ইন্টারনেট চ্যানেল তথ্য রিফ্রেশ করুন।",
|
||||
"TaskRefreshChannels": "চ্যানেল রিফ্রেশ করুন",
|
||||
"TaskCleanTranscodeDescription": "এক দিনেরও বেশি পুরানো ট্রান্সকোড ফাইলগুলি মুছে ফেলুন।",
|
||||
"TaskCleanTranscode": "ট্রান্সকোড ডিরেক্টরি ক্লিন করুন",
|
||||
"TaskUpdatePluginsDescription": "স্বয়ংক্রিয়ভাবে আপডেট কনফিগার করা প্লাগইনগুলির জন্য আপডেট ডাউনলোড এবং ইনস্টল করুন।",
|
||||
"TaskUpdatePlugins": "প্লাগইন আপডেট করুন",
|
||||
"TaskRefreshPeopleDescription": "আপনার মিডিয়া লাইব্রেরিতে অভিনেতা এবং পরিচালকদের জন্য মেটাডাটা আপডেট করুন।",
|
||||
"TaskRefreshPeople": "পিপল রিফ্রেশ করুন",
|
||||
"TaskCleanLogsDescription": "{0} দিনের বেশী পুরানো লগ ফাইলগুলি মুছে ফেলুন।",
|
||||
"TaskCleanLogs": "লগ ডিরেক্টরি ক্লিন করুন",
|
||||
"TaskRefreshLibraryDescription": "নতুন ফাইলের জন্য মিডিয়া লাইব্রেরি স্ক্যান এবং মেটাডাটা রিফ্রেশ করুন।"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"AppDeviceValues": "App: {0}, Gerät: {1}",
|
||||
"Application": "Anwendung",
|
||||
"Artists": "Interpreten",
|
||||
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert",
|
||||
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
|
||||
"Books": "Bücher",
|
||||
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
|
||||
"Channels": "Kanäle",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"HeaderContinueWatching": "Seguir viendo",
|
||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||
"HeaderFavoriteEpisodes": "Episodios favoritos",
|
||||
"HeaderFavoriteEpisodes": "Capítulos favoritos",
|
||||
"HeaderFavoriteShows": "Programas favoritos",
|
||||
"HeaderFavoriteSongs": "Canciones favoritas",
|
||||
"HeaderLiveTV": "TV en vivo",
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
||||
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
||||
"LabelIpAddressValue": "Dirección IP: {0}",
|
||||
"LabelRunningTimeValue": "Duración: {0}",
|
||||
"LabelRunningTimeValue": "Tiempo de reproducción: {0}",
|
||||
"Latest": "Recientes",
|
||||
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
|
||||
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"LabelRunningTimeValue": "Duración: {0}",
|
||||
"LabelRunningTimeValue": "Tiempo en ejecución: {0}",
|
||||
"ValueSpecialEpisodeName": "Especial - {0}",
|
||||
"Sync": "Sincronizar",
|
||||
"Songs": "Canciones",
|
||||
|
|
|
@ -109,9 +109,10 @@
|
|||
"TaskCleanLogs": "Nettoyer le répertoire des journaux",
|
||||
"TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour trouver de nouveaux fichiers et rafraîchit les métadonnées.",
|
||||
"TaskRefreshChapterImages": "Extraire les images de chapitre",
|
||||
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour les vidéos qui ont des chapitres",
|
||||
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour les vidéos qui ont des chapitres.",
|
||||
"TaskRefreshLibrary": "Analyser la bibliothèque de médias",
|
||||
"TaskCleanCache": "Nettoyer le répertoire des fichiers temporaires",
|
||||
"TasksApplicationCategory": "Application",
|
||||
"TaskCleanCacheDescription": "Supprime les fichiers temporaires qui ne sont plus nécessaire pour le système."
|
||||
"TaskCleanCacheDescription": "Supprime les fichiers temporaires qui ne sont plus nécessaire pour le système.",
|
||||
"TasksChannelsCategory": "Canaux Internet"
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue