mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-19 20:09:03 -07:00
commit
1a6ee3d48a
@ -392,10 +392,27 @@ namespace Emby.Common.Implementations.IO
|
||||
|
||||
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)
|
||||
@ -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)
|
||||
{
|
||||
return attributes & ~attributesToRemove;
|
||||
@ -673,20 +733,7 @@ namespace Emby.Common.Implementations.IO
|
||||
return;
|
||||
}
|
||||
|
||||
var fileInfo = GetFileInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
if (fileInfo.IsHidden)
|
||||
{
|
||||
SetHidden(path, false);
|
||||
}
|
||||
if (fileInfo.IsReadOnly)
|
||||
{
|
||||
SetReadOnly(path, false);
|
||||
}
|
||||
}
|
||||
|
||||
SetAttributes(path, false, false);
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,11 @@ namespace Emby.Common.Implementations.IO
|
||||
if (separator == '/')
|
||||
{
|
||||
result = result.Replace('\\', '/');
|
||||
|
||||
if (result.StartsWith("smb:/", StringComparison.OrdinalIgnoreCase) && !result.StartsWith("smb://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = result.Replace("smb:/", "smb://");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -161,23 +166,38 @@ namespace Emby.Common.Implementations.IO
|
||||
public void SetHidden(string path, bool isHidden)
|
||||
{
|
||||
var file = CreateSmbFile(path);
|
||||
|
||||
var isCurrentlyHidden = file.IsHidden();
|
||||
|
||||
if (isCurrentlyHidden && !isHidden)
|
||||
{
|
||||
file.SetAttributes(file.GetAttributes() & ~SmbFile.AttrReadonly);
|
||||
}
|
||||
else if (!isCurrentlyHidden && isHidden)
|
||||
{
|
||||
file.SetAttributes(file.GetAttributes() | SmbFile.AttrReadonly);
|
||||
}
|
||||
SetHidden(file, isHidden);
|
||||
}
|
||||
|
||||
public void SetReadOnly(string path, bool isReadOnly)
|
||||
{
|
||||
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();
|
||||
|
||||
if (isCurrentlyReadOnly && !isReadOnly)
|
||||
|
@ -45,7 +45,7 @@
|
||||
"System.Net.Requests": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "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.Sockets": "4.3.0",
|
||||
"System.Net.NetworkInformation": "4.3.0",
|
||||
|
@ -587,10 +587,7 @@ namespace Emby.Dlna
|
||||
new DirectTvProfile(),
|
||||
new DishHopperJoeyProfile(),
|
||||
new DefaultProfile(),
|
||||
new PopcornHourProfile(),
|
||||
new VlcProfile(),
|
||||
new BubbleUpnpProfile(),
|
||||
new KodiProfile(),
|
||||
new PopcornHourProfile()
|
||||
};
|
||||
|
||||
foreach (var item in list)
|
||||
|
@ -79,13 +79,11 @@
|
||||
<Compile Include="PlayTo\uParserObject.cs" />
|
||||
<Compile Include="PlayTo\UpnpContainer.cs" />
|
||||
<Compile Include="PlayTo\uPnpNamespaces.cs" />
|
||||
<Compile Include="Profiles\BubbleUpnpProfile.cs" />
|
||||
<Compile Include="Profiles\DefaultProfile.cs" />
|
||||
<Compile Include="Profiles\DenonAvrProfile.cs" />
|
||||
<Compile Include="Profiles\DirectTvProfile.cs" />
|
||||
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
|
||||
<Compile Include="Profiles\Foobar2000Profile.cs" />
|
||||
<Compile Include="Profiles\KodiProfile.cs" />
|
||||
<Compile Include="Profiles\LgTvProfile.cs" />
|
||||
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
|
||||
<Compile Include="Profiles\MediaMonkeyProfile.cs" />
|
||||
@ -105,7 +103,6 @@
|
||||
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
|
||||
<Compile Include="Profiles\SonyPs3Profile.cs" />
|
||||
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
||||
<Compile Include="Profiles\VlcProfile.cs" />
|
||||
<Compile Include="Profiles\WdtvLiveProfile.cs" />
|
||||
<Compile Include="Profiles\Xbox360Profile.cs" />
|
||||
<Compile Include="Profiles\XboxOneProfile.cs" />
|
||||
@ -153,13 +150,11 @@
|
||||
<EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" />
|
||||
<EmbeddedResource Include="Profiles\Xml\Default.xml" />
|
||||
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
|
||||
<EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
|
||||
<EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.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\Linksys DMA2100.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 PlayStation 3.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\Xbox 360.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;
|
||||
MaxIconHeight = 48;
|
||||
|
||||
MaxStreamingBitrate = 30000000;
|
||||
MaxStaticBitrate = 30000000;
|
||||
MaxStreamingBitrate = 40000000;
|
||||
MaxStaticBitrate = 40000000;
|
||||
MusicStreamingTranscodingBitrate = 192000;
|
||||
|
||||
EnableAlbumArtInDidl = false;
|
||||
@ -64,15 +64,13 @@ namespace Emby.Dlna.Profiles
|
||||
{
|
||||
new DirectPlayProfile
|
||||
{
|
||||
Container = "m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov",
|
||||
VideoCodec = "h264",
|
||||
AudioCodec = "aac,mp3,ac3",
|
||||
Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms",
|
||||
Type = DlnaProfileType.Video
|
||||
},
|
||||
|
||||
new DirectPlayProfile
|
||||
{
|
||||
Container = "mp3,wma,aac,wav,flac",
|
||||
Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac",
|
||||
Type = DlnaProfileType.Audio
|
||||
}
|
||||
};
|
||||
@ -82,13 +80,61 @@ namespace Emby.Dlna.Profiles
|
||||
new SubtitleProfile
|
||||
{
|
||||
Format = "srt",
|
||||
Method = SubtitleDeliveryMethod.Embed
|
||||
Method = SubtitleDeliveryMethod.External,
|
||||
},
|
||||
|
||||
new SubtitleProfile
|
||||
{
|
||||
Format = "sub",
|
||||
Method = SubtitleDeliveryMethod.External,
|
||||
},
|
||||
|
||||
new SubtitleProfile
|
||||
{
|
||||
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>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
@ -29,8 +29,8 @@
|
||||
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
||||
<XmlRootAttributes />
|
||||
<DirectPlayProfiles>
|
||||
<DirectPlayProfile container="m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
|
||||
<DirectPlayProfile container="mp3,wma,aac,wav,flac" type="Audio" />
|
||||
<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="aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac" type="Audio" />
|
||||
</DirectPlayProfiles>
|
||||
<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" />
|
||||
@ -45,7 +45,15 @@
|
||||
</ResponseProfile>
|
||||
</ResponseProfiles>
|
||||
<SubtitleProfiles>
|
||||
<SubtitleProfile format="srt" method="Embed" />
|
||||
<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>
|
||||
</Profile>
|
@ -21,8 +21,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
@ -45,7 +45,15 @@
|
||||
<CodecProfiles />
|
||||
<ResponseProfiles />
|
||||
<SubtitleProfiles>
|
||||
<SubtitleProfile format="srt" method="Embed" />
|
||||
<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>
|
||||
</Profile>
|
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
File diff suppressed because one or more lines are too long
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -20,8 +20,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
@ -51,7 +51,15 @@
|
||||
<CodecProfiles />
|
||||
<ResponseProfiles />
|
||||
<SubtitleProfiles>
|
||||
<SubtitleProfile format="srt" method="Embed" />
|
||||
<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>
|
||||
</Profile>
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -16,8 +16,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -26,8 +26,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -26,8 +26,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
File diff suppressed because one or more lines are too long
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
|
@ -22,8 +22,8 @@
|
||||
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
|
||||
<MaxIconWidth>48</MaxIconWidth>
|
||||
<MaxIconHeight>48</MaxIconHeight>
|
||||
<MaxStreamingBitrate>30000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>30000000</MaxStaticBitrate>
|
||||
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
|
||||
<MaxStaticBitrate>40000000</MaxStaticBitrate>
|
||||
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
|
||||
<MaxStaticMusicBitrate xsi:nil="true" />
|
||||
<XDlnaDoc>DMS-1.50</XDlnaDoc>
|
||||
@ -51,7 +51,15 @@
|
||||
<CodecProfiles />
|
||||
<ResponseProfiles />
|
||||
<SubtitleProfiles>
|
||||
<SubtitleProfile format="srt" method="Embed" />
|
||||
<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>
|
||||
</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)
|
||||
{
|
||||
CheckDisposed();
|
||||
@ -150,6 +139,11 @@ namespace Emby.Drawing.ImageMagick
|
||||
{
|
||||
using (var originalImage = new MagickWand(inputPath))
|
||||
{
|
||||
if (options.CropWhiteSpace)
|
||||
{
|
||||
originalImage.CurrentImage.TrimImage(10);
|
||||
}
|
||||
|
||||
ScaleImage(originalImage, width, height, options.Blur ?? 0);
|
||||
|
||||
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));
|
||||
|
||||
using (var outputStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false))
|
||||
{
|
||||
croppedImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100);
|
||||
}
|
||||
return originalImage.CropWhitespace();
|
||||
}
|
||||
}
|
||||
|
||||
return Image.FromFile(path);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
using (var originalImage = Image.FromFile(inputPath))
|
||||
using (var originalImage = GetImage(inputPath, options.CropWhiteSpace))
|
||||
{
|
||||
var newWidth = Convert.ToInt32(width);
|
||||
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 IJsonSerializer _jsonSerializer;
|
||||
private readonly IServerApplicationPaths _appPaths;
|
||||
private readonly IImageEncoder _imageEncoder;
|
||||
private IImageEncoder _imageEncoder;
|
||||
private readonly Func<ILibraryManager> _libraryManager;
|
||||
|
||||
public ImageProcessor(ILogger logger,
|
||||
@ -64,7 +64,7 @@ namespace Emby.Drawing
|
||||
IFileSystem fileSystem,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IImageEncoder imageEncoder,
|
||||
int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
|
||||
Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
@ -103,6 +103,20 @@ namespace Emby.Drawing
|
||||
_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
|
||||
{
|
||||
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)
|
||||
{
|
||||
ImageEnhancers = enhancers.ToArray();
|
||||
@ -186,14 +192,6 @@ namespace Emby.Drawing
|
||||
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)
|
||||
{
|
||||
var tuple = await GetEnhancedImage(new ItemImageInfo
|
||||
@ -214,7 +212,7 @@ namespace Emby.Drawing
|
||||
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||
}
|
||||
|
||||
ImageSize? originalImageSize;
|
||||
ImageSize? originalImageSize = null;
|
||||
try
|
||||
{
|
||||
originalImageSize = GetImageSize(originalImagePath, dateModified, true);
|
||||
@ -241,8 +239,8 @@ namespace Emby.Drawing
|
||||
|
||||
if (!_fileSystem.FileExists(cacheFilePath))
|
||||
{
|
||||
var newWidth = Convert.ToInt32(newSize.Width);
|
||||
var newHeight = Convert.ToInt32(newSize.Height);
|
||||
var newWidth = Convert.ToInt32(Math.Round(newSize.Width));
|
||||
var newHeight = Convert.ToInt32(Math.Round(newSize.Height));
|
||||
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(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);
|
||||
}
|
||||
|
||||
var aspect = GetEstimatedAspectRatio(options.Image.Type);
|
||||
var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item);
|
||||
|
||||
var width = options.Width ?? options.MaxWidth;
|
||||
|
||||
if (width.HasValue)
|
||||
{
|
||||
var heightValue = aspect / width.Value;
|
||||
return new ImageSize(width.Value, Convert.ToInt32(heightValue));
|
||||
var heightValue = width.Value / aspect;
|
||||
return new ImageSize(width.Value, heightValue);
|
||||
}
|
||||
|
||||
var height = options.Height ?? options.MaxHeight ?? 200;
|
||||
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)
|
||||
{
|
||||
@ -384,7 +382,7 @@ namespace Emby.Drawing
|
||||
case ImageType.Logo:
|
||||
return 2.58;
|
||||
case ImageType.Primary:
|
||||
return .667;
|
||||
return item.GetDefaultPrimaryImageAspectRatio() ?? .667;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
@ -400,46 +398,6 @@ namespace Emby.Drawing
|
||||
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)
|
||||
{
|
||||
return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
|
||||
@ -555,26 +513,39 @@ namespace Emby.Drawing
|
||||
/// <returns>ImageSize.</returns>
|
||||
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
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if (allowSlowMethod)
|
||||
{
|
||||
return _imageEncoder.GetImageSize(path);
|
||||
}
|
||||
|
||||
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ITimer _saveImageSizeTimer;
|
||||
|
@ -57,6 +57,11 @@ namespace Emby.Drawing
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public ImageSize GetImageSize(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ namespace Emby.Server.Core
|
||||
/// <value>The HTTP server.</value>
|
||||
private IHttpServer HttpServer { get; set; }
|
||||
private IDtoService DtoService { get; set; }
|
||||
private IImageProcessor ImageProcessor { get; set; }
|
||||
public IImageProcessor ImageProcessor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the media encoder.
|
||||
@ -761,7 +761,10 @@ namespace Emby.Server.Core
|
||||
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;
|
||||
if (!localCert.HasPrivateKey)
|
||||
{
|
||||
@ -780,14 +783,7 @@ namespace Emby.Server.Core
|
||||
|
||||
private IImageProcessor GetImageProcessor()
|
||||
{
|
||||
var maxConcurrentImageProcesses = Math.Max(Environment.ProcessorCount, 4);
|
||||
|
||||
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);
|
||||
return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory);
|
||||
}
|
||||
|
||||
protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
|
||||
@ -1132,7 +1128,8 @@ namespace Emby.Server.Core
|
||||
// Custom cert
|
||||
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;
|
||||
_activeRefreshers.Add(newRefresher);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@
|
||||
"System.AppDomain": "2.0.11",
|
||||
"System.Globalization.Extensions": "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.Runtime.Extensions": "4.3.0",
|
||||
"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)"))
|
||||
{
|
||||
statement.TryBind("@Id", entry.Id.ToGuidParamValue());
|
||||
statement.TryBind("@Id", entry.Id.ToGuidBlob());
|
||||
statement.TryBind("@Name", entry.Name);
|
||||
|
||||
statement.TryBind("@Overview", entry.Overview);
|
||||
@ -168,7 +168,7 @@ namespace Emby.Server.Implementations.Activity
|
||||
|
||||
var info = new ActivityLogEntry
|
||||
{
|
||||
Id = reader[index].ReadGuid().ToString("N")
|
||||
Id = reader[index].ReadGuidFromBlob().ToString("N")
|
||||
};
|
||||
|
||||
index++;
|
||||
|
@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
Logger.Info("Saving system configuration");
|
||||
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
||||
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
@ -293,7 +293,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||
|
||||
var path = GetConfigurationFile(key);
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
||||
|
||||
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 (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
|
||||
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)"))
|
||||
{
|
||||
statement.TryBind("@id", displayPreferences.Id.ToGuidParamValue());
|
||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@id", displayPreferences.Id.ToGuidBlob());
|
||||
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||
statement.TryBind("@client", client);
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@id", guidId.ToGuidParamValue());
|
||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@id", guidId.ToGuidBlob());
|
||||
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||
statement.TryBind("@client", client);
|
||||
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static Guid ReadGuid(this IResultSetValue result)
|
||||
public static Guid ReadGuidFromBlob(this IResultSetValue result)
|
||||
{
|
||||
return new Guid(result.ToBlob());
|
||||
}
|
||||
@ -172,7 +172,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
|
||||
{
|
||||
return result[index].ReadGuid();
|
||||
return result[index].ReadGuidFromBlob();
|
||||
}
|
||||
|
||||
private static void CheckName(string name)
|
||||
@ -262,7 +262,7 @@ namespace Emby.Server.Implementations.Data
|
||||
IBindParameter bindParam;
|
||||
if (statement.BindParameters.TryGetValue(name, out bindParam))
|
||||
{
|
||||
bindParam.Bind(value.ToGuidParamValue());
|
||||
bindParam.Bind(value.ToGuidBlob());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
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("@TargetPath", result.TargetPath);
|
||||
@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
|
||||
{
|
||||
statement.TryBind("@ResultId", id.ToGuidParamValue());
|
||||
statement.TryBind("@ResultId", id.ToGuidBlob());
|
||||
statement.MoveNext();
|
||||
}
|
||||
}, 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"))
|
||||
{
|
||||
statement.TryBind("@ResultId", id.ToGuidParamValue());
|
||||
statement.TryBind("@ResultId", id.ToGuidBlob());
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
var result = new FileOrganizationResult
|
||||
{
|
||||
Id = reader[0].ReadGuid().ToString("N")
|
||||
Id = reader[0].ReadGuidFromBlob().ToString("N")
|
||||
};
|
||||
|
||||
index++;
|
||||
|
@ -2128,7 +2128,7 @@ namespace Emby.Server.Implementations.Data
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
// 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)"))
|
||||
{
|
||||
@ -2139,7 +2139,7 @@ namespace Emby.Server.Implementations.Data
|
||||
saveChapterStatement.Reset();
|
||||
}
|
||||
|
||||
saveChapterStatement.TryBind("@ItemId", id.ToGuidParamValue());
|
||||
saveChapterStatement.TryBind("@ItemId", id.ToGuidBlob());
|
||||
saveChapterStatement.TryBind("@ChapterIndex", index);
|
||||
saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
|
||||
saveChapterStatement.TryBind("@Name", chapter.Name);
|
||||
@ -2919,7 +2919,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
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())
|
||||
{
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, personId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, personId.ToGuidBlob());
|
||||
}
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, artistId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, artistId.ToGuidBlob());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@ -3862,7 +3862,7 @@ namespace Emby.Server.Implementations.Data
|
||||
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, albumId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, albumId.ToGuidBlob());
|
||||
}
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, artistId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, artistId.ToGuidBlob());
|
||||
}
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, genreId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, genreId.ToGuidBlob());
|
||||
}
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, studioId.ToGuidParamValue());
|
||||
statement.TryBind(paramName, studioId.ToGuidBlob());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@ -4521,22 +4521,22 @@ namespace Emby.Server.Implementations.Data
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
// Delete people
|
||||
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidParamValue());
|
||||
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidBlob());
|
||||
|
||||
// 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
|
||||
ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidParamValue());
|
||||
ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidBlob());
|
||||
|
||||
// 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
|
||||
ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidParamValue());
|
||||
ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidBlob());
|
||||
|
||||
// Delete the item
|
||||
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidParamValue());
|
||||
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidBlob());
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
@ -4643,7 +4643,7 @@ namespace Emby.Server.Implementations.Data
|
||||
whereClauses.Add("ItemId=@ItemId");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
|
||||
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
|
||||
}
|
||||
}
|
||||
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)");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
|
||||
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidBlob());
|
||||
}
|
||||
}
|
||||
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
|
||||
@ -4730,14 +4730,14 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
// First delete
|
||||
deleteAncestorsStatement.Reset();
|
||||
deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
||||
deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||
deleteAncestorsStatement.MoveNext();
|
||||
|
||||
foreach (var ancestorId in ancestorIds)
|
||||
{
|
||||
updateAncestorsStatement.Reset();
|
||||
updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
||||
updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidParamValue());
|
||||
updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||
updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidBlob());
|
||||
updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
|
||||
updateAncestorsStatement.MoveNext();
|
||||
}
|
||||
@ -5198,7 +5198,7 @@ namespace Emby.Server.Implementations.Data
|
||||
CheckDisposed();
|
||||
|
||||
// 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)"))
|
||||
{
|
||||
@ -5214,7 +5214,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
statement.Reset();
|
||||
|
||||
statement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
||||
statement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||
statement.TryBind("@Type", pair.Item1);
|
||||
statement.TryBind("@Value", itemValue);
|
||||
|
||||
@ -5252,7 +5252,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
// First delete
|
||||
// "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;
|
||||
|
||||
@ -5266,7 +5266,7 @@ namespace Emby.Server.Implementations.Data
|
||||
statement.Reset();
|
||||
}
|
||||
|
||||
statement.TryBind("@ItemId", itemId.ToGuidParamValue());
|
||||
statement.TryBind("@ItemId", itemId.ToGuidBlob());
|
||||
statement.TryBind("@Name", person.Name);
|
||||
statement.TryBind("@Role", person.Role);
|
||||
statement.TryBind("@PersonType", person.Type);
|
||||
@ -5339,7 +5339,7 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
using (var statement = PrepareStatementSafe(connection, cmdText))
|
||||
{
|
||||
statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
|
||||
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
|
||||
|
||||
if (query.Type.HasValue)
|
||||
{
|
||||
@ -5383,7 +5383,7 @@ namespace Emby.Server.Implementations.Data
|
||||
using (var connection = CreateConnection())
|
||||
{
|
||||
// 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})",
|
||||
string.Join(",", _mediaStreamSaveColumns),
|
||||
@ -5393,7 +5393,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
var paramList = new List<object>();
|
||||
|
||||
paramList.Add(id.ToGuidParamValue());
|
||||
paramList.Add(id.ToGuidBlob());
|
||||
paramList.Add(stream.Index);
|
||||
paramList.Add(stream.Type.ToString());
|
||||
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)"))
|
||||
{
|
||||
statement.TryBind("@userId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@userId", userId.ToGuidBlob());
|
||||
statement.TryBind("@key", key);
|
||||
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||
statement.TryBind("@Key", key);
|
||||
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
@ -386,7 +386,7 @@ namespace Emby.Server.Implementations.Data
|
||||
var userData = new UserItemData();
|
||||
|
||||
userData.Key = reader[0].ToString();
|
||||
userData.UserId = reader[1].ReadGuid();
|
||||
userData.UserId = reader[1].ReadGuidFromBlob();
|
||||
|
||||
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)"))
|
||||
{
|
||||
statement.TryBind("@guid", user.Id.ToGuidParamValue());
|
||||
statement.TryBind("@guid", user.Id.ToGuidBlob());
|
||||
statement.TryBind("@data", serialized);
|
||||
statement.MoveNext();
|
||||
}
|
||||
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
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()))
|
||||
{
|
||||
@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
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();
|
||||
}
|
||||
}, TransactionMode);
|
||||
|
@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.Devices
|
||||
|
||||
_libraryMonitor.ReportFileSystemChangeBeginning(path);
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Devices
|
||||
public Task SaveDevice(DeviceInfo device)
|
||||
{
|
||||
var path = Path.Combine(GetDevicePath(device.Id), "device.json");
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.Devices
|
||||
public void AddCameraUpload(string deviceId, LocalFileInfo file)
|
||||
{
|
||||
var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
|
@ -308,7 +308,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<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>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.FFMpeg
|
||||
else
|
||||
{
|
||||
info = existingVersion;
|
||||
versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
|
||||
versionedDirectoryPath = _fileSystem.GetDirectoryName(info.EncoderPath);
|
||||
excludeFromDeletions.Add(versionedDirectoryPath);
|
||||
}
|
||||
}
|
||||
@ -135,7 +135,7 @@ namespace Emby.Server.Implementations.FFMpeg
|
||||
{
|
||||
EncoderPath = encoder,
|
||||
ProbePath = probe,
|
||||
Version = Path.GetFileName(Path.GetDirectoryName(probe))
|
||||
Version = Path.GetFileName(_fileSystem.GetDirectoryName(probe))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +228,8 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
_streamFactory,
|
||||
_enableDualModeSockets,
|
||||
GetRequest,
|
||||
_fileSystem);
|
||||
_fileSystem,
|
||||
_environment);
|
||||
}
|
||||
|
||||
private IHttpRequest GetRequest(HttpListenerContext httpContext)
|
||||
@ -452,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
var date = DateTime.Now;
|
||||
var httpRes = httpReq.Response;
|
||||
bool enableLog = false;
|
||||
bool logHeaders = false;
|
||||
string urlToLog = null;
|
||||
string remoteIp = null;
|
||||
|
||||
@ -490,13 +492,14 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
var urlString = url.OriginalString;
|
||||
enableLog = EnableLogging(urlString, localPath);
|
||||
urlToLog = urlString;
|
||||
logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
|
||||
if (enableLog)
|
||||
{
|
||||
urlToLog = GetUrlToLog(urlString);
|
||||
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) ||
|
||||
@ -611,7 +614,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
|
||||
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>
|
||||
/// Pres the process optimized result.
|
||||
/// </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)
|
||||
{
|
||||
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);
|
||||
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
|
||||
if (IsNotModified(requestContext, cacheKey, lastDateModified, 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;
|
||||
}
|
||||
@ -673,11 +670,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// <summary>
|
||||
/// Adds the caching responseHeaders.
|
||||
/// </summary>
|
||||
/// <param name="responseHeaders">The responseHeaders.</param>
|
||||
/// <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)
|
||||
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, bool noCache)
|
||||
{
|
||||
// Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
|
||||
// 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");
|
||||
}
|
||||
|
||||
if (cacheDuration.HasValue)
|
||||
if (!noCache && cacheDuration.HasValue)
|
||||
{
|
||||
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";
|
||||
}
|
||||
@ -701,18 +694,15 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
|
||||
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the expires header.
|
||||
/// </summary>
|
||||
/// <param name="responseHeaders">The responseHeaders.</param>
|
||||
/// <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)
|
||||
private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration, bool noCache)
|
||||
{
|
||||
if (cacheDuration.HasValue)
|
||||
if (!noCache && cacheDuration.HasValue)
|
||||
{
|
||||
responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.Services;
|
||||
using SocketHttpListener.Net;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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>
|
||||
@ -32,12 +43,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="endPoint">The end point.</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 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.Net;
|
||||
using MediaBrowser.Model.Services;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Text;
|
||||
using SocketHttpListener.Primitives;
|
||||
|
||||
@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
|
||||
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;
|
||||
_certificate = certificate;
|
||||
@ -44,6 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||
_enableDualMode = enableDualMode;
|
||||
_httpRequestFactory = httpRequestFactory;
|
||||
_fileSystem = fileSystem;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
|
||||
@ -56,7 +59,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||
public void Start(IEnumerable<string> urlPrefixes)
|
||||
{
|
||||
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;
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Services;
|
||||
using SocketHttpListener.Net;
|
||||
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
|
||||
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
|
||||
@ -66,6 +67,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
|
||||
_response.AddHeader(name, value);
|
||||
}
|
||||
|
||||
public QueryParamCollection Headers
|
||||
{
|
||||
get
|
||||
{
|
||||
return _response.Headers;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetHeader(string name)
|
||||
{
|
||||
return _response.Headers[name];
|
||||
|
@ -34,8 +34,9 @@ namespace Emby.Server.Implementations.IO
|
||||
|
||||
public event EventHandler<EventArgs> Completed;
|
||||
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);
|
||||
Path = path;
|
||||
@ -47,6 +48,7 @@ namespace Emby.Server.Implementations.IO
|
||||
Logger = logger;
|
||||
_timerFactory = timerFactory;
|
||||
_environmentInfo = environmentInfo;
|
||||
_libraryManager = libraryManager1;
|
||||
AddPath(path);
|
||||
}
|
||||
|
||||
@ -235,6 +237,12 @@ namespace Emby.Server.Implementations.IO
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only try to open video files
|
||||
if (!_libraryManager.IsVideoFile(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var data = _fileSystem.GetFileSystemInfo(path);
|
||||
|
@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -1197,6 +1197,7 @@ namespace Emby.Server.Implementations.Library
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Info("Post-scan task cancelled: {0}", task.GetType().Name);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -26,6 +26,8 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
|
||||
while (yearNumber < maxYear)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var year = _libraryManager.GetYear(yearNumber);
|
||||
@ -35,7 +37,7 @@ namespace Emby.Server.Implementations.Library.Validators
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Don't clutter the log
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Notifications
|
||||
}
|
||||
|
||||
clauses.Add("UserId=?");
|
||||
paramList.Add(query.UserId.ToGuidParamValue());
|
||||
paramList.Add(query.UserId.ToGuidBlob());
|
||||
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@IsRead", false);
|
||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||
|
||||
var levels = new List<NotificationLevel>();
|
||||
|
||||
@ -159,8 +159,8 @@ namespace Emby.Server.Implementations.Notifications
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = reader[0].ReadGuid().ToString("N"),
|
||||
UserId = reader[1].ReadGuid().ToString("N"),
|
||||
Id = reader[0].ReadGuidFromBlob().ToString("N"),
|
||||
UserId = reader[1].ReadGuidFromBlob().ToString("N"),
|
||||
Date = reader[2].ReadDateTime(),
|
||||
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)"))
|
||||
{
|
||||
statement.TryBind("@Id", notification.Id.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", notification.UserId.ToGuidParamValue());
|
||||
statement.TryBind("@Id", notification.Id.ToGuidBlob());
|
||||
statement.TryBind("@UserId", notification.UserId.ToGuidBlob());
|
||||
statement.TryBind("@Date", notification.Date.ToDateTimeParamValue());
|
||||
statement.TryBind("@Name", notification.Name);
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@IsRead", isRead);
|
||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||
|
||||
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"))
|
||||
{
|
||||
statement.TryBind("@IsRead", isRead);
|
||||
statement.TryBind("@UserId", userId.ToGuidParamValue());
|
||||
statement.TryBind("@UserId", userId.ToGuidBlob());
|
||||
|
||||
foreach (var id in notificationIdList)
|
||||
{
|
||||
statement.Reset();
|
||||
|
||||
statement.TryBind("@Id", id.ToGuidParamValue());
|
||||
statement.TryBind("@Id", id.ToGuidBlob());
|
||||
|
||||
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)"))
|
||||
{
|
||||
statement.TryBind("@Id", info.Id.ToGuidParamValue());
|
||||
statement.TryBind("@Id", info.Id.ToGuidBlob());
|
||||
statement.TryBind("@AccessToken", info.AccessToken);
|
||||
|
||||
statement.TryBind("@DeviceId", info.DeviceId);
|
||||
@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.Security
|
||||
|
||||
using (var statement = connection.PrepareStatement(commandText))
|
||||
{
|
||||
statement.BindParameters["@Id"].Bind(id.ToGuidParamValue());
|
||||
statement.BindParameters["@Id"].Bind(id.ToGuidBlob());
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
@ -275,7 +275,7 @@ namespace Emby.Server.Implementations.Security
|
||||
{
|
||||
var info = new AuthenticationInfo
|
||||
{
|
||||
Id = reader[0].ReadGuid().ToString("N"),
|
||||
Id = reader[0].ReadGuidFromBlob().ToString("N"),
|
||||
AccessToken = reader[1].ToString()
|
||||
};
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Social
|
||||
var commandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (?, ?, ?, ?)";
|
||||
|
||||
db.Execute(commandText,
|
||||
info.Id.ToGuidParamValue(),
|
||||
info.Id.ToGuidBlob(),
|
||||
info.ItemId,
|
||||
info.UserId,
|
||||
info.ExpirationDate.ToDateTimeParamValue());
|
||||
@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Social
|
||||
var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
|
||||
|
||||
var paramList = new List<object>();
|
||||
paramList.Add(id.ToGuidParamValue());
|
||||
paramList.Add(id.ToGuidBlob());
|
||||
|
||||
foreach (var row in connection.Query(commandText, paramList.ToArray()))
|
||||
{
|
||||
@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Social
|
||||
{
|
||||
var info = new SocialShareInfo();
|
||||
|
||||
info.Id = reader[0].ReadGuid().ToString("N");
|
||||
info.Id = reader[0].ReadGuidFromBlob().ToString("N");
|
||||
info.ItemId = reader[1].ToString();
|
||||
info.UserId = reader[2].ToString();
|
||||
info.ExpirationDate = reader[3].ReadDateTime();
|
||||
|
@ -664,9 +664,19 @@ namespace Emby.Server.Implementations.Updates
|
||||
// Remove it the quick way for now
|
||||
_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);
|
||||
|
||||
|
@ -3,5 +3,5 @@
|
||||
<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="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>
|
@ -54,8 +54,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server.Core", "Emby.Se
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
|
||||
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}"
|
||||
EndProject
|
||||
Global
|
||||
|
@ -82,10 +82,6 @@
|
||||
<Compile Include="Reports\Model\ReportRow.cs" />
|
||||
<Compile Include="Reports\ReportRequests.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="StartupWizardService.cs" />
|
||||
<Compile Include="Subtitles\SubtitleService.cs" />
|
||||
|
@ -671,12 +671,15 @@ namespace MediaBrowser.Api.Playback
|
||||
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)
|
||||
{
|
||||
Request = request,
|
||||
RequestedUrl = url,
|
||||
UserAgent = Request.UserAgent,
|
||||
EnableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params)
|
||||
EnableDlnaHeaders = enableDlnaHeaders
|
||||
};
|
||||
|
||||
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
|
||||
|
@ -879,7 +879,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
// Add resolution params, if specified
|
||||
if (!hasGraphicalSubs)
|
||||
{
|
||||
args += EncodingHelper.GetOutputSizeParam(state, codec, EnableCopyTs(state));
|
||||
args += EncodingHelper.GetOutputSizeParam(state, codec, true);
|
||||
}
|
||||
|
||||
// This is for internal graphical subs
|
||||
@ -891,7 +891,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
//args += " -flags -global_header";
|
||||
}
|
||||
|
||||
if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
{
|
||||
args += " -copyts";
|
||||
}
|
||||
@ -901,13 +901,9 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
args += " -vsync " + state.OutputVideoSync;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
args += EncodingHelper.GetOutputFFlags(state);
|
||||
|
||||
private bool EnableCopyTs(StreamState state)
|
||||
{
|
||||
//return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
|
||||
return true;
|
||||
return args;
|
||||
}
|
||||
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
|
@ -124,6 +124,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
args += " -vsync " + state.OutputVideoSync;
|
||||
}
|
||||
|
||||
args += EncodingHelper.GetOutputFFlags(state);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,8 @@ namespace MediaBrowser.Api.Reports
|
||||
Tracks,
|
||||
EpisodeSeries,
|
||||
EpisodeSeason,
|
||||
AudioAlbumArtist,
|
||||
EpisodeNumber,
|
||||
AudioAlbumArtist,
|
||||
MusicArtist,
|
||||
AudioAlbum,
|
||||
Locked,
|
||||
|
@ -148,6 +148,11 @@ namespace MediaBrowser.Api.Reports
|
||||
/// <returns> The localized header. </returns>
|
||||
protected static string GetLocalizedHeader(HeaderMetadata internalHeader)
|
||||
{
|
||||
if (internalHeader == HeaderMetadata.EpisodeNumber)
|
||||
{
|
||||
return "Episode";
|
||||
}
|
||||
|
||||
string headerName = "";
|
||||
if (internalHeader != HeaderMetadata.None)
|
||||
{
|
||||
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Reports
|
||||
HeaderMetadata.Series,
|
||||
HeaderMetadata.Season,
|
||||
HeaderMetadata.SeasonNumber,
|
||||
HeaderMetadata.DateAdded,
|
||||
HeaderMetadata.DateAdded,
|
||||
HeaderMetadata.Year,
|
||||
HeaderMetadata.Genres
|
||||
};
|
||||
@ -269,10 +269,11 @@ namespace MediaBrowser.Api.Reports
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
HeaderMetadata.Name,
|
||||
HeaderMetadata.Name,
|
||||
HeaderMetadata.EpisodeSeries,
|
||||
HeaderMetadata.Season,
|
||||
HeaderMetadata.DateAdded,
|
||||
HeaderMetadata.EpisodeNumber,
|
||||
HeaderMetadata.DateAdded,
|
||||
HeaderMetadata.ReleaseDate,
|
||||
HeaderMetadata.Year,
|
||||
HeaderMetadata.Genres,
|
||||
@ -450,6 +451,12 @@ namespace MediaBrowser.Api.Reports
|
||||
internalHeader = HeaderMetadata.Season;
|
||||
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:
|
||||
option.Column = (i, r) => this.GetListAsString(i.Studios);
|
||||
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>
|
||||
ImageFormat[] SupportedOutputFormats { get; }
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input path.</param>
|
||||
@ -56,5 +50,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
||||
bool SupportsImageEncoding { get; }
|
||||
|
||||
ImageSize GetImageSize(string path);
|
||||
}
|
||||
}
|
||||
|
@ -112,5 +112,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
|
||||
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 New Issue
Block a user