Merge pull request #2633 from MediaBrowser/beta

Beta
This commit is contained in:
Luke 2017-05-12 14:19:33 -04:00 committed by GitHub
commit 1a6ee3d48a
133 changed files with 1919 additions and 1495 deletions

View file

@ -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);
}

View file

@ -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)

View file

@ -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",

View file

@ -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)

View file

@ -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" />

View file

@ -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 = "",
}
};
}
}
}

View file

@ -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
}
};

View file

@ -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 = "",
}
};
}
}
}

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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)

View file

@ -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);

View 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>

View 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);
}
}
}
}

View 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;
}
}
}

View 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
//

View 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; }
}
}
}

View 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;
}
}
}

View 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);
}
}
}
}

View 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>

View file

@ -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;

View file

@ -57,6 +57,11 @@ namespace Emby.Drawing
get { return false; }
}
public ImageSize GetImageSize(string path)
{
throw new NotImplementedException();
}
public void Dispose()
{
}

View file

@ -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
};
}

View file

@ -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);
}

View file

@ -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": {

View file

@ -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++;

View file

@ -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)
{

View file

@ -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);

View file

@ -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())
{

View file

@ -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
{

View file

@ -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++;

View file

@ -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);

View file

@ -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)
{

View file

@ -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);

View file

@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.Devices
_libraryMonitor.ReportFileSystemChangeBeginning(path);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
try
{

View file

@ -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)
{

View file

@ -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>

View file

@ -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))
};
}
}

View file

@ -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);
}
}
}

View file

@ -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");
}

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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];

View file

@ -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);

View file

@ -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);
}

View file

@ -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)
{

View file

@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Validators
catch (OperationCanceledException)
{
// Don't clutter the log
break;
throw;
}
catch (Exception ex)
{

View file

@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
catch (OperationCanceledException)
{
// Don't clutter the log
break;
throw;
}
catch (Exception ex)
{

View file

@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
catch (OperationCanceledException)
{
// Don't clutter the log
break;
throw;
}
catch (Exception ex)
{

View file

@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
catch (OperationCanceledException)
{
// Don't clutter the log
break;
throw;
}
catch (Exception ex)
{

View file

@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
catch (OperationCanceledException)
{
// Don't clutter the log
break;
throw;
}
catch (Exception ex)
{

View file

@ -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)
{

View file

@ -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();
}

View file

@ -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()
};

View file

@ -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();

View file

@ -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);

View file

@ -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>

View file

@ -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

View file

@ -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" />

View file

@ -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);

View file

@ -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)

View file

@ -124,6 +124,8 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -vsync " + state.OutputVideoSync;
}
args += EncodingHelper.GetOutputFFlags(state);
return args;
}

View file

@ -35,7 +35,8 @@ namespace MediaBrowser.Api.Reports
Tracks,
EpisodeSeries,
EpisodeSeason,
AudioAlbumArtist,
EpisodeNumber,
AudioAlbumArtist,
MusicArtist,
AudioAlbum,
Locked,

View file

@ -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)
{

View file

@ -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());

View file

@ -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
}
}

View file

@ -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);
}
}
}

View file

@ -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; }
}
}

View file

@ -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; }
}
}

View file

@ -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);
}
}

View file

@ -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