mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-07 11:57:55 +02:00
commit
1a6ee3d48a
133 changed files with 1919 additions and 1495 deletions
|
@ -392,10 +392,27 @@ namespace Emby.Common.Implementations.IO
|
||||||
|
|
||||||
if (_supportsAsyncFileStreams && isAsync)
|
if (_supportsAsyncFileStreams && isAsync)
|
||||||
{
|
{
|
||||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
|
return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
|
return GetFileStream(path, mode, access, share, FileOpenOptions.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
|
||||||
|
{
|
||||||
|
if (_sharpCifsFileSystem.IsEnabledForPath(path))
|
||||||
|
{
|
||||||
|
return _sharpCifsFileSystem.GetFileStream(path, mode, access, share);
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultBufferSize = 4096;
|
||||||
|
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), defaultBufferSize, GetFileOptions(fileOpenOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileOptions GetFileOptions(FileOpenOptions mode)
|
||||||
|
{
|
||||||
|
var val = (int)mode;
|
||||||
|
return (FileOptions)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileMode GetFileMode(FileOpenMode mode)
|
private FileMode GetFileMode(FileOpenMode mode)
|
||||||
|
@ -501,6 +518,49 @@ namespace Emby.Common.Implementations.IO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAttributes(string path, bool isHidden, bool isReadOnly)
|
||||||
|
{
|
||||||
|
if (_sharpCifsFileSystem.IsEnabledForPath(path))
|
||||||
|
{
|
||||||
|
_sharpCifsFileSystem.SetAttributes(path, isHidden, isReadOnly);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = GetFileInfo(path);
|
||||||
|
|
||||||
|
if (!info.Exists)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.IsReadOnly == isReadOnly && info.IsHidden == isHidden)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributes = File.GetAttributes(path);
|
||||||
|
|
||||||
|
if (isReadOnly)
|
||||||
|
{
|
||||||
|
attributes = attributes | FileAttributes.ReadOnly;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHidden)
|
||||||
|
{
|
||||||
|
attributes = attributes | FileAttributes.Hidden;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.SetAttributes(path, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
||||||
{
|
{
|
||||||
return attributes & ~attributesToRemove;
|
return attributes & ~attributesToRemove;
|
||||||
|
@ -673,20 +733,7 @@ namespace Emby.Common.Implementations.IO
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileInfo = GetFileInfo(path);
|
SetAttributes(path, false, false);
|
||||||
|
|
||||||
if (fileInfo.Exists)
|
|
||||||
{
|
|
||||||
if (fileInfo.IsHidden)
|
|
||||||
{
|
|
||||||
SetHidden(path, false);
|
|
||||||
}
|
|
||||||
if (fileInfo.IsReadOnly)
|
|
||||||
{
|
|
||||||
SetReadOnly(path, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,11 @@ namespace Emby.Common.Implementations.IO
|
||||||
if (separator == '/')
|
if (separator == '/')
|
||||||
{
|
{
|
||||||
result = result.Replace('\\', '/');
|
result = result.Replace('\\', '/');
|
||||||
|
|
||||||
|
if (result.StartsWith("smb:/", StringComparison.OrdinalIgnoreCase) && !result.StartsWith("smb://", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result = result.Replace("smb:/", "smb://");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -161,23 +166,38 @@ namespace Emby.Common.Implementations.IO
|
||||||
public void SetHidden(string path, bool isHidden)
|
public void SetHidden(string path, bool isHidden)
|
||||||
{
|
{
|
||||||
var file = CreateSmbFile(path);
|
var file = CreateSmbFile(path);
|
||||||
|
SetHidden(file, isHidden);
|
||||||
var isCurrentlyHidden = file.IsHidden();
|
|
||||||
|
|
||||||
if (isCurrentlyHidden && !isHidden)
|
|
||||||
{
|
|
||||||
file.SetAttributes(file.GetAttributes() & ~SmbFile.AttrReadonly);
|
|
||||||
}
|
|
||||||
else if (!isCurrentlyHidden && isHidden)
|
|
||||||
{
|
|
||||||
file.SetAttributes(file.GetAttributes() | SmbFile.AttrReadonly);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetReadOnly(string path, bool isReadOnly)
|
public void SetReadOnly(string path, bool isReadOnly)
|
||||||
{
|
{
|
||||||
var file = CreateSmbFile(path);
|
var file = CreateSmbFile(path);
|
||||||
|
SetReadOnly(file, isReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttributes(string path, bool isHidden, bool isReadOnly)
|
||||||
|
{
|
||||||
|
var file = CreateSmbFile(path);
|
||||||
|
SetHidden(file, isHidden);
|
||||||
|
SetReadOnly(file, isReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetHidden(SmbFile file, bool isHidden)
|
||||||
|
{
|
||||||
|
var isCurrentlyHidden = file.IsHidden();
|
||||||
|
|
||||||
|
if (isCurrentlyHidden && !isHidden)
|
||||||
|
{
|
||||||
|
file.SetAttributes(file.GetAttributes() & ~SmbFile.AttrHidden);
|
||||||
|
}
|
||||||
|
else if (!isCurrentlyHidden && isHidden)
|
||||||
|
{
|
||||||
|
file.SetAttributes(file.GetAttributes() | SmbFile.AttrHidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetReadOnly(SmbFile file, bool isReadOnly)
|
||||||
|
{
|
||||||
var isCurrentlyReadOnly = !file.CanWrite();
|
var isCurrentlyReadOnly = !file.CanWrite();
|
||||||
|
|
||||||
if (isCurrentlyReadOnly && !isReadOnly)
|
if (isCurrentlyReadOnly && !isReadOnly)
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
"System.Net.Requests": "4.3.0",
|
"System.Net.Requests": "4.3.0",
|
||||||
"System.Xml.ReaderWriter": "4.3.0",
|
"System.Xml.ReaderWriter": "4.3.0",
|
||||||
"System.Xml.XmlSerializer": "4.3.0",
|
"System.Xml.XmlSerializer": "4.3.0",
|
||||||
"System.Net.Http": "4.3.0",
|
"System.Net.Http": "4.3.2",
|
||||||
"System.Net.Primitives": "4.3.0",
|
"System.Net.Primitives": "4.3.0",
|
||||||
"System.Net.Sockets": "4.3.0",
|
"System.Net.Sockets": "4.3.0",
|
||||||
"System.Net.NetworkInformation": "4.3.0",
|
"System.Net.NetworkInformation": "4.3.0",
|
||||||
|
|
|
@ -587,10 +587,7 @@ namespace Emby.Dlna
|
||||||
new DirectTvProfile(),
|
new DirectTvProfile(),
|
||||||
new DishHopperJoeyProfile(),
|
new DishHopperJoeyProfile(),
|
||||||
new DefaultProfile(),
|
new DefaultProfile(),
|
||||||
new PopcornHourProfile(),
|
new PopcornHourProfile()
|
||||||
new VlcProfile(),
|
|
||||||
new BubbleUpnpProfile(),
|
|
||||||
new KodiProfile(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
|
|
|
@ -79,13 +79,11 @@
|
||||||
<Compile Include="PlayTo\uParserObject.cs" />
|
<Compile Include="PlayTo\uParserObject.cs" />
|
||||||
<Compile Include="PlayTo\UpnpContainer.cs" />
|
<Compile Include="PlayTo\UpnpContainer.cs" />
|
||||||
<Compile Include="PlayTo\uPnpNamespaces.cs" />
|
<Compile Include="PlayTo\uPnpNamespaces.cs" />
|
||||||
<Compile Include="Profiles\BubbleUpnpProfile.cs" />
|
|
||||||
<Compile Include="Profiles\DefaultProfile.cs" />
|
<Compile Include="Profiles\DefaultProfile.cs" />
|
||||||
<Compile Include="Profiles\DenonAvrProfile.cs" />
|
<Compile Include="Profiles\DenonAvrProfile.cs" />
|
||||||
<Compile Include="Profiles\DirectTvProfile.cs" />
|
<Compile Include="Profiles\DirectTvProfile.cs" />
|
||||||
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
|
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
|
||||||
<Compile Include="Profiles\Foobar2000Profile.cs" />
|
<Compile Include="Profiles\Foobar2000Profile.cs" />
|
||||||
<Compile Include="Profiles\KodiProfile.cs" />
|
|
||||||
<Compile Include="Profiles\LgTvProfile.cs" />
|
<Compile Include="Profiles\LgTvProfile.cs" />
|
||||||
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
|
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
|
||||||
<Compile Include="Profiles\MediaMonkeyProfile.cs" />
|
<Compile Include="Profiles\MediaMonkeyProfile.cs" />
|
||||||
|
@ -105,7 +103,6 @@
|
||||||
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
|
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
|
||||||
<Compile Include="Profiles\SonyPs3Profile.cs" />
|
<Compile Include="Profiles\SonyPs3Profile.cs" />
|
||||||
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
||||||
<Compile Include="Profiles\VlcProfile.cs" />
|
|
||||||
<Compile Include="Profiles\WdtvLiveProfile.cs" />
|
<Compile Include="Profiles\WdtvLiveProfile.cs" />
|
||||||
<Compile Include="Profiles\Xbox360Profile.cs" />
|
<Compile Include="Profiles\Xbox360Profile.cs" />
|
||||||
<Compile Include="Profiles\XboxOneProfile.cs" />
|
<Compile Include="Profiles\XboxOneProfile.cs" />
|
||||||
|
@ -153,13 +150,11 @@
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" />
|
|
||||||
<EmbeddedResource Include="Profiles\Xml\Default.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Default.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
|
<EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\foobar2000.xml" />
|
<EmbeddedResource Include="Profiles\Xml\foobar2000.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Kodi.xml" />
|
|
||||||
<EmbeddedResource Include="Profiles\Xml\LG Smart TV.xml" />
|
<EmbeddedResource Include="Profiles\Xml\LG Smart TV.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Linksys DMA2100.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Linksys DMA2100.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
|
<EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
|
||||||
|
@ -178,7 +173,6 @@
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Vlc.xml" />
|
|
||||||
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
|
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Xbox 360.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Xbox 360.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
using MediaBrowser.Model.Dlna;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.Profiles
|
|
||||||
{
|
|
||||||
[XmlRoot("Profile")]
|
|
||||||
public class BubbleUpnpProfile : DefaultProfile
|
|
||||||
{
|
|
||||||
public BubbleUpnpProfile()
|
|
||||||
{
|
|
||||||
Name = "BubbleUPnp";
|
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
|
||||||
{
|
|
||||||
ModelName = "BubbleUPnp",
|
|
||||||
|
|
||||||
Headers = new[]
|
|
||||||
{
|
|
||||||
new HttpHeaderInfo {Name = "User-Agent", Value = "BubbleUPnp", Match = HeaderMatchType.Substring}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TranscodingProfiles = new[]
|
|
||||||
{
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "ts",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
AudioCodec = "aac",
|
|
||||||
VideoCodec = "h264"
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DirectPlayProfiles = new[]
|
|
||||||
{
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Photo,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseProfiles = new ResponseProfile[] { };
|
|
||||||
|
|
||||||
ContainerProfiles = new ContainerProfile[] { };
|
|
||||||
|
|
||||||
CodecProfiles = new CodecProfile[] { };
|
|
||||||
|
|
||||||
SubtitleProfiles = new[]
|
|
||||||
{
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ass",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ssa",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "smi",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "dvdsub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgs",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgssub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,8 +30,8 @@ namespace Emby.Dlna.Profiles
|
||||||
MaxIconWidth = 48;
|
MaxIconWidth = 48;
|
||||||
MaxIconHeight = 48;
|
MaxIconHeight = 48;
|
||||||
|
|
||||||
MaxStreamingBitrate = 30000000;
|
MaxStreamingBitrate = 40000000;
|
||||||
MaxStaticBitrate = 30000000;
|
MaxStaticBitrate = 40000000;
|
||||||
MusicStreamingTranscodingBitrate = 192000;
|
MusicStreamingTranscodingBitrate = 192000;
|
||||||
|
|
||||||
EnableAlbumArtInDidl = false;
|
EnableAlbumArtInDidl = false;
|
||||||
|
@ -64,15 +64,13 @@ namespace Emby.Dlna.Profiles
|
||||||
{
|
{
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov",
|
Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms",
|
||||||
VideoCodec = "h264",
|
|
||||||
AudioCodec = "aac,mp3,ac3",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
|
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "mp3,wma,aac,wav,flac",
|
Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac",
|
||||||
Type = DlnaProfileType.Audio
|
Type = DlnaProfileType.Audio
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -82,13 +80,61 @@ namespace Emby.Dlna.Profiles
|
||||||
new SubtitleProfile
|
new SubtitleProfile
|
||||||
{
|
{
|
||||||
Format = "srt",
|
Format = "srt",
|
||||||
Method = SubtitleDeliveryMethod.Embed
|
Method = SubtitleDeliveryMethod.External,
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "sub",
|
||||||
|
Method = SubtitleDeliveryMethod.External,
|
||||||
},
|
},
|
||||||
|
|
||||||
new SubtitleProfile
|
new SubtitleProfile
|
||||||
{
|
{
|
||||||
Format = "srt",
|
Format = "srt",
|
||||||
Method = SubtitleDeliveryMethod.External,
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "ass",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "ssa",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "smi",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "dvdsub",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "pgs",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "pgssub",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
|
},
|
||||||
|
|
||||||
|
new SubtitleProfile
|
||||||
|
{
|
||||||
|
Format = "sub",
|
||||||
|
Method = SubtitleDeliveryMethod.Embed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
using MediaBrowser.Model.Dlna;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.Profiles
|
|
||||||
{
|
|
||||||
[XmlRoot("Profile")]
|
|
||||||
public class KodiProfile : DefaultProfile
|
|
||||||
{
|
|
||||||
public KodiProfile()
|
|
||||||
{
|
|
||||||
Name = "Kodi";
|
|
||||||
|
|
||||||
MaxStreamingBitrate = 100000000;
|
|
||||||
MusicStreamingTranscodingBitrate = 1280000;
|
|
||||||
|
|
||||||
TimelineOffsetSeconds = 5;
|
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
|
||||||
{
|
|
||||||
ModelName = "Kodi",
|
|
||||||
|
|
||||||
Headers = new[]
|
|
||||||
{
|
|
||||||
new HttpHeaderInfo {Name = "User-Agent", Value = "Kodi", Match = HeaderMatchType.Substring}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TranscodingProfiles = new[]
|
|
||||||
{
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "ts",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
AudioCodec = "aac",
|
|
||||||
VideoCodec = "h264"
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DirectPlayProfiles = new[]
|
|
||||||
{
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Photo,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseProfiles = new ResponseProfile[] { };
|
|
||||||
|
|
||||||
ContainerProfiles = new ContainerProfile[] { };
|
|
||||||
|
|
||||||
CodecProfiles = new CodecProfile[] { };
|
|
||||||
|
|
||||||
SubtitleProfiles = new[]
|
|
||||||
{
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ass",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ssa",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "smi",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "dvdsub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgs",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgssub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
using MediaBrowser.Model.Dlna;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.Profiles
|
|
||||||
{
|
|
||||||
[XmlRoot("Profile")]
|
|
||||||
public class VlcProfile : DefaultProfile
|
|
||||||
{
|
|
||||||
public VlcProfile()
|
|
||||||
{
|
|
||||||
Name = "Vlc";
|
|
||||||
|
|
||||||
|
|
||||||
TimelineOffsetSeconds = 5;
|
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
|
||||||
{
|
|
||||||
ModelName = "Vlc",
|
|
||||||
|
|
||||||
Headers = new[]
|
|
||||||
{
|
|
||||||
new HttpHeaderInfo {Name = "User-Agent", Value = "vlc", Match = HeaderMatchType.Substring}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TranscodingProfiles = new[]
|
|
||||||
{
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "ts",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
AudioCodec = "aac",
|
|
||||||
VideoCodec = "h264"
|
|
||||||
},
|
|
||||||
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DirectPlayProfiles = new[]
|
|
||||||
{
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "",
|
|
||||||
Type = DlnaProfileType.Photo,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseProfiles = new ResponseProfile[] { };
|
|
||||||
|
|
||||||
ContainerProfiles = new ContainerProfile[] { };
|
|
||||||
|
|
||||||
CodecProfiles = new CodecProfile[] { };
|
|
||||||
|
|
||||||
SubtitleProfiles = new[]
|
|
||||||
{
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.External,
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ass",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "ssa",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "smi",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "dvdsub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgs",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "pgssub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "sub",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed,
|
|
||||||
DidlMode = "",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -16,8 +16,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
||||||
<XmlRootAttributes />
|
<XmlRootAttributes />
|
||||||
<DirectPlayProfiles>
|
<DirectPlayProfiles>
|
||||||
<DirectPlayProfile container="m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
|
<DirectPlayProfile container="m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms" type="Video" />
|
||||||
<DirectPlayProfile container="mp3,wma,aac,wav,flac" type="Audio" />
|
<DirectPlayProfile container="aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac" type="Audio" />
|
||||||
</DirectPlayProfiles>
|
</DirectPlayProfiles>
|
||||||
<TranscodingProfiles>
|
<TranscodingProfiles>
|
||||||
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
|
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
|
||||||
|
@ -45,7 +45,15 @@
|
||||||
</ResponseProfile>
|
</ResponseProfile>
|
||||||
</ResponseProfiles>
|
</ResponseProfiles>
|
||||||
<SubtitleProfiles>
|
<SubtitleProfiles>
|
||||||
<SubtitleProfile format="srt" method="Embed" />
|
|
||||||
<SubtitleProfile format="srt" method="External" />
|
<SubtitleProfile format="srt" method="External" />
|
||||||
|
<SubtitleProfile format="sub" method="External" />
|
||||||
|
<SubtitleProfile format="srt" method="Embed" />
|
||||||
|
<SubtitleProfile format="ass" method="Embed" />
|
||||||
|
<SubtitleProfile format="ssa" method="Embed" />
|
||||||
|
<SubtitleProfile format="smi" method="Embed" />
|
||||||
|
<SubtitleProfile format="dvdsub" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgs" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgssub" method="Embed" />
|
||||||
|
<SubtitleProfile format="sub" method="Embed" />
|
||||||
</SubtitleProfiles>
|
</SubtitleProfiles>
|
||||||
</Profile>
|
</Profile>
|
|
@ -21,8 +21,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
@ -45,7 +45,15 @@
|
||||||
<CodecProfiles />
|
<CodecProfiles />
|
||||||
<ResponseProfiles />
|
<ResponseProfiles />
|
||||||
<SubtitleProfiles>
|
<SubtitleProfiles>
|
||||||
<SubtitleProfile format="srt" method="Embed" />
|
|
||||||
<SubtitleProfile format="srt" method="External" />
|
<SubtitleProfile format="srt" method="External" />
|
||||||
|
<SubtitleProfile format="sub" method="External" />
|
||||||
|
<SubtitleProfile format="srt" method="Embed" />
|
||||||
|
<SubtitleProfile format="ass" method="Embed" />
|
||||||
|
<SubtitleProfile format="ssa" method="Embed" />
|
||||||
|
<SubtitleProfile format="smi" method="Embed" />
|
||||||
|
<SubtitleProfile format="dvdsub" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgs" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgssub" method="Embed" />
|
||||||
|
<SubtitleProfile format="sub" method="Embed" />
|
||||||
</SubtitleProfiles>
|
</SubtitleProfiles>
|
||||||
</Profile>
|
</Profile>
|
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
@ -51,7 +51,15 @@
|
||||||
<CodecProfiles />
|
<CodecProfiles />
|
||||||
<ResponseProfiles />
|
<ResponseProfiles />
|
||||||
<SubtitleProfiles>
|
<SubtitleProfiles>
|
||||||
<SubtitleProfile format="srt" method="Embed" />
|
|
||||||
<SubtitleProfile format="srt" method="External" />
|
<SubtitleProfile format="srt" method="External" />
|
||||||
|
<SubtitleProfile format="sub" method="External" />
|
||||||
|
<SubtitleProfile format="srt" method="Embed" />
|
||||||
|
<SubtitleProfile format="ass" method="Embed" />
|
||||||
|
<SubtitleProfile format="ssa" method="Embed" />
|
||||||
|
<SubtitleProfile format="smi" method="Embed" />
|
||||||
|
<SubtitleProfile format="dvdsub" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgs" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgssub" method="Embed" />
|
||||||
|
<SubtitleProfile format="sub" method="Embed" />
|
||||||
</SubtitleProfiles>
|
</SubtitleProfiles>
|
||||||
</Profile>
|
</Profile>
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||||
<MaxIconWidth>48</MaxIconWidth>
|
<MaxIconWidth>48</MaxIconWidth>
|
||||||
<MaxIconHeight>48</MaxIconHeight>
|
<MaxIconHeight>48</MaxIconHeight>
|
||||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||||
|
@ -51,7 +51,15 @@
|
||||||
<CodecProfiles />
|
<CodecProfiles />
|
||||||
<ResponseProfiles />
|
<ResponseProfiles />
|
||||||
<SubtitleProfiles>
|
<SubtitleProfiles>
|
||||||
<SubtitleProfile format="srt" method="Embed" />
|
|
||||||
<SubtitleProfile format="srt" method="External" />
|
<SubtitleProfile format="srt" method="External" />
|
||||||
|
<SubtitleProfile format="sub" method="External" />
|
||||||
|
<SubtitleProfile format="srt" method="Embed" />
|
||||||
|
<SubtitleProfile format="ass" method="Embed" />
|
||||||
|
<SubtitleProfile format="ssa" method="Embed" />
|
||||||
|
<SubtitleProfile format="smi" method="Embed" />
|
||||||
|
<SubtitleProfile format="dvdsub" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgs" method="Embed" />
|
||||||
|
<SubtitleProfile format="pgssub" method="Embed" />
|
||||||
|
<SubtitleProfile format="sub" method="Embed" />
|
||||||
</SubtitleProfiles>
|
</SubtitleProfiles>
|
||||||
</Profile>
|
</Profile>
|
|
@ -105,17 +105,6 @@ namespace Emby.Drawing.ImageMagick
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CropWhiteSpace(string inputPath, string outputPath)
|
|
||||||
{
|
|
||||||
CheckDisposed();
|
|
||||||
|
|
||||||
using (var wand = new MagickWand(inputPath))
|
|
||||||
{
|
|
||||||
wand.CurrentImage.TrimImage(10);
|
|
||||||
wand.SaveImage(outputPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageSize GetImageSize(string path)
|
public ImageSize GetImageSize(string path)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
@ -150,6 +139,11 @@ namespace Emby.Drawing.ImageMagick
|
||||||
{
|
{
|
||||||
using (var originalImage = new MagickWand(inputPath))
|
using (var originalImage = new MagickWand(inputPath))
|
||||||
{
|
{
|
||||||
|
if (options.CropWhiteSpace)
|
||||||
|
{
|
||||||
|
originalImage.CurrentImage.TrimImage(10);
|
||||||
|
}
|
||||||
|
|
||||||
ScaleImage(originalImage, width, height, options.Blur ?? 0);
|
ScaleImage(originalImage, width, height, options.Blur ?? 0);
|
||||||
|
|
||||||
if (autoOrient)
|
if (autoOrient)
|
||||||
|
|
|
@ -75,27 +75,24 @@ namespace Emby.Drawing.Net
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CropWhiteSpace(string inputPath, string outputPath)
|
private Image GetImage(string path, bool cropWhitespace)
|
||||||
{
|
{
|
||||||
using (var image = (Bitmap)Image.FromFile(inputPath))
|
if (cropWhitespace)
|
||||||
{
|
{
|
||||||
using (var croppedImage = image.CropWhitespace())
|
using (var originalImage = (Bitmap)Image.FromFile(path))
|
||||||
{
|
{
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
|
return originalImage.CropWhitespace();
|
||||||
|
|
||||||
using (var outputStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false))
|
|
||||||
{
|
|
||||||
croppedImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Image.FromFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EncodeImage(string inputPath, string cacheFilePath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
|
public void EncodeImage(string inputPath, string cacheFilePath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
|
||||||
{
|
{
|
||||||
var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0;
|
var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0;
|
||||||
|
|
||||||
using (var originalImage = Image.FromFile(inputPath))
|
using (var originalImage = GetImage(inputPath, options.CropWhiteSpace))
|
||||||
{
|
{
|
||||||
var newWidth = Convert.ToInt32(width);
|
var newWidth = Convert.ToInt32(width);
|
||||||
var newHeight = Convert.ToInt32(height);
|
var newHeight = Convert.ToInt32(height);
|
||||||
|
|
80
Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
Normal file
80
Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{2312DA6D-FF86-4597-9777-BCEEC32D96DD}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Emby.Drawing.Skia</RootNamespace>
|
||||||
|
<AssemblyName>Emby.Drawing.Skia</AssemblyName>
|
||||||
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||||
|
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||||
|
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||||
|
<Name>MediaBrowser.Common</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
|
||||||
|
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||||
|
<Name>MediaBrowser.Controller</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||||
|
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||||
|
<Name>MediaBrowser.Model</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\SharedVersion.cs">
|
||||||
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="PercentPlayedDrawer.cs" />
|
||||||
|
<Compile Include="PlayedIndicatorDrawer.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="SkiaEncoder.cs" />
|
||||||
|
<Compile Include="StripCollageBuilder.cs" />
|
||||||
|
<Compile Include="UnplayedCountIndicator.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="SkiaSharp, Version=1.57.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\SkiaSharp.1.57.1\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="fonts\robotoregular.ttf" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
31
Emby.Drawing.Skia/PercentPlayedDrawer.cs
Normal file
31
Emby.Drawing.Skia/PercentPlayedDrawer.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using SkiaSharp;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Emby.Drawing.Skia
|
||||||
|
{
|
||||||
|
public class PercentPlayedDrawer
|
||||||
|
{
|
||||||
|
private const int IndicatorHeight = 8;
|
||||||
|
|
||||||
|
public void Process(SKCanvas canvas, ImageSize imageSize, double percent)
|
||||||
|
{
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
var endX = imageSize.Width - 1;
|
||||||
|
var endY = imageSize.Height - 1;
|
||||||
|
|
||||||
|
paint.Color = SKColor.Parse("#99000000");
|
||||||
|
paint.Style = SKPaintStyle.Fill;
|
||||||
|
canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, (float)endX, (float)endY), paint);
|
||||||
|
|
||||||
|
double foregroundWidth = endX;
|
||||||
|
foregroundWidth *= percent;
|
||||||
|
foregroundWidth /= 100;
|
||||||
|
|
||||||
|
paint.Color = SKColor.Parse("#FF52B54B");
|
||||||
|
canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(Math.Round(foregroundWidth)), (float)endY), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
Normal file
120
Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using SkiaSharp;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
using MediaBrowser.Controller.IO;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Emby.Drawing.Skia
|
||||||
|
{
|
||||||
|
public class PlayedIndicatorDrawer
|
||||||
|
{
|
||||||
|
private const int FontSize = 42;
|
||||||
|
private const int OffsetFromTopRightCorner = 38;
|
||||||
|
|
||||||
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IHttpClient _iHttpClient;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_appPaths = appPaths;
|
||||||
|
_iHttpClient = iHttpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
|
||||||
|
{
|
||||||
|
var x = imageSize.Width - OffsetFromTopRightCorner;
|
||||||
|
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
paint.Color = SKColor.Parse("#CC52B54B");
|
||||||
|
paint.Style = SKPaintStyle.Fill;
|
||||||
|
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
paint.Color = new SKColor(255, 255, 255, 255);
|
||||||
|
paint.Style = SKPaintStyle.Fill;
|
||||||
|
paint.Typeface = SKTypeface.FromFile(await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf",
|
||||||
|
_appPaths, _iHttpClient, _fileSystem).ConfigureAwait(false));
|
||||||
|
paint.TextSize = FontSize;
|
||||||
|
paint.IsAntialias = true;
|
||||||
|
|
||||||
|
canvas.DrawText("a", (float)x-20, OffsetFromTopRightCorner + 12, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ExtractFont(string name, IApplicationPaths paths, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
|
||||||
|
|
||||||
|
if (fileSystem.FileExists(filePath))
|
||||||
|
{
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
|
||||||
|
var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
|
||||||
|
fileSystem.CreateDirectory(fileSystem.GetDirectoryName(tempPath));
|
||||||
|
|
||||||
|
using (var stream = typeof(PlayedIndicatorDrawer).GetTypeInfo().Assembly.GetManifestResourceStream(namespacePath))
|
||||||
|
{
|
||||||
|
using (var fileStream = fileSystem.GetFileStream(tempPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
||||||
|
{
|
||||||
|
stream.CopyTo(fileStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileSystem.CopyFile(tempPath, filePath, false);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
|
||||||
|
|
||||||
|
if (fileSystem.FileExists(filePath))
|
||||||
|
{
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempPath = await httpClient.GetTempFile(new HttpRequestOptions
|
||||||
|
{
|
||||||
|
Url = url,
|
||||||
|
Progress = new Progress<double>()
|
||||||
|
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileSystem.CopyFile(tempPath, filePath, false);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
Emby.Drawing.Skia/Properties/AssemblyInfo.cs
Normal file
25
Emby.Drawing.Skia/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Resources;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Emby.Drawing.Skia")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Emby.Drawing.Skia")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
387
Emby.Drawing.Skia/SkiaEncoder.cs
Normal file
387
Emby.Drawing.Skia/SkiaEncoder.cs
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
|
using MediaBrowser.Model.Logging;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Emby.Drawing.Skia
|
||||||
|
{
|
||||||
|
public class SkiaEncoder : IImageEncoder
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly Func<IHttpClient> _httpClientFactory;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public SkiaEncoder(ILogger logger, IApplicationPaths appPaths, Func<IHttpClient> httpClientFactory, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_appPaths = appPaths;
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
|
LogVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] SupportedInputFormats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Some common file name extensions for RAW picture files include: .cr2, .crw, .dng, .nef, .orf, .rw2, .pef, .arw, .sr2, .srf, and .tif.
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
"jpeg",
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"dng",
|
||||||
|
"webp",
|
||||||
|
"gif",
|
||||||
|
"bmp",
|
||||||
|
"ico",
|
||||||
|
"astc",
|
||||||
|
"ktx",
|
||||||
|
"pkm",
|
||||||
|
"wbmp"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageFormat[] SupportedOutputFormats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png, ImageFormat.Bmp };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogVersion()
|
||||||
|
{
|
||||||
|
_logger.Info("SkiaSharp version: " + GetVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetVersion()
|
||||||
|
{
|
||||||
|
using (var bitmap = new SKBitmap())
|
||||||
|
{
|
||||||
|
return typeof(SKBitmap).GetTypeInfo().Assembly.GetName().Version.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWhiteSpace(SKColor color)
|
||||||
|
{
|
||||||
|
return (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
|
||||||
|
{
|
||||||
|
switch (selectedFormat)
|
||||||
|
{
|
||||||
|
case ImageFormat.Bmp:
|
||||||
|
return SKEncodedImageFormat.Bmp;
|
||||||
|
case ImageFormat.Jpg:
|
||||||
|
return SKEncodedImageFormat.Jpeg;
|
||||||
|
case ImageFormat.Gif:
|
||||||
|
return SKEncodedImageFormat.Gif;
|
||||||
|
case ImageFormat.Webp:
|
||||||
|
return SKEncodedImageFormat.Webp;
|
||||||
|
default:
|
||||||
|
return SKEncodedImageFormat.Png;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAllWhiteRow(SKBitmap bmp, int row)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < bmp.Width; ++i)
|
||||||
|
{
|
||||||
|
if (!IsWhiteSpace(bmp.GetPixel(i, row)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAllWhiteColumn(SKBitmap bmp, int col)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < bmp.Height; ++i)
|
||||||
|
{
|
||||||
|
if (!IsWhiteSpace(bmp.GetPixel(col, i)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKBitmap CropWhiteSpace(SKBitmap bitmap)
|
||||||
|
{
|
||||||
|
var topmost = 0;
|
||||||
|
for (int row = 0; row < bitmap.Height; ++row)
|
||||||
|
{
|
||||||
|
if (IsAllWhiteRow(bitmap, row))
|
||||||
|
topmost = row;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bottommost = 0;
|
||||||
|
for (int row = bitmap.Height - 1; row >= 0; --row)
|
||||||
|
{
|
||||||
|
if (IsAllWhiteRow(bitmap, row))
|
||||||
|
bottommost = row;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int leftmost = 0, rightmost = 0;
|
||||||
|
for (int col = 0; col < bitmap.Width; ++col)
|
||||||
|
{
|
||||||
|
if (IsAllWhiteColumn(bitmap, col))
|
||||||
|
leftmost = col;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int col = bitmap.Width - 1; col >= 0; --col)
|
||||||
|
{
|
||||||
|
if (IsAllWhiteColumn(bitmap, col))
|
||||||
|
rightmost = col;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost);
|
||||||
|
|
||||||
|
using (var image = SKImage.FromBitmap(bitmap))
|
||||||
|
{
|
||||||
|
using (var subset = image.Subset(newRect))
|
||||||
|
{
|
||||||
|
return SKBitmap.FromImage(subset);
|
||||||
|
//using (var data = subset.Encode(StripCollageBuilder.GetEncodedFormat(outputPath), 90))
|
||||||
|
//{
|
||||||
|
// using (var fileStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
||||||
|
// {
|
||||||
|
// data.AsStream().CopyTo(fileStream);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageSize GetImageSize(string path)
|
||||||
|
{
|
||||||
|
using (var s = new SKFileStream(path))
|
||||||
|
{
|
||||||
|
using (var codec = SKCodec.Create(s))
|
||||||
|
{
|
||||||
|
var info = codec.Info;
|
||||||
|
|
||||||
|
return new ImageSize
|
||||||
|
{
|
||||||
|
Width = info.Width,
|
||||||
|
Height = info.Height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] TransparentImageTypes = new string[] { ".png", ".gif", ".webp" };
|
||||||
|
private SKBitmap Decode(string path)
|
||||||
|
{
|
||||||
|
var requiresTransparencyHack = TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
|
||||||
|
|
||||||
|
if (requiresTransparencyHack)
|
||||||
|
{
|
||||||
|
using (var stream = new SKFileStream(path))
|
||||||
|
{
|
||||||
|
var codec = SKCodec.Create(stream);
|
||||||
|
|
||||||
|
// create the bitmap
|
||||||
|
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height);
|
||||||
|
// decode
|
||||||
|
codec.GetPixels(bitmap.Info, bitmap.GetPixels());
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SKBitmap.Decode(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKBitmap GetBitmap(string path, bool cropWhitespace)
|
||||||
|
{
|
||||||
|
if (cropWhitespace)
|
||||||
|
{
|
||||||
|
using (var bitmap = Decode(path))
|
||||||
|
{
|
||||||
|
return CropWhiteSpace(bitmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decode(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(inputPath))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("inputPath");
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(inputPath))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("outputPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
var skiaOutputFormat = GetImageFormat(selectedOutputFormat);
|
||||||
|
|
||||||
|
var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor);
|
||||||
|
var hasForegroundColor = !string.IsNullOrWhiteSpace(options.ForegroundLayer);
|
||||||
|
var blur = options.Blur ?? 0;
|
||||||
|
var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
|
||||||
|
|
||||||
|
using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace))
|
||||||
|
{
|
||||||
|
using (var resizedBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType))
|
||||||
|
{
|
||||||
|
// scale image
|
||||||
|
var resizeMethod = SKBitmapResizeMethod.Lanczos3;
|
||||||
|
|
||||||
|
bitmap.Resize(resizedBitmap, resizeMethod);
|
||||||
|
|
||||||
|
// If all we're doing is resizing then we can stop now
|
||||||
|
if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
|
||||||
|
{
|
||||||
|
using (var outputStream = new SKFileWStream(outputPath))
|
||||||
|
{
|
||||||
|
resizedBitmap.Encode(outputStream, skiaOutputFormat, quality);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create bitmap to use for canvas drawing
|
||||||
|
using (var saveBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType))
|
||||||
|
{
|
||||||
|
// create canvas used to draw into bitmap
|
||||||
|
using (var canvas = new SKCanvas(saveBitmap))
|
||||||
|
{
|
||||||
|
// set background color if present
|
||||||
|
if (hasBackgroundColor)
|
||||||
|
{
|
||||||
|
canvas.Clear(SKColor.Parse(options.BackgroundColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add blur if option is present
|
||||||
|
if (blur > 0)
|
||||||
|
{
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
// create image from resized bitmap to apply blur
|
||||||
|
using (var filter = SKImageFilter.CreateBlur(blur, blur))
|
||||||
|
{
|
||||||
|
paint.ImageFilter = filter;
|
||||||
|
canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// draw resized bitmap onto canvas
|
||||||
|
canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If foreground layer present then draw
|
||||||
|
if (hasForegroundColor)
|
||||||
|
{
|
||||||
|
Double opacity;
|
||||||
|
if (!Double.TryParse(options.ForegroundLayer, out opacity)) opacity = .4;
|
||||||
|
|
||||||
|
canvas.DrawColor(new SKColor(0, 0, 0, (Byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasIndicator)
|
||||||
|
{
|
||||||
|
DrawIndicator(canvas, width, height, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var outputStream = new SKFileWStream(outputPath))
|
||||||
|
{
|
||||||
|
saveBitmap.Encode(outputStream, skiaOutputFormat, quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateImageCollage(ImageCollageOptions options)
|
||||||
|
{
|
||||||
|
double ratio = options.Width;
|
||||||
|
ratio /= options.Height;
|
||||||
|
|
||||||
|
if (ratio >= 1.4)
|
||||||
|
{
|
||||||
|
new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
|
||||||
|
}
|
||||||
|
else if (ratio >= .9)
|
||||||
|
{
|
||||||
|
new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// @todo create Poster collage capability
|
||||||
|
new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawIndicator(SKCanvas canvas, int imageWidth, int imageHeight, ImageProcessingOptions options)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var currentImageSize = new ImageSize(imageWidth, imageHeight);
|
||||||
|
|
||||||
|
if (options.AddPlayedIndicator)
|
||||||
|
{
|
||||||
|
var task = new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(canvas, currentImageSize);
|
||||||
|
Task.WaitAll(task);
|
||||||
|
}
|
||||||
|
else if (options.UnplayedCount.HasValue)
|
||||||
|
{
|
||||||
|
new UnplayedCountIndicator(_appPaths, _httpClientFactory(), _fileSystem).DrawUnplayedCountIndicator(canvas, currentImageSize, options.UnplayedCount.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.PercentPlayed > 0)
|
||||||
|
{
|
||||||
|
new PercentPlayedDrawer().Process(canvas, currentImageSize, options.PercentPlayed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error drawing indicator overlay", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return "Skia"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageCollageCreation
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageEncoding
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
190
Emby.Drawing.Skia/StripCollageBuilder.cs
Normal file
190
Emby.Drawing.Skia/StripCollageBuilder.cs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
using SkiaSharp;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
|
namespace Emby.Drawing.Skia
|
||||||
|
{
|
||||||
|
public class StripCollageBuilder
|
||||||
|
{
|
||||||
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_appPaths = appPaths;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SKEncodedImageFormat GetEncodedFormat(string outputPath)
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(outputPath).ToLower();
|
||||||
|
|
||||||
|
if (ext == ".jpg" || ext == ".jpeg")
|
||||||
|
return SKEncodedImageFormat.Jpeg;
|
||||||
|
|
||||||
|
if (ext == ".webp")
|
||||||
|
return SKEncodedImageFormat.Webp;
|
||||||
|
|
||||||
|
if (ext == ".gif")
|
||||||
|
return SKEncodedImageFormat.Gif;
|
||||||
|
|
||||||
|
if (ext == ".bmp")
|
||||||
|
return SKEncodedImageFormat.Bmp;
|
||||||
|
|
||||||
|
// default to png
|
||||||
|
return SKEncodedImageFormat.Png;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildPosterCollage(string[] paths, string outputPath, int width, int height)
|
||||||
|
{
|
||||||
|
// @todo
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
|
||||||
|
{
|
||||||
|
using (var bitmap = BuildSquareCollageBitmap(paths, width, height))
|
||||||
|
{
|
||||||
|
using (var outputStream = new SKFileWStream(outputPath))
|
||||||
|
{
|
||||||
|
bitmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
|
||||||
|
{
|
||||||
|
using (var bitmap = BuildThumbCollageBitmap(paths, width, height))
|
||||||
|
{
|
||||||
|
using (var outputStream = new SKFileWStream(outputPath))
|
||||||
|
{
|
||||||
|
bitmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height)
|
||||||
|
{
|
||||||
|
var bitmap = new SKBitmap(width, height);
|
||||||
|
|
||||||
|
using (var canvas = new SKCanvas(bitmap))
|
||||||
|
{
|
||||||
|
canvas.Clear(SKColors.Black);
|
||||||
|
|
||||||
|
// determine sizes for each image that will composited into the final image
|
||||||
|
var iSlice = Convert.ToInt32(width * 0.23475);
|
||||||
|
int iTrans = Convert.ToInt32(height * .25);
|
||||||
|
int iHeight = Convert.ToInt32(height * .70);
|
||||||
|
var horizontalImagePadding = Convert.ToInt32(width * 0.0125);
|
||||||
|
var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
|
||||||
|
int imageIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
using (var currentBitmap = SKBitmap.Decode(paths[imageIndex]))
|
||||||
|
{
|
||||||
|
// resize to the same aspect as the original
|
||||||
|
int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
|
||||||
|
using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
|
||||||
|
{
|
||||||
|
currentBitmap.Resize(resizeBitmap, SKBitmapResizeMethod.Lanczos3);
|
||||||
|
// determine how much to crop
|
||||||
|
int ix = (int)Math.Abs((iWidth - iSlice) / 2);
|
||||||
|
using (var image = SKImage.FromBitmap(resizeBitmap))
|
||||||
|
{
|
||||||
|
// crop image
|
||||||
|
using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
|
||||||
|
{
|
||||||
|
// draw image onto canvas
|
||||||
|
canvas.DrawImage(subset, (horizontalImagePadding * (i + 1)) + (iSlice * i), verticalSpacing);
|
||||||
|
|
||||||
|
using (var croppedBitmap = SKBitmap.FromImage(subset))
|
||||||
|
{
|
||||||
|
// create reflection of image below the drawn image
|
||||||
|
using (var reflectionBitmap = new SKBitmap(croppedBitmap.Width, croppedBitmap.Height / 2, croppedBitmap.ColorType, croppedBitmap.AlphaType))
|
||||||
|
{
|
||||||
|
// resize to half height
|
||||||
|
croppedBitmap.Resize(reflectionBitmap, SKBitmapResizeMethod.Lanczos3);
|
||||||
|
|
||||||
|
using (var flippedBitmap = new SKBitmap(reflectionBitmap.Width, reflectionBitmap.Height, reflectionBitmap.ColorType, reflectionBitmap.AlphaType))
|
||||||
|
{
|
||||||
|
using (var flippedCanvas = new SKCanvas(flippedBitmap))
|
||||||
|
{
|
||||||
|
// flip image vertically
|
||||||
|
var matrix = SKMatrix.MakeScale(1, -1);
|
||||||
|
matrix.SetScaleTranslate(1, -1, 0, flippedBitmap.Height);
|
||||||
|
flippedCanvas.SetMatrix(matrix);
|
||||||
|
flippedCanvas.DrawBitmap(reflectionBitmap, 0, 0);
|
||||||
|
flippedCanvas.ResetMatrix();
|
||||||
|
|
||||||
|
// create gradient to make image appear as a reflection
|
||||||
|
var remainingHeight = height - (iHeight + (2 * verticalSpacing));
|
||||||
|
flippedCanvas.ClipRect(SKRect.Create(reflectionBitmap.Width, remainingHeight));
|
||||||
|
using (var gradient = new SKPaint())
|
||||||
|
{
|
||||||
|
gradient.IsAntialias = true;
|
||||||
|
gradient.BlendMode = SKBlendMode.SrcOver;
|
||||||
|
gradient.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(0, remainingHeight), new[] { new SKColor(0, 0, 0, 128), new SKColor(0, 0, 0, 208), new SKColor(0, 0, 0, 240), new SKColor(0, 0, 0, 255) }, null, SKShaderTileMode.Clamp);
|
||||||
|
flippedCanvas.DrawPaint(gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally draw reflection onto canvas
|
||||||
|
canvas.DrawBitmap(flippedBitmap, (horizontalImagePadding * (i + 1)) + (iSlice * i), iHeight + (2 * verticalSpacing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageIndex++;
|
||||||
|
|
||||||
|
if (imageIndex >= paths.Length)
|
||||||
|
imageIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SKBitmap BuildSquareCollageBitmap(string[] paths, int width, int height)
|
||||||
|
{
|
||||||
|
var bitmap = new SKBitmap(width, height);
|
||||||
|
var imageIndex = 0;
|
||||||
|
var cellWidth = width / 2;
|
||||||
|
var cellHeight = height / 2;
|
||||||
|
|
||||||
|
using (var canvas = new SKCanvas(bitmap))
|
||||||
|
{
|
||||||
|
for (var x = 0; x < 2; x++)
|
||||||
|
{
|
||||||
|
for (var y = 0; y < 2; y++)
|
||||||
|
{
|
||||||
|
using (var currentBitmap = SKBitmap.Decode(paths[imageIndex]))
|
||||||
|
{
|
||||||
|
using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
|
||||||
|
{
|
||||||
|
// scale image
|
||||||
|
currentBitmap.Resize(resizedBitmap, SKBitmapResizeMethod.Lanczos3);
|
||||||
|
|
||||||
|
// draw this image into the strip at the next position
|
||||||
|
var xPos = x * cellWidth;
|
||||||
|
var yPos = y * cellHeight;
|
||||||
|
canvas.DrawBitmap(resizedBitmap, xPos, yPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imageIndex++;
|
||||||
|
|
||||||
|
if (imageIndex >= paths.Length)
|
||||||
|
imageIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
Emby.Drawing.Skia/UnplayedCountIndicator.cs
Normal file
68
Emby.Drawing.Skia/UnplayedCountIndicator.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using SkiaSharp;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
using MediaBrowser.Controller.IO;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
|
namespace Emby.Drawing.Skia
|
||||||
|
{
|
||||||
|
public class UnplayedCountIndicator
|
||||||
|
{
|
||||||
|
private const int OffsetFromTopRightCorner = 38;
|
||||||
|
|
||||||
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IHttpClient _iHttpClient;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public UnplayedCountIndicator(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_appPaths = appPaths;
|
||||||
|
_iHttpClient = iHttpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawUnplayedCountIndicator(SKCanvas canvas, ImageSize imageSize, int count)
|
||||||
|
{
|
||||||
|
var x = imageSize.Width - OffsetFromTopRightCorner;
|
||||||
|
var text = count.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
paint.Color = SKColor.Parse("#CC52B54B");
|
||||||
|
paint.Style = SKPaintStyle.Fill;
|
||||||
|
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
|
||||||
|
}
|
||||||
|
using (var paint = new SKPaint())
|
||||||
|
{
|
||||||
|
paint.Color = new SKColor(255, 255, 255, 255);
|
||||||
|
paint.Style = SKPaintStyle.Fill;
|
||||||
|
paint.Typeface = SKTypeface.FromFile(PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem));
|
||||||
|
paint.TextSize = 24;
|
||||||
|
paint.IsAntialias = true;
|
||||||
|
|
||||||
|
var y = OffsetFromTopRightCorner + 9;
|
||||||
|
|
||||||
|
if (text.Length == 1)
|
||||||
|
{
|
||||||
|
x -= 7;
|
||||||
|
}
|
||||||
|
if (text.Length == 2)
|
||||||
|
{
|
||||||
|
x -= 13;
|
||||||
|
}
|
||||||
|
else if (text.Length >= 3)
|
||||||
|
{
|
||||||
|
x -= 15;
|
||||||
|
y -= 2;
|
||||||
|
paint.TextSize = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.DrawText(text, (float)x, y, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
Emby.Drawing.Skia/packages.config
Normal file
4
Emby.Drawing.Skia/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="SkiaSharp" version="1.57.1" targetFramework="portable45-net45+win8" />
|
||||||
|
</packages>
|
|
@ -56,7 +56,7 @@ namespace Emby.Drawing
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
private readonly IServerApplicationPaths _appPaths;
|
private readonly IServerApplicationPaths _appPaths;
|
||||||
private readonly IImageEncoder _imageEncoder;
|
private IImageEncoder _imageEncoder;
|
||||||
private readonly Func<ILibraryManager> _libraryManager;
|
private readonly Func<ILibraryManager> _libraryManager;
|
||||||
|
|
||||||
public ImageProcessor(ILogger logger,
|
public ImageProcessor(ILogger logger,
|
||||||
|
@ -64,7 +64,7 @@ namespace Emby.Drawing
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
IImageEncoder imageEncoder,
|
IImageEncoder imageEncoder,
|
||||||
int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
|
Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
@ -103,6 +103,20 @@ namespace Emby.Drawing
|
||||||
_cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
|
_cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IImageEncoder ImageEncoder
|
||||||
|
{
|
||||||
|
get { return _imageEncoder; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("value");
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageEncoder = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string[] SupportedInputFormats
|
public string[] SupportedInputFormats
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -136,14 +150,6 @@ namespace Emby.Drawing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CroppedWhitespaceImageCachePath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Path.Combine(_appPaths.ImageCachePath, "cropped-images");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddParts(IEnumerable<IImageEnhancer> enhancers)
|
public void AddParts(IEnumerable<IImageEnhancer> enhancers)
|
||||||
{
|
{
|
||||||
ImageEnhancers = enhancers.ToArray();
|
ImageEnhancers = enhancers.ToArray();
|
||||||
|
@ -186,14 +192,6 @@ namespace Emby.Drawing
|
||||||
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.CropWhiteSpace && _imageEncoder.SupportsImageEncoding)
|
|
||||||
{
|
|
||||||
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
|
||||||
|
|
||||||
originalImagePath = tuple.Item1;
|
|
||||||
dateModified = tuple.Item2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.Enhancers.Count > 0)
|
if (options.Enhancers.Count > 0)
|
||||||
{
|
{
|
||||||
var tuple = await GetEnhancedImage(new ItemImageInfo
|
var tuple = await GetEnhancedImage(new ItemImageInfo
|
||||||
|
@ -214,7 +212,7 @@ namespace Emby.Drawing
|
||||||
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageSize? originalImageSize;
|
ImageSize? originalImageSize = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
originalImageSize = GetImageSize(originalImagePath, dateModified, true);
|
originalImageSize = GetImageSize(originalImagePath, dateModified, true);
|
||||||
|
@ -241,8 +239,8 @@ namespace Emby.Drawing
|
||||||
|
|
||||||
if (!_fileSystem.FileExists(cacheFilePath))
|
if (!_fileSystem.FileExists(cacheFilePath))
|
||||||
{
|
{
|
||||||
var newWidth = Convert.ToInt32(newSize.Width);
|
var newWidth = Convert.ToInt32(Math.Round(newSize.Width));
|
||||||
var newHeight = Convert.ToInt32(newSize.Height);
|
var newHeight = Convert.ToInt32(Math.Round(newSize.Height));
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
|
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
|
||||||
var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
|
var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
|
||||||
|
@ -349,22 +347,22 @@ namespace Emby.Drawing
|
||||||
return new ImageSize(options.Width.Value, options.Height.Value);
|
return new ImageSize(options.Width.Value, options.Height.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
var aspect = GetEstimatedAspectRatio(options.Image.Type);
|
var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item);
|
||||||
|
|
||||||
var width = options.Width ?? options.MaxWidth;
|
var width = options.Width ?? options.MaxWidth;
|
||||||
|
|
||||||
if (width.HasValue)
|
if (width.HasValue)
|
||||||
{
|
{
|
||||||
var heightValue = aspect / width.Value;
|
var heightValue = width.Value / aspect;
|
||||||
return new ImageSize(width.Value, Convert.ToInt32(heightValue));
|
return new ImageSize(width.Value, heightValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
var height = options.Height ?? options.MaxHeight ?? 200;
|
var height = options.Height ?? options.MaxHeight ?? 200;
|
||||||
var widthValue = aspect * height;
|
var widthValue = aspect * height;
|
||||||
return new ImageSize(Convert.ToInt32(widthValue), height);
|
return new ImageSize(widthValue, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GetEstimatedAspectRatio(ImageType type)
|
private double GetEstimatedAspectRatio(ImageType type, IHasImages item)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -384,7 +382,7 @@ namespace Emby.Drawing
|
||||||
case ImageType.Logo:
|
case ImageType.Logo:
|
||||||
return 2.58;
|
return 2.58;
|
||||||
case ImageType.Primary:
|
case ImageType.Primary:
|
||||||
return .667;
|
return item.GetDefaultPrimaryImageAspectRatio() ?? .667;
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -400,46 +398,6 @@ namespace Emby.Drawing
|
||||||
return requestedFormat;
|
return requestedFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crops whitespace from an image, caches the result, and returns the cached path
|
|
||||||
/// </summary>
|
|
||||||
private async Task<Tuple<string, DateTime>> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified)
|
|
||||||
{
|
|
||||||
var name = originalImagePath;
|
|
||||||
name += "datemodified=" + dateModified.Ticks;
|
|
||||||
|
|
||||||
var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath));
|
|
||||||
|
|
||||||
// Check again in case of contention
|
|
||||||
if (_fileSystem.FileExists(croppedImagePath))
|
|
||||||
{
|
|
||||||
return GetResult(croppedImagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(croppedImagePath));
|
|
||||||
var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(croppedImagePath));
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath));
|
|
||||||
|
|
||||||
_imageEncoder.CropWhiteSpace(originalImagePath, tmpPath);
|
|
||||||
CopyFile(tmpPath, croppedImagePath);
|
|
||||||
return GetResult(tmpPath);
|
|
||||||
}
|
|
||||||
catch (NotImplementedException)
|
|
||||||
{
|
|
||||||
// No need to spam the log with an error message
|
|
||||||
return new Tuple<string, DateTime>(originalImagePath, dateModified);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
|
|
||||||
_logger.ErrorException("Error cropping image {0}", ex, originalImagePath);
|
|
||||||
|
|
||||||
return new Tuple<string, DateTime>(originalImagePath, dateModified);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tuple<string, DateTime> GetResult(string path)
|
private Tuple<string, DateTime> GetResult(string path)
|
||||||
{
|
{
|
||||||
return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
|
return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
|
||||||
|
@ -555,26 +513,39 @@ namespace Emby.Drawing
|
||||||
/// <returns>ImageSize.</returns>
|
/// <returns>ImageSize.</returns>
|
||||||
private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
|
private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
|
||||||
{
|
{
|
||||||
|
// Can't use taglib because it keeps a lock on the file
|
||||||
|
//try
|
||||||
|
//{
|
||||||
|
// using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), _fileSystem.OpenRead(path), null)))
|
||||||
|
// {
|
||||||
|
// var image = file as TagLib.Image.File;
|
||||||
|
|
||||||
|
// var properties = image.Properties;
|
||||||
|
|
||||||
|
// return new ImageSize
|
||||||
|
// {
|
||||||
|
// Height = properties.PhotoHeight,
|
||||||
|
// Width = properties.PhotoWidth
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//catch
|
||||||
|
//{
|
||||||
|
//}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), _fileSystem.OpenRead(path), null)))
|
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||||
{
|
|
||||||
var image = file as TagLib.Image.File;
|
|
||||||
|
|
||||||
var properties = image.Properties;
|
|
||||||
|
|
||||||
return new ImageSize
|
|
||||||
{
|
|
||||||
Height = properties.PhotoHeight,
|
|
||||||
Width = properties.PhotoWidth
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
if (allowSlowMethod)
|
||||||
|
{
|
||||||
|
return _imageEncoder.GetImageSize(path);
|
||||||
|
}
|
||||||
|
|
||||||
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ITimer _saveImageSizeTimer;
|
private readonly ITimer _saveImageSizeTimer;
|
||||||
|
|
|
@ -57,6 +57,11 @@ namespace Emby.Drawing
|
||||||
get { return false; }
|
get { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageSize GetImageSize(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ namespace Emby.Server.Core
|
||||||
/// <value>The HTTP server.</value>
|
/// <value>The HTTP server.</value>
|
||||||
private IHttpServer HttpServer { get; set; }
|
private IHttpServer HttpServer { get; set; }
|
||||||
private IDtoService DtoService { get; set; }
|
private IDtoService DtoService { get; set; }
|
||||||
private IImageProcessor ImageProcessor { get; set; }
|
public IImageProcessor ImageProcessor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the media encoder.
|
/// Gets or sets the media encoder.
|
||||||
|
@ -761,7 +761,10 @@ namespace Emby.Server.Core
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
X509Certificate2 localCert = new X509Certificate2(certificateLocation, info.Password);
|
// Don't use an empty string password
|
||||||
|
var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password;
|
||||||
|
|
||||||
|
X509Certificate2 localCert = new X509Certificate2(certificateLocation, password);
|
||||||
//localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
|
//localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
|
||||||
if (!localCert.HasPrivateKey)
|
if (!localCert.HasPrivateKey)
|
||||||
{
|
{
|
||||||
|
@ -780,14 +783,7 @@ namespace Emby.Server.Core
|
||||||
|
|
||||||
private IImageProcessor GetImageProcessor()
|
private IImageProcessor GetImageProcessor()
|
||||||
{
|
{
|
||||||
var maxConcurrentImageProcesses = Math.Max(Environment.ProcessorCount, 4);
|
return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory);
|
||||||
|
|
||||||
if (StartupOptions.ContainsOption("-imagethreads"))
|
|
||||||
{
|
|
||||||
int.TryParse(StartupOptions.GetOption("-imagethreads"), NumberStyles.Any, CultureInfo.InvariantCulture, out maxConcurrentImageProcesses);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, maxConcurrentImageProcesses, () => LibraryManager, TimerFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
|
protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
|
||||||
|
@ -1132,7 +1128,8 @@ namespace Emby.Server.Core
|
||||||
// Custom cert
|
// Custom cert
|
||||||
return new CertificateInfo
|
return new CertificateInfo
|
||||||
{
|
{
|
||||||
Path = ServerConfigurationManager.Configuration.CertificatePath
|
Path = ServerConfigurationManager.Configuration.CertificatePath,
|
||||||
|
Password = ServerConfigurationManager.Configuration.CertificatePassword
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -537,7 +537,7 @@ namespace Emby.Server.Core.IO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo);
|
var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo, LibraryManager);
|
||||||
newRefresher.Completed += NewRefresher_Completed;
|
newRefresher.Completed += NewRefresher_Completed;
|
||||||
_activeRefreshers.Add(newRefresher);
|
_activeRefreshers.Add(newRefresher);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
"System.AppDomain": "2.0.11",
|
"System.AppDomain": "2.0.11",
|
||||||
"System.Globalization.Extensions": "4.3.0",
|
"System.Globalization.Extensions": "4.3.0",
|
||||||
"System.IO.FileSystem.Watcher": "4.3.0",
|
"System.IO.FileSystem.Watcher": "4.3.0",
|
||||||
"System.Net.Security": "4.3.0",
|
"System.Net.Security": "4.3.1",
|
||||||
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
"System.Runtime.Extensions": "4.3.0",
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
"MediaBrowser.Model": {
|
"MediaBrowser.Model": {
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
|
using (var statement = db.PrepareStatement("replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@Id", entry.Id.ToGuidParamValue());
|
statement.TryBind("@Id", entry.Id.ToGuidBlob());
|
||||||
statement.TryBind("@Name", entry.Name);
|
statement.TryBind("@Name", entry.Name);
|
||||||
|
|
||||||
statement.TryBind("@Overview", entry.Overview);
|
statement.TryBind("@Overview", entry.Overview);
|
||||||
|
@ -168,7 +168,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
|
|
||||||
var info = new ActivityLogEntry
|
var info = new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Id = reader[index].ReadGuid().ToString("N")
|
Id = reader[index].ReadGuidFromBlob().ToString("N")
|
||||||
};
|
};
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
|
|
@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
Logger.Info("Saving system configuration");
|
Logger.Info("Saving system configuration");
|
||||||
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
||||||
|
|
||||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_configurationSyncLock)
|
lock (_configurationSyncLock)
|
||||||
{
|
{
|
||||||
|
@ -293,7 +293,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||||
|
|
||||||
var path = GetConfigurationFile(key);
|
var path = GetConfigurationFile(key);
|
||||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_configurationSyncLock)
|
lock (_configurationSyncLock)
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
// If the file didn't exist before, or if something has changed, re-save
|
// If the file didn't exist before, or if something has changed, re-save
|
||||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
||||||
{
|
{
|
||||||
fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
fileSystem.CreateDirectory(fileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
// Save it after load in case we got new items
|
// Save it after load in case we got new items
|
||||||
fileSystem.WriteAllBytes(path, newBytes);
|
fileSystem.WriteAllBytes(path, newBytes);
|
||||||
|
|
|
@ -106,8 +106,8 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
|
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@id", displayPreferences.Id.ToGuidParamValue());
|
statement.TryBind("@id", displayPreferences.Id.ToGuidBlob());
|
||||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||||
statement.TryBind("@client", client);
|
statement.TryBind("@client", client);
|
||||||
statement.TryBind("@data", serialized);
|
statement.TryBind("@data", serialized);
|
||||||
|
|
||||||
|
@ -170,8 +170,8 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
|
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@id", guidId.ToGuidParamValue());
|
statement.TryBind("@id", guidId.ToGuidBlob());
|
||||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||||
statement.TryBind("@client", client);
|
statement.TryBind("@client", client);
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
|
@ -204,7 +204,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
|
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,17 +26,17 @@ namespace Emby.Server.Implementations.Data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] ToGuidParamValue(this string str)
|
public static byte[] ToGuidBlob(this string str)
|
||||||
{
|
{
|
||||||
return ToGuidParamValue(new Guid(str));
|
return ToGuidBlob(new Guid(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] ToGuidParamValue(this Guid guid)
|
public static byte[] ToGuidBlob(this Guid guid)
|
||||||
{
|
{
|
||||||
return guid.ToByteArray();
|
return guid.ToByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Guid ReadGuid(this IResultSetValue result)
|
public static Guid ReadGuidFromBlob(this IResultSetValue result)
|
||||||
{
|
{
|
||||||
return new Guid(result.ToBlob());
|
return new Guid(result.ToBlob());
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
|
public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
|
||||||
{
|
{
|
||||||
return result[index].ReadGuid();
|
return result[index].ReadGuidFromBlob();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckName(string name)
|
private static void CheckName(string name)
|
||||||
|
@ -262,7 +262,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
IBindParameter bindParam;
|
IBindParameter bindParam;
|
||||||
if (statement.BindParameters.TryGetValue(name, out bindParam))
|
if (statement.BindParameters.TryGetValue(name, out bindParam))
|
||||||
{
|
{
|
||||||
bindParam.Bind(value.ToGuidParamValue());
|
bindParam.Bind(value.ToGuidBlob());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
using (var statement = db.PrepareStatement(commandText))
|
using (var statement = db.PrepareStatement(commandText))
|
||||||
{
|
{
|
||||||
statement.TryBind("@ResultId", result.Id.ToGuidParamValue());
|
statement.TryBind("@ResultId", result.Id.ToGuidBlob());
|
||||||
statement.TryBind("@OriginalPath", result.OriginalPath);
|
statement.TryBind("@OriginalPath", result.OriginalPath);
|
||||||
|
|
||||||
statement.TryBind("@TargetPath", result.TargetPath);
|
statement.TryBind("@TargetPath", result.TargetPath);
|
||||||
|
@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
|
using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@ResultId", id.ToGuidParamValue());
|
statement.TryBind("@ResultId", id.ToGuidBlob());
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
}, TransactionMode);
|
}, TransactionMode);
|
||||||
|
@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = connection.PrepareStatement("select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@ResultId"))
|
using (var statement = connection.PrepareStatement("select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@ResultId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@ResultId", id.ToGuidParamValue());
|
statement.TryBind("@ResultId", id.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
|
@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
var result = new FileOrganizationResult
|
var result = new FileOrganizationResult
|
||||||
{
|
{
|
||||||
Id = reader[0].ReadGuid().ToString("N")
|
Id = reader[0].ReadGuidFromBlob().ToString("N")
|
||||||
};
|
};
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
|
|
@ -2128,7 +2128,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
connection.RunInTransaction(db =>
|
connection.RunInTransaction(db =>
|
||||||
{
|
{
|
||||||
// First delete chapters
|
// First delete chapters
|
||||||
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidParamValue());
|
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidBlob());
|
||||||
|
|
||||||
using (var saveChapterStatement = PrepareStatement(db, "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
|
using (var saveChapterStatement = PrepareStatement(db, "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
|
||||||
{
|
{
|
||||||
|
@ -2139,7 +2139,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
saveChapterStatement.Reset();
|
saveChapterStatement.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveChapterStatement.TryBind("@ItemId", id.ToGuidParamValue());
|
saveChapterStatement.TryBind("@ItemId", id.ToGuidBlob());
|
||||||
saveChapterStatement.TryBind("@ChapterIndex", index);
|
saveChapterStatement.TryBind("@ChapterIndex", index);
|
||||||
saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
|
saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
|
||||||
saveChapterStatement.TryBind("@Name", chapter.Name);
|
saveChapterStatement.TryBind("@Name", chapter.Name);
|
||||||
|
@ -2919,7 +2919,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
list.Add(row[0].ReadGuid());
|
list.Add(row[0].ReadGuidFromBlob());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3113,7 +3113,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
list.Add(row[0].ReadGuid());
|
list.Add(row[0].ReadGuidFromBlob());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3643,7 +3643,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("(select Name from TypedBaseItems where guid=" + paramName + ") in (select Name from People where ItemId=Guid)");
|
clauses.Add("(select Name from TypedBaseItems where guid=" + paramName + ") in (select Name from People where ItemId=Guid)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, personId.ToGuidParamValue());
|
statement.TryBind(paramName, personId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -3843,7 +3843,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
|
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, artistId.ToGuidParamValue());
|
statement.TryBind(paramName, artistId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -3862,7 +3862,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
|
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, albumId.ToGuidParamValue());
|
statement.TryBind(paramName, albumId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -3881,7 +3881,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
|
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, artistId.ToGuidParamValue());
|
statement.TryBind(paramName, artistId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -3900,7 +3900,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
|
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, genreId.ToGuidParamValue());
|
statement.TryBind(paramName, genreId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -3953,7 +3953,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
|
clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind(paramName, studioId.ToGuidParamValue());
|
statement.TryBind(paramName, studioId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -4521,22 +4521,22 @@ namespace Emby.Server.Implementations.Data
|
||||||
connection.RunInTransaction(db =>
|
connection.RunInTransaction(db =>
|
||||||
{
|
{
|
||||||
// Delete people
|
// Delete people
|
||||||
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
// Delete chapters
|
// Delete chapters
|
||||||
ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
// Delete media streams
|
// Delete media streams
|
||||||
ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
// Delete ancestors
|
// Delete ancestors
|
||||||
ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
// Delete item values
|
// Delete item values
|
||||||
ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
// Delete the item
|
// Delete the item
|
||||||
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidParamValue());
|
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidBlob());
|
||||||
}, TransactionMode);
|
}, TransactionMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4643,7 +4643,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
whereClauses.Add("ItemId=@ItemId");
|
whereClauses.Add("ItemId=@ItemId");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
|
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query.AppearsInItemId != Guid.Empty)
|
if (query.AppearsInItemId != Guid.Empty)
|
||||||
|
@ -4651,7 +4651,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
|
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
{
|
{
|
||||||
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
|
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidBlob());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
|
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
|
||||||
|
@ -4730,14 +4730,14 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
// First delete
|
// First delete
|
||||||
deleteAncestorsStatement.Reset();
|
deleteAncestorsStatement.Reset();
|
||||||
deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||||
deleteAncestorsStatement.MoveNext();
|
deleteAncestorsStatement.MoveNext();
|
||||||
|
|
||||||
foreach (var ancestorId in ancestorIds)
|
foreach (var ancestorId in ancestorIds)
|
||||||
{
|
{
|
||||||
updateAncestorsStatement.Reset();
|
updateAncestorsStatement.Reset();
|
||||||
updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||||
updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidParamValue());
|
updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidBlob());
|
||||||
updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
|
updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
|
||||||
updateAncestorsStatement.MoveNext();
|
updateAncestorsStatement.MoveNext();
|
||||||
}
|
}
|
||||||
|
@ -5198,7 +5198,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
// First delete
|
// First delete
|
||||||
db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidParamValue());
|
db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidBlob());
|
||||||
|
|
||||||
using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
|
using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
|
||||||
{
|
{
|
||||||
|
@ -5214,7 +5214,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
statement.Reset();
|
statement.Reset();
|
||||||
|
|
||||||
statement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
statement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||||
statement.TryBind("@Type", pair.Item1);
|
statement.TryBind("@Type", pair.Item1);
|
||||||
statement.TryBind("@Value", itemValue);
|
statement.TryBind("@Value", itemValue);
|
||||||
|
|
||||||
|
@ -5252,7 +5252,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
// First delete
|
// First delete
|
||||||
// "delete from People where ItemId=?"
|
// "delete from People where ItemId=?"
|
||||||
connection.Execute("delete from People where ItemId=?", itemId.ToGuidParamValue());
|
connection.Execute("delete from People where ItemId=?", itemId.ToGuidBlob());
|
||||||
|
|
||||||
var listIndex = 0;
|
var listIndex = 0;
|
||||||
|
|
||||||
|
@ -5266,7 +5266,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
statement.Reset();
|
statement.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
statement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||||
statement.TryBind("@Name", person.Name);
|
statement.TryBind("@Name", person.Name);
|
||||||
statement.TryBind("@Role", person.Role);
|
statement.TryBind("@Role", person.Role);
|
||||||
statement.TryBind("@PersonType", person.Type);
|
statement.TryBind("@PersonType", person.Type);
|
||||||
|
@ -5339,7 +5339,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
using (var statement = PrepareStatementSafe(connection, cmdText))
|
using (var statement = PrepareStatementSafe(connection, cmdText))
|
||||||
{
|
{
|
||||||
statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
|
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
|
||||||
|
|
||||||
if (query.Type.HasValue)
|
if (query.Type.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -5383,7 +5383,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
using (var connection = CreateConnection())
|
using (var connection = CreateConnection())
|
||||||
{
|
{
|
||||||
// First delete chapters
|
// First delete chapters
|
||||||
connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidParamValue());
|
connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidBlob());
|
||||||
|
|
||||||
using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
|
using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
|
||||||
string.Join(",", _mediaStreamSaveColumns),
|
string.Join(",", _mediaStreamSaveColumns),
|
||||||
|
@ -5393,7 +5393,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
var paramList = new List<object>();
|
var paramList = new List<object>();
|
||||||
|
|
||||||
paramList.Add(id.ToGuidParamValue());
|
paramList.Add(id.ToGuidBlob());
|
||||||
paramList.Add(stream.Index);
|
paramList.Add(stream.Index);
|
||||||
paramList.Add(stream.Type.ToString());
|
paramList.Add(stream.Type.ToString());
|
||||||
paramList.Add(stream.Codec);
|
paramList.Add(stream.Codec);
|
||||||
|
|
|
@ -213,7 +213,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
|
using (var statement = db.PrepareStatement("replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||||
statement.TryBind("@key", key);
|
statement.TryBind("@key", key);
|
||||||
|
|
||||||
if (userData.Rating.HasValue)
|
if (userData.Rating.HasValue)
|
||||||
|
@ -311,7 +311,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key =@Key and userId=@UserId"))
|
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key =@Key and userId=@UserId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||||
statement.TryBind("@Key", key);
|
statement.TryBind("@Key", key);
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
|
@ -364,7 +364,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@UserId"))
|
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@UserId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
|
@ -386,7 +386,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
var userData = new UserItemData();
|
var userData = new UserItemData();
|
||||||
|
|
||||||
userData.Key = reader[0].ToString();
|
userData.Key = reader[0].ToString();
|
||||||
userData.UserId = reader[1].ReadGuid();
|
userData.UserId = reader[1].ReadGuidFromBlob();
|
||||||
|
|
||||||
if (reader[2].SQLiteType != SQLiteType.Null)
|
if (reader[2].SQLiteType != SQLiteType.Null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("replace into users (guid, data) values (@guid, @data)"))
|
using (var statement = db.PrepareStatement("replace into users (guid, data) values (@guid, @data)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@guid", user.Id.ToGuidParamValue());
|
statement.TryBind("@guid", user.Id.ToGuidBlob());
|
||||||
statement.TryBind("@data", serialized);
|
statement.TryBind("@data", serialized);
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
foreach (var row in connection.Query("select guid,data from users"))
|
foreach (var row in connection.Query("select guid,data from users"))
|
||||||
{
|
{
|
||||||
var id = row[0].ReadGuid();
|
var id = row[0].ReadGuidFromBlob();
|
||||||
|
|
||||||
using (var stream = _memoryStreamProvider.CreateNew(row[1].ToBlob()))
|
using (var stream = _memoryStreamProvider.CreateNew(row[1].ToBlob()))
|
||||||
{
|
{
|
||||||
|
@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("delete from users where guid=@id"))
|
using (var statement = db.PrepareStatement("delete from users where guid=@id"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@id", user.Id.ToGuidParamValue());
|
statement.TryBind("@id", user.Id.ToGuidBlob());
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
}, TransactionMode);
|
}, TransactionMode);
|
||||||
|
|
|
@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.Devices
|
||||||
|
|
||||||
_libraryMonitor.ReportFileSystemChangeBeginning(path);
|
_libraryMonitor.ReportFileSystemChangeBeginning(path);
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Devices
|
||||||
public Task SaveDevice(DeviceInfo device)
|
public Task SaveDevice(DeviceInfo device)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(GetDevicePath(device.Id), "device.json");
|
var path = Path.Combine(GetDevicePath(device.Id), "device.json");
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_syncLock)
|
lock (_syncLock)
|
||||||
{
|
{
|
||||||
|
@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.Devices
|
||||||
public void AddCameraUpload(string deviceId, LocalFileInfo file)
|
public void AddCameraUpload(string deviceId, LocalFileInfo file)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
|
var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_syncLock)
|
lock (_syncLock)
|
||||||
{
|
{
|
||||||
|
|
|
@ -308,7 +308,7 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\SQLitePCLRaw.core.1.1.2\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
|
<HintPath>..\packages\SQLitePCLRaw.core.1.1.5\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.FFMpeg
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info = existingVersion;
|
info = existingVersion;
|
||||||
versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
|
versionedDirectoryPath = _fileSystem.GetDirectoryName(info.EncoderPath);
|
||||||
excludeFromDeletions.Add(versionedDirectoryPath);
|
excludeFromDeletions.Add(versionedDirectoryPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ namespace Emby.Server.Implementations.FFMpeg
|
||||||
{
|
{
|
||||||
EncoderPath = encoder,
|
EncoderPath = encoder,
|
||||||
ProbePath = probe,
|
ProbePath = probe,
|
||||||
Version = Path.GetFileName(Path.GetDirectoryName(probe))
|
Version = Path.GetFileName(_fileSystem.GetDirectoryName(probe))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,8 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
_streamFactory,
|
_streamFactory,
|
||||||
_enableDualModeSockets,
|
_enableDualModeSockets,
|
||||||
GetRequest,
|
GetRequest,
|
||||||
_fileSystem);
|
_fileSystem,
|
||||||
|
_environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpRequest GetRequest(HttpListenerContext httpContext)
|
private IHttpRequest GetRequest(HttpListenerContext httpContext)
|
||||||
|
@ -452,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
var date = DateTime.Now;
|
var date = DateTime.Now;
|
||||||
var httpRes = httpReq.Response;
|
var httpRes = httpReq.Response;
|
||||||
bool enableLog = false;
|
bool enableLog = false;
|
||||||
|
bool logHeaders = false;
|
||||||
string urlToLog = null;
|
string urlToLog = null;
|
||||||
string remoteIp = null;
|
string remoteIp = null;
|
||||||
|
|
||||||
|
@ -490,13 +492,14 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
var urlString = url.OriginalString;
|
var urlString = url.OriginalString;
|
||||||
enableLog = EnableLogging(urlString, localPath);
|
enableLog = EnableLogging(urlString, localPath);
|
||||||
urlToLog = urlString;
|
urlToLog = urlString;
|
||||||
|
logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
|
|
||||||
if (enableLog)
|
if (enableLog)
|
||||||
{
|
{
|
||||||
urlToLog = GetUrlToLog(urlString);
|
urlToLog = GetUrlToLog(urlString);
|
||||||
remoteIp = httpReq.RemoteIp;
|
remoteIp = httpReq.RemoteIp;
|
||||||
|
|
||||||
LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent);
|
LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent, logHeaders ? httpReq.Headers : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
@ -611,7 +614,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
var duration = DateTime.Now - date;
|
var duration = DateTime.Now - date;
|
||||||
|
|
||||||
LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration);
|
LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration, logHeaders ? httpRes.Headers : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,31 +353,28 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pres the process optimized result.
|
/// Pres the process optimized result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestContext">The request context.</param>
|
|
||||||
/// <param name="responseHeaders">The responseHeaders.</param>
|
|
||||||
/// <param name="cacheKey">The cache key.</param>
|
|
||||||
/// <param name="cacheKeyString">The cache key string.</param>
|
|
||||||
/// <param name="lastDateModified">The last date modified.</param>
|
|
||||||
/// <param name="cacheDuration">Duration of the cache.</param>
|
|
||||||
/// <param name="contentType">Type of the content.</param>
|
|
||||||
/// <returns>System.Object.</returns>
|
|
||||||
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
|
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
|
||||||
{
|
{
|
||||||
responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
|
responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
|
||||||
|
|
||||||
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
|
var noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
|
|
||||||
|
if (!noCache)
|
||||||
{
|
{
|
||||||
AddAgeHeader(responseHeaders, lastDateModified);
|
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
|
||||||
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
|
{
|
||||||
|
AddAgeHeader(responseHeaders, lastDateModified);
|
||||||
|
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration, noCache);
|
||||||
|
|
||||||
var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
|
var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
|
||||||
|
|
||||||
AddResponseHeaders(result, responseHeaders);
|
AddResponseHeaders(result, responseHeaders);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration);
|
AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration, noCache);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -673,11 +670,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the caching responseHeaders.
|
/// Adds the caching responseHeaders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="responseHeaders">The responseHeaders.</param>
|
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, bool noCache)
|
||||||
/// <param name="cacheKey">The cache key.</param>
|
|
||||||
/// <param name="lastDateModified">The last date modified.</param>
|
|
||||||
/// <param name="cacheDuration">Duration of the cache.</param>
|
|
||||||
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
|
|
||||||
{
|
{
|
||||||
// Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
|
// Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
|
||||||
// https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
|
// https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
|
||||||
|
@ -687,11 +680,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
|
responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cacheDuration.HasValue)
|
if (!noCache && cacheDuration.HasValue)
|
||||||
{
|
{
|
||||||
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
|
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrEmpty(cacheKey))
|
else if (!noCache && !string.IsNullOrEmpty(cacheKey))
|
||||||
{
|
{
|
||||||
responseHeaders["Cache-Control"] = "public";
|
responseHeaders["Cache-Control"] = "public";
|
||||||
}
|
}
|
||||||
|
@ -701,18 +694,15 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
|
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
|
||||||
}
|
}
|
||||||
|
|
||||||
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
|
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the expires header.
|
/// Adds the expires header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="responseHeaders">The responseHeaders.</param>
|
private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration, bool noCache)
|
||||||
/// <param name="cacheKey">The cache key.</param>
|
|
||||||
/// <param name="cacheDuration">Duration of the cache.</param>
|
|
||||||
private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
|
|
||||||
{
|
{
|
||||||
if (cacheDuration.HasValue)
|
if (!noCache && cacheDuration.HasValue)
|
||||||
{
|
{
|
||||||
responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
|
responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using MediaBrowser.Model.Services;
|
||||||
using SocketHttpListener.Net;
|
using SocketHttpListener.Net;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpServer
|
namespace Emby.Server.Implementations.HttpServer
|
||||||
|
@ -19,9 +21,18 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
|
logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogRequest(ILogger logger, string url, string method, string userAgent)
|
public static void LogRequest(ILogger logger, string url, string method, string userAgent, QueryParamCollection headers)
|
||||||
{
|
{
|
||||||
logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
|
if (headers == null)
|
||||||
|
{
|
||||||
|
logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var headerText = string.Join(", ", headers.Select(i => i.Name + "=" + i.Value).ToArray());
|
||||||
|
|
||||||
|
logger.Info("HTTP {0} {1}. {2}", method, url, headerText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -32,12 +43,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// <param name="url">The URL.</param>
|
/// <param name="url">The URL.</param>
|
||||||
/// <param name="endPoint">The end point.</param>
|
/// <param name="endPoint">The end point.</param>
|
||||||
/// <param name="duration">The duration.</param>
|
/// <param name="duration">The duration.</param>
|
||||||
public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration)
|
public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration, QueryParamCollection headers)
|
||||||
{
|
{
|
||||||
var durationMs = duration.TotalMilliseconds;
|
var durationMs = duration.TotalMilliseconds;
|
||||||
var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms";
|
var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms";
|
||||||
|
|
||||||
logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url);
|
var headerText = headers == null ? string.Empty : "Headers: " + string.Join(", ", headers.Where(i => i.Name.IndexOf("Access-", StringComparison.OrdinalIgnoreCase) == -1).Select(i => i.Name + "=" + i.Value).ToArray());
|
||||||
|
logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4} {5}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url, headerText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using MediaBrowser.Model.Cryptography;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
using MediaBrowser.Model.System;
|
||||||
using MediaBrowser.Model.Text;
|
using MediaBrowser.Model.Text;
|
||||||
using SocketHttpListener.Primitives;
|
using SocketHttpListener.Primitives;
|
||||||
|
|
||||||
|
@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
|
private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
|
||||||
private readonly bool _enableDualMode;
|
private readonly bool _enableDualMode;
|
||||||
|
private readonly IEnvironmentInfo _environment;
|
||||||
|
|
||||||
public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem)
|
public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_certificate = certificate;
|
_certificate = certificate;
|
||||||
|
@ -44,6 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||||
_enableDualMode = enableDualMode;
|
_enableDualMode = enableDualMode;
|
||||||
_httpRequestFactory = httpRequestFactory;
|
_httpRequestFactory = httpRequestFactory;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
_environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
|
public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
|
||||||
|
@ -56,7 +59,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||||
public void Start(IEnumerable<string> urlPrefixes)
|
public void Start(IEnumerable<string> urlPrefixes)
|
||||||
{
|
{
|
||||||
if (_listener == null)
|
if (_listener == null)
|
||||||
_listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem);
|
_listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment);
|
||||||
|
|
||||||
_listener.EnableDualMode = _enableDualMode;
|
_listener.EnableDualMode = _enableDualMode;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
using MediaBrowser.Model.Services;
|
||||||
using SocketHttpListener.Net;
|
using SocketHttpListener.Net;
|
||||||
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
|
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
|
||||||
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
|
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
|
||||||
|
@ -66,6 +67,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||||
_response.AddHeader(name, value);
|
_response.AddHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public QueryParamCollection Headers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _response.Headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string GetHeader(string name)
|
public string GetHeader(string name)
|
||||||
{
|
{
|
||||||
return _response.Headers[name];
|
return _response.Headers[name];
|
||||||
|
|
|
@ -34,8 +34,9 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Completed;
|
public event EventHandler<EventArgs> Completed;
|
||||||
private readonly IEnvironmentInfo _environmentInfo;
|
private readonly IEnvironmentInfo _environmentInfo;
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo)
|
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
|
||||||
{
|
{
|
||||||
logger.Debug("New file refresher created for {0}", path);
|
logger.Debug("New file refresher created for {0}", path);
|
||||||
Path = path;
|
Path = path;
|
||||||
|
@ -47,6 +48,7 @@ namespace Emby.Server.Implementations.IO
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
_timerFactory = timerFactory;
|
_timerFactory = timerFactory;
|
||||||
_environmentInfo = environmentInfo;
|
_environmentInfo = environmentInfo;
|
||||||
|
_libraryManager = libraryManager1;
|
||||||
AddPath(path);
|
AddPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +237,12 @@ namespace Emby.Server.Implementations.IO
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only try to open video files
|
||||||
|
if (!_libraryManager.IsVideoFile(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = _fileSystem.GetFileSystemInfo(path);
|
var data = _fileSystem.GetFileSystemInfo(path);
|
||||||
|
|
|
@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
|
||||||
{
|
{
|
||||||
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
|
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre)
|
if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre || item is PhotoAlbum)
|
||||||
{
|
{
|
||||||
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
|
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1197,6 +1197,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
_logger.Info("Post-scan task cancelled: {0}", task.GetType().Name);
|
_logger.Info("Post-scan task cancelled: {0}", task.GetType().Name);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,8 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
|
|
||||||
while (yearNumber < maxYear)
|
while (yearNumber < maxYear)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var year = _libraryManager.GetYear(yearNumber);
|
var year = _libraryManager.GetYear(yearNumber);
|
||||||
|
@ -35,7 +37,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// Don't clutter the log
|
// Don't clutter the log
|
||||||
break;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
clauses.Add("UserId=?");
|
clauses.Add("UserId=?");
|
||||||
paramList.Add(query.UserId.ToGuidParamValue());
|
paramList.Add(query.UserId.ToGuidBlob());
|
||||||
|
|
||||||
var whereClause = " where " + string.Join(" And ", clauses.ToArray());
|
var whereClause = " where " + string.Join(" And ", clauses.ToArray());
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
using (var statement = connection.PrepareStatement("select Level from Notifications where UserId=@UserId and IsRead=@IsRead"))
|
using (var statement = connection.PrepareStatement("select Level from Notifications where UserId=@UserId and IsRead=@IsRead"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@IsRead", false);
|
statement.TryBind("@IsRead", false);
|
||||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||||
|
|
||||||
var levels = new List<NotificationLevel>();
|
var levels = new List<NotificationLevel>();
|
||||||
|
|
||||||
|
@ -159,8 +159,8 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
{
|
{
|
||||||
var notification = new Notification
|
var notification = new Notification
|
||||||
{
|
{
|
||||||
Id = reader[0].ReadGuid().ToString("N"),
|
Id = reader[0].ReadGuidFromBlob().ToString("N"),
|
||||||
UserId = reader[1].ReadGuid().ToString("N"),
|
UserId = reader[1].ReadGuidFromBlob().ToString("N"),
|
||||||
Date = reader[2].ReadDateTime(),
|
Date = reader[2].ReadDateTime(),
|
||||||
Name = reader[3].ToString()
|
Name = reader[3].ToString()
|
||||||
};
|
};
|
||||||
|
@ -251,8 +251,8 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
{
|
{
|
||||||
using (var statement = conn.PrepareStatement("replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"))
|
using (var statement = conn.PrepareStatement("replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@Id", notification.Id.ToGuidParamValue());
|
statement.TryBind("@Id", notification.Id.ToGuidBlob());
|
||||||
statement.TryBind("@UserId", notification.UserId.ToGuidParamValue());
|
statement.TryBind("@UserId", notification.UserId.ToGuidBlob());
|
||||||
statement.TryBind("@Date", notification.Date.ToDateTimeParamValue());
|
statement.TryBind("@Date", notification.Date.ToDateTimeParamValue());
|
||||||
statement.TryBind("@Name", notification.Name);
|
statement.TryBind("@Name", notification.Name);
|
||||||
statement.TryBind("@Description", notification.Description);
|
statement.TryBind("@Description", notification.Description);
|
||||||
|
@ -315,7 +315,7 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId"))
|
using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@IsRead", isRead);
|
statement.TryBind("@IsRead", isRead);
|
||||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||||
|
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
@ -337,13 +337,13 @@ namespace Emby.Server.Implementations.Notifications
|
||||||
using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId and Id=@Id"))
|
using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId and Id=@Id"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@IsRead", isRead);
|
statement.TryBind("@IsRead", isRead);
|
||||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var id in notificationIdList)
|
foreach (var id in notificationIdList)
|
||||||
{
|
{
|
||||||
statement.Reset();
|
statement.Reset();
|
||||||
|
|
||||||
statement.TryBind("@Id", id.ToGuidParamValue());
|
statement.TryBind("@Id", id.ToGuidBlob());
|
||||||
|
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Security
|
||||||
{
|
{
|
||||||
using (var statement = db.PrepareStatement("replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)"))
|
using (var statement = db.PrepareStatement("replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)"))
|
||||||
{
|
{
|
||||||
statement.TryBind("@Id", info.Id.ToGuidParamValue());
|
statement.TryBind("@Id", info.Id.ToGuidBlob());
|
||||||
statement.TryBind("@AccessToken", info.AccessToken);
|
statement.TryBind("@AccessToken", info.AccessToken);
|
||||||
|
|
||||||
statement.TryBind("@DeviceId", info.DeviceId);
|
statement.TryBind("@DeviceId", info.DeviceId);
|
||||||
|
@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.Security
|
||||||
|
|
||||||
using (var statement = connection.PrepareStatement(commandText))
|
using (var statement = connection.PrepareStatement(commandText))
|
||||||
{
|
{
|
||||||
statement.BindParameters["@Id"].Bind(id.ToGuidParamValue());
|
statement.BindParameters["@Id"].Bind(id.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
foreach (var row in statement.ExecuteQuery())
|
||||||
{
|
{
|
||||||
|
@ -275,7 +275,7 @@ namespace Emby.Server.Implementations.Security
|
||||||
{
|
{
|
||||||
var info = new AuthenticationInfo
|
var info = new AuthenticationInfo
|
||||||
{
|
{
|
||||||
Id = reader[0].ReadGuid().ToString("N"),
|
Id = reader[0].ReadGuidFromBlob().ToString("N"),
|
||||||
AccessToken = reader[1].ToString()
|
AccessToken = reader[1].ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Social
|
||||||
var commandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (?, ?, ?, ?)";
|
var commandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (?, ?, ?, ?)";
|
||||||
|
|
||||||
db.Execute(commandText,
|
db.Execute(commandText,
|
||||||
info.Id.ToGuidParamValue(),
|
info.Id.ToGuidBlob(),
|
||||||
info.ItemId,
|
info.ItemId,
|
||||||
info.UserId,
|
info.UserId,
|
||||||
info.ExpirationDate.ToDateTimeParamValue());
|
info.ExpirationDate.ToDateTimeParamValue());
|
||||||
|
@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Social
|
||||||
var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
|
var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
|
||||||
|
|
||||||
var paramList = new List<object>();
|
var paramList = new List<object>();
|
||||||
paramList.Add(id.ToGuidParamValue());
|
paramList.Add(id.ToGuidBlob());
|
||||||
|
|
||||||
foreach (var row in connection.Query(commandText, paramList.ToArray()))
|
foreach (var row in connection.Query(commandText, paramList.ToArray()))
|
||||||
{
|
{
|
||||||
|
@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Social
|
||||||
{
|
{
|
||||||
var info = new SocialShareInfo();
|
var info = new SocialShareInfo();
|
||||||
|
|
||||||
info.Id = reader[0].ReadGuid().ToString("N");
|
info.Id = reader[0].ReadGuidFromBlob().ToString("N");
|
||||||
info.ItemId = reader[1].ToString();
|
info.ItemId = reader[1].ToString();
|
||||||
info.UserId = reader[2].ToString();
|
info.UserId = reader[2].ToString();
|
||||||
info.ExpirationDate = reader[3].ReadDateTime();
|
info.ExpirationDate = reader[3].ReadDateTime();
|
||||||
|
|
|
@ -664,9 +664,19 @@ namespace Emby.Server.Implementations.Updates
|
||||||
// Remove it the quick way for now
|
// Remove it the quick way for now
|
||||||
_applicationHost.RemovePlugin(plugin);
|
_applicationHost.RemovePlugin(plugin);
|
||||||
|
|
||||||
_logger.Info("Deleting plugin file {0}", plugin.AssemblyFilePath);
|
var path = plugin.AssemblyFilePath;
|
||||||
|
_logger.Info("Deleting plugin file {0}", path);
|
||||||
|
|
||||||
_fileSystem.DeleteFile(plugin.AssemblyFilePath);
|
// Make this case-insensitive to account for possible incorrect assembly naming
|
||||||
|
var file = _fileSystem.GetFilePaths(path)
|
||||||
|
.FirstOrDefault(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(file))
|
||||||
|
{
|
||||||
|
path = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileSystem.DeleteFile(path);
|
||||||
|
|
||||||
OnPluginUninstalled(plugin);
|
OnPluginUninstalled(plugin);
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
<package id="Emby.XmlTv" version="1.0.8" targetFramework="portable45-net45+win8" />
|
<package id="Emby.XmlTv" version="1.0.8" targetFramework="portable45-net45+win8" />
|
||||||
<package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
|
<package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
|
||||||
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
|
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
|
||||||
<package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="portable45-net45+win8" />
|
<package id="SQLitePCLRaw.core" version="1.1.5" targetFramework="portable45-net45+win8" />
|
||||||
</packages>
|
</packages>
|
|
@ -54,8 +54,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server.Core", "Emby.Se
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
|
|
|
@ -82,10 +82,6 @@
|
||||||
<Compile Include="Reports\Model\ReportRow.cs" />
|
<Compile Include="Reports\Model\ReportRow.cs" />
|
||||||
<Compile Include="Reports\ReportRequests.cs" />
|
<Compile Include="Reports\ReportRequests.cs" />
|
||||||
<Compile Include="Reports\ReportsService.cs" />
|
<Compile Include="Reports\ReportsService.cs" />
|
||||||
<Compile Include="Reports\Stat\ReportStatBuilder.cs" />
|
|
||||||
<Compile Include="Reports\Stat\ReportStatGroup.cs" />
|
|
||||||
<Compile Include="Reports\Stat\ReportStatItem.cs" />
|
|
||||||
<Compile Include="Reports\Stat\ReportStatResult.cs" />
|
|
||||||
<Compile Include="Social\SharingService.cs" />
|
<Compile Include="Social\SharingService.cs" />
|
||||||
<Compile Include="StartupWizardService.cs" />
|
<Compile Include="StartupWizardService.cs" />
|
||||||
<Compile Include="Subtitles\SubtitleService.cs" />
|
<Compile Include="Subtitles\SubtitleService.cs" />
|
||||||
|
|
|
@ -671,12 +671,15 @@ namespace MediaBrowser.Api.Playback
|
||||||
request.AudioCodec = EncodingHelper.InferAudioCodec(url);
|
request.AudioCodec = EncodingHelper.InferAudioCodec(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
|
||||||
|
string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
|
||||||
|
|
||||||
var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
|
var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
|
||||||
{
|
{
|
||||||
Request = request,
|
Request = request,
|
||||||
RequestedUrl = url,
|
RequestedUrl = url,
|
||||||
UserAgent = Request.UserAgent,
|
UserAgent = Request.UserAgent,
|
||||||
EnableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params)
|
EnableDlnaHeaders = enableDlnaHeaders
|
||||||
};
|
};
|
||||||
|
|
||||||
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
|
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
|
||||||
|
|
|
@ -879,7 +879,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
// Add resolution params, if specified
|
// Add resolution params, if specified
|
||||||
if (!hasGraphicalSubs)
|
if (!hasGraphicalSubs)
|
||||||
{
|
{
|
||||||
args += EncodingHelper.GetOutputSizeParam(state, codec, EnableCopyTs(state));
|
args += EncodingHelper.GetOutputSizeParam(state, codec, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for internal graphical subs
|
// This is for internal graphical subs
|
||||||
|
@ -891,7 +891,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
//args += " -flags -global_header";
|
//args += " -flags -global_header";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
|
if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
|
||||||
{
|
{
|
||||||
args += " -copyts";
|
args += " -copyts";
|
||||||
}
|
}
|
||||||
|
@ -901,13 +901,9 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
args += " -vsync " + state.OutputVideoSync;
|
args += " -vsync " + state.OutputVideoSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
args += EncodingHelper.GetOutputFFlags(state);
|
||||||
}
|
|
||||||
|
|
||||||
private bool EnableCopyTs(StreamState state)
|
return args;
|
||||||
{
|
|
||||||
//return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||||
|
|
|
@ -124,6 +124,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
args += " -vsync " + state.OutputVideoSync;
|
args += " -vsync " + state.OutputVideoSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args += EncodingHelper.GetOutputFFlags(state);
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ namespace MediaBrowser.Api.Reports
|
||||||
Tracks,
|
Tracks,
|
||||||
EpisodeSeries,
|
EpisodeSeries,
|
||||||
EpisodeSeason,
|
EpisodeSeason,
|
||||||
AudioAlbumArtist,
|
EpisodeNumber,
|
||||||
|
AudioAlbumArtist,
|
||||||
MusicArtist,
|
MusicArtist,
|
||||||
AudioAlbum,
|
AudioAlbum,
|
||||||
Locked,
|
Locked,
|
||||||
|
|
|
@ -148,6 +148,11 @@ namespace MediaBrowser.Api.Reports
|
||||||
/// <returns> The localized header. </returns>
|
/// <returns> The localized header. </returns>
|
||||||
protected static string GetLocalizedHeader(HeaderMetadata internalHeader)
|
protected static string GetLocalizedHeader(HeaderMetadata internalHeader)
|
||||||
{
|
{
|
||||||
|
if (internalHeader == HeaderMetadata.EpisodeNumber)
|
||||||
|
{
|
||||||
|
return "Episode";
|
||||||
|
}
|
||||||
|
|
||||||
string headerName = "";
|
string headerName = "";
|
||||||
if (internalHeader != HeaderMetadata.None)
|
if (internalHeader != HeaderMetadata.None)
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Reports
|
||||||
HeaderMetadata.Series,
|
HeaderMetadata.Series,
|
||||||
HeaderMetadata.Season,
|
HeaderMetadata.Season,
|
||||||
HeaderMetadata.SeasonNumber,
|
HeaderMetadata.SeasonNumber,
|
||||||
HeaderMetadata.DateAdded,
|
HeaderMetadata.DateAdded,
|
||||||
HeaderMetadata.Year,
|
HeaderMetadata.Year,
|
||||||
HeaderMetadata.Genres
|
HeaderMetadata.Genres
|
||||||
};
|
};
|
||||||
|
@ -269,10 +269,11 @@ namespace MediaBrowser.Api.Reports
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
HeaderMetadata.Name,
|
HeaderMetadata.Name,
|
||||||
HeaderMetadata.EpisodeSeries,
|
HeaderMetadata.EpisodeSeries,
|
||||||
HeaderMetadata.Season,
|
HeaderMetadata.Season,
|
||||||
HeaderMetadata.DateAdded,
|
HeaderMetadata.EpisodeNumber,
|
||||||
|
HeaderMetadata.DateAdded,
|
||||||
HeaderMetadata.ReleaseDate,
|
HeaderMetadata.ReleaseDate,
|
||||||
HeaderMetadata.Year,
|
HeaderMetadata.Year,
|
||||||
HeaderMetadata.Genres,
|
HeaderMetadata.Genres,
|
||||||
|
@ -450,6 +451,12 @@ namespace MediaBrowser.Api.Reports
|
||||||
internalHeader = HeaderMetadata.Season;
|
internalHeader = HeaderMetadata.Season;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HeaderMetadata.EpisodeNumber:
|
||||||
|
option.Column = (i, r) => this.GetObject<BaseItem, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString());
|
||||||
|
//option.Header.SortField = "IndexNumber";
|
||||||
|
//option.Header.HeaderFieldType = ReportFieldType.Int;
|
||||||
|
break;
|
||||||
|
|
||||||
case HeaderMetadata.Network:
|
case HeaderMetadata.Network:
|
||||||
option.Column = (i, r) => this.GetListAsString(i.Studios);
|
option.Column = (i, r) => this.GetListAsString(i.Studios);
|
||||||
option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault());
|
option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault());
|
||||||
|
|
|
@ -1,256 +0,0 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Controller.Library;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Reports
|
|
||||||
{
|
|
||||||
/// <summary> A report stat builder. </summary>
|
|
||||||
/// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
|
|
||||||
public class ReportStatBuilder : ReportBuilderBase
|
|
||||||
{
|
|
||||||
#region [Constructors]
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary>
|
|
||||||
/// <param name="libraryManager"> Manager for library. </param>
|
|
||||||
public ReportStatBuilder(ILibraryManager libraryManager)
|
|
||||||
: base(libraryManager)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region [Public Methods]
|
|
||||||
|
|
||||||
/// <summary> Gets report stat result. </summary>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="reportIncludeItemTypes"> List of types of the report include items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The report stat result. </returns>
|
|
||||||
public ReportStatResult GetResult(BaseItem[] items, ReportIncludeItemTypes reportIncludeItemTypes, int topItem = 5)
|
|
||||||
{
|
|
||||||
ReportStatResult result = new ReportStatResult();
|
|
||||||
result = this.GetResultGenres(result, items, topItem);
|
|
||||||
result = this.GetResultStudios(result, items, topItem);
|
|
||||||
result = this.GetResultPersons(result, items, topItem);
|
|
||||||
result = this.GetResultProductionYears(result, items, topItem);
|
|
||||||
result = this.GetResultCommunityRatings(result, items, topItem);
|
|
||||||
result = this.GetResultParentalRatings(result, items, topItem);
|
|
||||||
|
|
||||||
switch (reportIncludeItemTypes)
|
|
||||||
{
|
|
||||||
case ReportIncludeItemTypes.Season:
|
|
||||||
case ReportIncludeItemTypes.Series:
|
|
||||||
case ReportIncludeItemTypes.MusicAlbum:
|
|
||||||
case ReportIncludeItemTypes.MusicArtist:
|
|
||||||
case ReportIncludeItemTypes.Game:
|
|
||||||
break;
|
|
||||||
case ReportIncludeItemTypes.Movie:
|
|
||||||
case ReportIncludeItemTypes.BoxSet:
|
|
||||||
|
|
||||||
break;
|
|
||||||
case ReportIncludeItemTypes.Book:
|
|
||||||
case ReportIncludeItemTypes.Episode:
|
|
||||||
case ReportIncludeItemTypes.Video:
|
|
||||||
case ReportIncludeItemTypes.MusicVideo:
|
|
||||||
case ReportIncludeItemTypes.Trailer:
|
|
||||||
case ReportIncludeItemTypes.Audio:
|
|
||||||
case ReportIncludeItemTypes.BaseItem:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region [Protected Internal Methods]
|
|
||||||
/// <summary> Gets the headers. </summary>
|
|
||||||
/// <typeparam name="H"> Type of the header. </typeparam>
|
|
||||||
/// <param name="request"> The request. </param>
|
|
||||||
/// <returns> The headers. </returns>
|
|
||||||
/// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/>
|
|
||||||
protected internal override List<ReportHeader> GetHeaders<H>(H request)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region [Private Methods]
|
|
||||||
|
|
||||||
/// <summary> Gets the groups. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="header"> The header. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <param name="top"> The top. </param>
|
|
||||||
private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top)
|
|
||||||
{
|
|
||||||
if (top != null && top.Count() > 0)
|
|
||||||
{
|
|
||||||
var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
|
|
||||||
group.Items.AddRange(top);
|
|
||||||
result.Groups.Add(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result community ratings. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result community ratings. </returns>
|
|
||||||
private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.CommunityRating), topItem,
|
|
||||||
items.Where(x => x.CommunityRating != null && x.CommunityRating > 0)
|
|
||||||
.GroupBy(x => x.CommunityRating)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem)
|
|
||||||
.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key.ToString(),
|
|
||||||
Value = x.Count().ToString()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result genres. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result genres. </returns>
|
|
||||||
private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Genres), topItem,
|
|
||||||
items.SelectMany(x => x.Genres)
|
|
||||||
.GroupBy(x => x)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem)
|
|
||||||
.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key,
|
|
||||||
Value = x.Count().ToString(),
|
|
||||||
Id = GetGenreID(x.Key)
|
|
||||||
}));
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result parental ratings. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result parental ratings. </returns>
|
|
||||||
private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.ParentalRatings), topItem,
|
|
||||||
items.Where(x => x.OfficialRating != null)
|
|
||||||
.GroupBy(x => x.OfficialRating)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem)
|
|
||||||
.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key.ToString(),
|
|
||||||
Value = x.Count().ToString()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result persons. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result persons. </returns>
|
|
||||||
private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
List<HeaderMetadata> t = new List<HeaderMetadata>
|
|
||||||
{
|
|
||||||
HeaderMetadata.Actor,
|
|
||||||
HeaderMetadata.Composer,
|
|
||||||
HeaderMetadata.Director,
|
|
||||||
HeaderMetadata.GuestStar,
|
|
||||||
HeaderMetadata.Producer,
|
|
||||||
HeaderMetadata.Writer,
|
|
||||||
HeaderMetadata.Artist,
|
|
||||||
HeaderMetadata.AlbumArtist
|
|
||||||
};
|
|
||||||
foreach (var item in t)
|
|
||||||
{
|
|
||||||
var ps = items.SelectMany(x => _libraryManager.GetPeople(x))
|
|
||||||
.Where(n => n.Type == item.ToString())
|
|
||||||
.GroupBy(x => x.Name)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem);
|
|
||||||
if (ps != null && ps.Count() > 0)
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(item), topItem,
|
|
||||||
ps.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key,
|
|
||||||
Value = x.Count().ToString(),
|
|
||||||
Id = GetPersonID(x.Key)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result production years. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result production years. </returns>
|
|
||||||
private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Year), topItem,
|
|
||||||
items.Where(x => x.ProductionYear != null && x.ProductionYear > 0)
|
|
||||||
.GroupBy(x => x.ProductionYear)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem)
|
|
||||||
.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key.ToString(),
|
|
||||||
Value = x.Count().ToString()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets result studios. </summary>
|
|
||||||
/// <param name="result"> The result. </param>
|
|
||||||
/// <param name="items"> The items. </param>
|
|
||||||
/// <param name="topItem"> The top item. </param>
|
|
||||||
/// <returns> The result studios. </returns>
|
|
||||||
private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5)
|
|
||||||
{
|
|
||||||
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Studios), topItem,
|
|
||||||
items.SelectMany(x => x.Studios)
|
|
||||||
.GroupBy(x => x)
|
|
||||||
.OrderByDescending(x => x.Count())
|
|
||||||
.Take(topItem)
|
|
||||||
.Select(x => new ReportStatItem
|
|
||||||
{
|
|
||||||
Name = x.Key,
|
|
||||||
Value = x.Count().ToString(),
|
|
||||||
Id = GetStudioID(x.Key)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Reports
|
|
||||||
{
|
|
||||||
/// <summary> A report stat group. </summary>
|
|
||||||
public class ReportStatGroup
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatGroup class. </summary>
|
|
||||||
public ReportStatGroup()
|
|
||||||
{
|
|
||||||
Items = new List<ReportStatItem>();
|
|
||||||
TotalRecordCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the header. </summary>
|
|
||||||
/// <value> The header. </value>
|
|
||||||
public string Header { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the items. </summary>
|
|
||||||
/// <value> The items. </value>
|
|
||||||
public List<ReportStatItem> Items { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the number of total records. </summary>
|
|
||||||
/// <value> The total number of record count. </value>
|
|
||||||
public int TotalRecordCount { get; set; }
|
|
||||||
|
|
||||||
internal static string FormatedHeader(string header, int topItem)
|
|
||||||
{
|
|
||||||
return string.Format("Top {0} {1}", topItem, header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
namespace MediaBrowser.Api.Reports
|
|
||||||
{
|
|
||||||
/// <summary> A report stat item. </summary>
|
|
||||||
public class ReportStatItem
|
|
||||||
{
|
|
||||||
/// <summary> Gets or sets the name. </summary>
|
|
||||||
/// <value> The name. </value>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the image. </summary>
|
|
||||||
/// <value> The image. </value>
|
|
||||||
public string Image { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the value. </summary>
|
|
||||||
/// <value> The value. </value>
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the identifier. </summary>
|
|
||||||
/// <value> The identifier. </value>
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Reports
|
|
||||||
{
|
|
||||||
/// <summary> Encapsulates the result of a report stat. </summary>
|
|
||||||
public class ReportStatResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatResult class. </summary>
|
|
||||||
public ReportStatResult()
|
|
||||||
{
|
|
||||||
Groups = new List<ReportStatGroup>();
|
|
||||||
TotalRecordCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the groups. </summary>
|
|
||||||
/// <value> The groups. </value>
|
|
||||||
public List<ReportStatGroup> Groups { get; set; }
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the number of total records. </summary>
|
|
||||||
/// <value> The total number of record count. </value>
|
|
||||||
public int TotalRecordCount { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,12 +16,6 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// <value>The supported output formats.</value>
|
/// <value>The supported output formats.</value>
|
||||||
ImageFormat[] SupportedOutputFormats { get; }
|
ImageFormat[] SupportedOutputFormats { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Crops the white space.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="inputPath">The input path.</param>
|
|
||||||
/// <param name="outputPath">The output path.</param>
|
|
||||||
void CropWhiteSpace(string inputPath, string outputPath);
|
|
||||||
/// <summary>
|
|
||||||
/// Encodes the image.
|
/// Encodes the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
|
@ -56,5 +50,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
||||||
bool SupportsImageEncoding { get; }
|
bool SupportsImageEncoding { get; }
|
||||||
|
|
||||||
|
ImageSize GetImageSize(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,5 +112,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
|
||||||
bool SupportsImageCollageCreation { get; }
|
bool SupportsImageCollageCreation { get; }
|
||||||
|
|
||||||
|
IImageEncoder ImageEncoder { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue