diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 96995c315..542fbaa31 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using MediaBrowser.Model.Drawing;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@@ -20,5 +21,18 @@ namespace MediaBrowser.Controller.Entities
return Model.Entities.MediaType.Photo;
}
}
+
+ public int? Width { get; set; }
+ public int? Height { get; set; }
+ public string CameraManufacturer { get; set; }
+ public string CameraModel { get; set; }
+ public string Software { get; set; }
+ public double? ExposureTime { get; set; }
+ public double? FocalLength { get; set; }
+
+ public ImageOrientation? Orientation { get; set; }
+
+ public double? Aperture { get; set; }
+ public double? ShutterSpeed { get; set; }
}
}
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index 99c4a4629..aaa44b009 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -287,6 +287,9 @@
Drawing\DrawingUtils.cs
+
+ Drawing\ImageOrientation.cs
+
Drawing\ImageOutputFormat.cs
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 919f676f1..6046d3606 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -250,6 +250,9 @@
Drawing\DrawingUtils.cs
+
+ Drawing\ImageOrientation.cs
+
Drawing\ImageOutputFormat.cs
diff --git a/MediaBrowser.Model/Drawing/ImageOrientation.cs b/MediaBrowser.Model/Drawing/ImageOrientation.cs
new file mode 100644
index 000000000..3fdfaf7bf
--- /dev/null
+++ b/MediaBrowser.Model/Drawing/ImageOrientation.cs
@@ -0,0 +1,16 @@
+
+namespace MediaBrowser.Model.Drawing
+{
+ public enum ImageOrientation
+ {
+ None = 0,
+ TopLeft = 1,
+ TopRight = 2,
+ BottomRight = 3,
+ BottomLeft = 4,
+ LeftTop = 5,
+ RightTop = 6,
+ RightBottom = 7,
+ LeftBottom = 8,
+ }
+}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index bb93fa687..c3f02236f 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -81,6 +81,7 @@
+
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 757c7a753..b69a4bc2f 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -56,6 +56,9 @@
..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
+
+ ..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll
+
@@ -64,6 +67,10 @@
+
+ ..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll
+ True
+
@@ -142,8 +149,6 @@
-
-
diff --git a/MediaBrowser.Providers/Photos/ExifReader.cs b/MediaBrowser.Providers/Photos/ExifReader.cs
deleted file mode 100644
index 8526a7f2a..000000000
--- a/MediaBrowser.Providers/Photos/ExifReader.cs
+++ /dev/null
@@ -1,613 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Text;
-
-namespace MediaBrowser.Providers.Photos
-{
- ///
- /// A class for reading Exif data from a JPEG file. The file will be open for reading for as long as the class exists.
- ///
- ///
- public class ExifReader : IDisposable
- {
- private readonly FileStream fileStream = null;
- private readonly BinaryReader reader = null;
-
- ///
- /// The catalogue of tag ids and their absolute offsets within the
- /// file
- ///
- private Dictionary catalogue;
-
- ///
- /// Indicates whether to read data using big or little endian byte aligns
- ///
- private bool isLittleEndian;
-
- ///
- /// The position in the filestream at which the TIFF header starts
- ///
- private long tiffHeaderStart;
-
- public ExifReader(string fileName)
- {
- // JPEG encoding uses big endian (i.e. Motorola) byte aligns. The TIFF encoding
- // found later in the document will specify the byte aligns used for the
- // rest of the document.
- isLittleEndian = false;
-
- try
- {
- // Open the file in a stream
- fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
- reader = new BinaryReader(fileStream);
-
- // Make sure the file's a JPEG.
- if (ReadUShort() != 0xFFD8)
- throw new Exception("File is not a valid JPEG");
-
- // Scan to the start of the Exif content
- ReadToExifStart();
-
- // Create an index of all Exif tags found within the document
- CreateTagIndex();
- }
- catch (Exception)
- {
- // If instantiation fails, make sure there's no mess left behind
- Dispose();
-
- throw;
- }
- }
-
- #region TIFF methods
-
- ///
- /// Returns the length (in bytes) per component of the specified TIFF data type
- ///
- ///
- private byte GetTIFFFieldLength(ushort tiffDataType)
- {
- switch (tiffDataType)
- {
- case 1:
- case 2:
- case 6:
- return 1;
- case 3:
- case 8:
- return 2;
- case 4:
- case 7:
- case 9:
- case 11:
- return 4;
- case 5:
- case 10:
- case 12:
- return 8;
- default:
- throw new Exception(string.Format("Unknown TIFF datatype: {0}", tiffDataType));
- }
- }
-
- #endregion
-
- #region Methods for reading data directly from the filestream
-
- ///
- /// Gets a 2 byte unsigned integer from the file
- ///
- ///
- private ushort ReadUShort()
- {
- return ToUShort(ReadBytes(2));
- }
-
- ///
- /// Gets a 4 byte unsigned integer from the file
- ///
- ///
- private uint ReadUint()
- {
- return ToUint(ReadBytes(4));
- }
-
- private string ReadString(int chars)
- {
- return Encoding.ASCII.GetString(ReadBytes(chars));
- }
-
- private byte[] ReadBytes(int byteCount)
- {
- return reader.ReadBytes(byteCount);
- }
-
- ///
- /// Reads some bytes from the specified TIFF offset
- ///
- ///
- ///
- ///
- private byte[] ReadBytes(ushort tiffOffset, int byteCount)
- {
- // Keep the current file offset
- long originalOffset = fileStream.Position;
-
- // Move to the TIFF offset and retrieve the data
- fileStream.Seek(tiffOffset + tiffHeaderStart, SeekOrigin.Begin);
-
- byte[] data = reader.ReadBytes(byteCount);
-
- // Restore the file offset
- fileStream.Position = originalOffset;
-
- return data;
- }
-
- #endregion
-
- #region Data conversion methods for interpreting datatypes from a byte array
-
- ///
- /// Converts 2 bytes to a ushort using the current byte aligns
- ///
- ///
- private ushort ToUShort(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToUInt16(data, 0);
- }
-
- ///
- /// Converts 8 bytes to an unsigned rational using the current byte aligns.
- ///
- ///
- ///
- ///
- private double ToURational(byte[] data)
- {
- var numeratorData = new byte[4];
- var denominatorData = new byte[4];
-
- Array.Copy(data, numeratorData, 4);
- Array.Copy(data, 4, denominatorData, 0, 4);
-
- uint numerator = ToUint(numeratorData);
- uint denominator = ToUint(denominatorData);
-
- return numerator / (double)denominator;
- }
-
- ///
- /// Converts 8 bytes to a signed rational using the current byte aligns.
- ///
- ///
- /// A TIFF rational contains 2 4-byte integers, the first of which is
- /// the numerator, and the second of which is the denominator.
- ///
- ///
- ///
- private double ToRational(byte[] data)
- {
- var numeratorData = new byte[4];
- var denominatorData = new byte[4];
-
- Array.Copy(data, numeratorData, 4);
- Array.Copy(data, 4, denominatorData, 0, 4);
-
- int numerator = ToInt(numeratorData);
- int denominator = ToInt(denominatorData);
-
- return numerator / (double)denominator;
- }
-
- ///
- /// Converts 4 bytes to a uint using the current byte aligns
- ///
- ///
- private uint ToUint(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToUInt32(data, 0);
- }
-
- ///
- /// Converts 4 bytes to an int using the current byte aligns
- ///
- ///
- private int ToInt(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToInt32(data, 0);
- }
-
- private double ToDouble(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToDouble(data, 0);
- }
-
- private float ToSingle(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToSingle(data, 0);
- }
-
- private short ToShort(byte[] data)
- {
- if (isLittleEndian != BitConverter.IsLittleEndian)
- Array.Reverse(data);
-
- return BitConverter.ToInt16(data, 0);
- }
-
- private sbyte ToSByte(byte[] data)
- {
- // An sbyte should just be a byte with an offset range.
- return (sbyte)(data[0] - byte.MaxValue);
- }
-
- ///
- /// Retrieves an array from a byte array using the supplied converter
- /// to read each individual element from the supplied byte array
- ///
- ///
- ///
- ///
- ///
- private Array GetArray(byte[] data, int elementLengthBytes, ConverterMethod converter)
- {
- Array convertedData = Array.CreateInstance(typeof(T), data.Length / elementLengthBytes);
-
- var buffer = new byte[elementLengthBytes];
-
- // Read each element from the array
- for (int elementCount = 0; elementCount < data.Length / elementLengthBytes; elementCount++)
- {
- // Place the data for the current element into the buffer
- Array.Copy(data, elementCount * elementLengthBytes, buffer, 0, elementLengthBytes);
-
- // Process the data and place it into the output array
- convertedData.SetValue(converter(buffer), elementCount);
- }
-
- return convertedData;
- }
-
- ///
- /// A delegate used to invoke any of the data conversion methods
- ///
- ///
- ///
- private delegate T ConverterMethod(byte[] data);
-
- #endregion
-
- #region Stream seek methods - used to get to locations within the JPEG
-
- ///
- /// Scans to the Exif block
- ///
- private void ReadToExifStart()
- {
- // The file has a number of blocks (Exif/JFIF), each of which
- // has a tag number followed by a length. We scan the document until the required tag (0xFFE1)
- // is found. All tags start with FF, so a non FF tag indicates an error.
-
- // Get the next tag.
- byte markerStart;
- byte markerNumber = 0;
- while (((markerStart = reader.ReadByte()) == 0xFF) && (markerNumber = reader.ReadByte()) != 0xE1)
- {
- // Get the length of the data.
- ushort dataLength = ReadUShort();
-
- // Jump to the end of the data (note that the size field includes its own size)!
- reader.BaseStream.Seek(dataLength - 2, SeekOrigin.Current);
- }
-
- // It's only success if we found the 0xFFE1 marker
- if (markerStart != 0xFF || markerNumber != 0xE1)
- throw new Exception("Could not find Exif data block");
- }
-
- ///
- /// Reads through the Exif data and builds an index of all Exif tags in the document
- ///
- ///
- private void CreateTagIndex()
- {
- // The next 4 bytes are the size of the Exif data.
- ReadUShort();
-
- // Next is the Exif data itself. It starts with the ASCII "Exif" followed by 2 zero bytes.
- if (ReadString(4) != "Exif")
- throw new Exception("Exif data not found");
-
- // 2 zero bytes
- if (ReadUShort() != 0)
- throw new Exception("Malformed Exif data");
-
- // We're now into the TIFF format
- tiffHeaderStart = reader.BaseStream.Position;
-
- // What byte align will be used for the TIFF part of the document? II for Intel, MM for Motorola
- isLittleEndian = ReadString(2) == "II";
-
- // Next 2 bytes are always the same.
- if (ReadUShort() != 0x002A)
- throw new Exception("Error in TIFF data");
-
- // Get the offset to the IFD (image file directory)
- uint ifdOffset = ReadUint();
-
- // Note that this offset is from the first byte of the TIFF header. Jump to the IFD.
- fileStream.Position = ifdOffset + tiffHeaderStart;
-
- // Catalogue this first IFD (there will be another IFD)
- CatalogueIFD();
-
- // There's more data stored in the subifd, the offset to which is found in tag 0x8769.
- // As with all TIFF offsets, it will be relative to the first byte of the TIFF header.
- uint offset;
- if (!GetTagValue(0x8769, out offset))
- throw new Exception("Unable to locate Exif data");
-
- // Jump to the exif SubIFD
- fileStream.Position = offset + tiffHeaderStart;
-
- // Add the subIFD to the catalogue too
- CatalogueIFD();
-
- // Go to the GPS IFD and catalogue that too. It's an optional
- // section.
- if (GetTagValue(0x8825, out offset))
- {
- // Jump to the GPS SubIFD
- fileStream.Position = offset + tiffHeaderStart;
-
- // Add the subIFD to the catalogue too
- CatalogueIFD();
- }
- }
-
- #endregion
-
- #region Exif data catalog and retrieval methods
-
- public bool GetTagValue(ExifTags tag, out T result)
- {
- return GetTagValue((ushort)tag, out result);
- }
-
- ///
- /// Retrieves an Exif value with the requested tag ID
- ///
- ///
- ///
- ///
- public bool GetTagValue(ushort tagID, out T result)
- {
- ushort tiffDataType;
- uint numberOfComponents;
- byte[] tagData = GetTagBytes(tagID, out tiffDataType, out numberOfComponents);
-
- if (tagData == null)
- {
- result = default(T);
- return false;
- }
-
- byte fieldLength = GetTIFFFieldLength(tiffDataType);
-
- // Convert the data to the appropriate datatype. Note the weird boxing via object.
- // The compiler doesn't like it otherwise.
- switch (tiffDataType)
- {
- case 1:
- // unsigned byte
- if (numberOfComponents == 1)
- result = (T)(object)tagData[0];
- else
- result = (T)(object)tagData;
- return true;
- case 2:
- // ascii string
- string str = Encoding.ASCII.GetString(tagData);
-
- // There may be a null character within the string
- int nullCharIndex = str.IndexOf('\0');
- if (nullCharIndex != -1)
- str = str.Substring(0, nullCharIndex);
-
- // Special processing for dates.
- if (typeof(T) == typeof(DateTime))
- {
- result =
- (T)(object)DateTime.ParseExact(str, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture);
- return true;
- }
-
- result = (T)(object)str;
- return true;
- case 3:
- // unsigned short
- if (numberOfComponents == 1)
- result = (T)(object)ToUShort(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToUShort);
- return true;
- case 4:
- // unsigned long
- if (numberOfComponents == 1)
- result = (T)(object)ToUint(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToUint);
- return true;
- case 5:
- // unsigned rational
- if (numberOfComponents == 1)
- result = (T)(object)ToURational(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToURational);
- return true;
- case 6:
- // signed byte
- if (numberOfComponents == 1)
- result = (T)(object)ToSByte(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToSByte);
- return true;
- case 7:
- // undefined. Treat it as an unsigned integer.
- if (numberOfComponents == 1)
- result = (T)(object)ToUint(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToUint);
- return true;
- case 8:
- // Signed short
- if (numberOfComponents == 1)
- result = (T)(object)ToShort(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToShort);
- return true;
- case 9:
- // Signed long
- if (numberOfComponents == 1)
- result = (T)(object)ToInt(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToInt);
- return true;
- case 10:
- // signed rational
- if (numberOfComponents == 1)
- result = (T)(object)ToRational(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToRational);
- return true;
- case 11:
- // single float
- if (numberOfComponents == 1)
- result = (T)(object)ToSingle(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToSingle);
- return true;
- case 12:
- // double float
- if (numberOfComponents == 1)
- result = (T)(object)ToDouble(tagData);
- else
- result = (T)(object)GetArray(tagData, fieldLength, ToDouble);
- return true;
- default:
- throw new Exception(string.Format("Unknown TIFF datatype: {0}", tiffDataType));
- }
- }
-
- ///
- /// Gets the data in the specified tag ID, starting from before the IFD block.
- ///
- ///
- /// The number of items which make up the data item - i.e. for a string, this will be the
- /// number of characters in the string
- ///
- private byte[] GetTagBytes(ushort tagID, out ushort tiffDataType, out uint numberOfComponents)
- {
- // Get the tag's offset from the catalogue and do some basic error checks
- if (fileStream == null || reader == null || catalogue == null || !catalogue.ContainsKey(tagID))
- {
- tiffDataType = 0;
- numberOfComponents = 0;
- return null;
- }
-
- long tagOffset = catalogue[tagID];
-
- // Jump to the TIFF offset
- fileStream.Position = tagOffset;
-
- // Read the tag number from the file
- ushort currentTagID = ReadUShort();
-
- if (currentTagID != tagID)
- throw new Exception("Tag number not at expected offset");
-
- // Read the offset to the Exif IFD
- tiffDataType = ReadUShort();
- numberOfComponents = ReadUint();
- byte[] tagData = ReadBytes(4);
-
- // If the total space taken up by the field is longer than the
- // 2 bytes afforded by the tagData, tagData will contain an offset
- // to the actual data.
- var dataSize = (int)(numberOfComponents * GetTIFFFieldLength(tiffDataType));
-
- if (dataSize > 4)
- {
- ushort offsetAddress = ToUShort(tagData);
- return ReadBytes(offsetAddress, dataSize);
- }
-
- // The value is stored in the tagData starting from the left
- Array.Resize(ref tagData, dataSize);
-
- return tagData;
- }
-
- ///
- /// Records all Exif tags and their offsets within
- /// the file from the current IFD
- ///
- private void CatalogueIFD()
- {
- if (catalogue == null)
- catalogue = new Dictionary();
-
- // Assume we're just before the IFD.
-
- // First 2 bytes is the number of entries in this IFD
- ushort entryCount = ReadUShort();
-
- for (ushort currentEntry = 0; currentEntry < entryCount; currentEntry++)
- {
- ushort currentTagNumber = ReadUShort();
-
- // Record this in the catalogue
- catalogue[currentTagNumber] = fileStream.Position - 2;
-
- // Go to the end of this item (10 bytes, as each entry is 12 bytes long)
- reader.BaseStream.Seek(10, SeekOrigin.Current);
- }
- }
-
- #endregion
-
- #region IDisposable Members
-
- public void Dispose()
- {
- // Make sure the file handle is released
- if (reader != null)
- reader.Close();
- if (fileStream != null)
- fileStream.Close();
- }
-
- #endregion
- }
-}
diff --git a/MediaBrowser.Providers/Photos/ExifTags.cs b/MediaBrowser.Providers/Photos/ExifTags.cs
deleted file mode 100644
index 39e153f2e..000000000
--- a/MediaBrowser.Providers/Photos/ExifTags.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-
-namespace MediaBrowser.Providers.Photos
-{
- ///
- /// All exif tags as per the Exif standard 2.2, JEITA CP-2451
- ///
- public enum ExifTags : ushort
- {
- // IFD0 items
- ImageWidth = 0x100,
- ImageLength = 0x101,
- BitsPerSample = 0x102,
- Compression = 0x103,
- PhotometricInterpretation = 0x106,
- ImageDescription = 0x10E,
- Make = 0x10F,
- Model = 0x110,
- StripOffsets = 0x111,
- Orientation = 0x112,
- SamplesPerPixel = 0x115,
- RowsPerStrip = 0x116,
- StripByteCounts = 0x117,
- XResolution = 0x11A,
- YResolution = 0x11B,
- PlanarConfiguration = 0x11C,
- ResolutionUnit = 0x128,
- TransferFunction = 0x12D,
- Software = 0x131,
- DateTime = 0x132,
- Artist = 0x13B,
- WhitePoint = 0x13E,
- PrimaryChromaticities = 0x13F,
- JPEGInterchangeFormat = 0x201,
- JPEGInterchangeFormatLength = 0x202,
- YCbCrCoefficients = 0x211,
- YCbCrSubSampling = 0x212,
- YCbCrPositioning = 0x213,
- ReferenceBlackWhite = 0x214,
- Copyright = 0x8298,
-
- // SubIFD items
- ExposureTime = 0x829A,
- FNumber = 0x829D,
- ExposureProgram = 0x8822,
- SpectralSensitivity = 0x8824,
- ISOSpeedRatings = 0x8827,
- OECF = 0x8828,
- ExifVersion = 0x9000,
- DateTimeOriginal = 0x9003,
- DateTimeDigitized = 0x9004,
- ComponentsConfiguration = 0x9101,
- CompressedBitsPerPixel = 0x9102,
- ShutterSpeedValue = 0x9201,
- ApertureValue = 0x9202,
- BrightnessValue = 0x9203,
- ExposureBiasValue = 0x9204,
- MaxApertureValue = 0x9205,
- SubjectDistance = 0x9206,
- MeteringMode = 0x9207,
- LightSource = 0x9208,
- Flash = 0x9209,
- FocalLength = 0x920A,
- SubjectArea = 0x9214,
- MakerNote = 0x927C,
- UserComment = 0x9286,
- SubsecTime = 0x9290,
- SubsecTimeOriginal = 0x9291,
- SubsecTimeDigitized = 0x9292,
- FlashpixVersion = 0xA000,
- ColorSpace = 0xA001,
- PixelXDimension = 0xA002,
- PixelYDimension = 0xA003,
- RelatedSoundFile = 0xA004,
- FlashEnergy = 0xA20B,
- SpatialFrequencyResponse = 0xA20C,
- FocalPlaneXResolution = 0xA20E,
- FocalPlaneYResolution = 0xA20F,
- FocalPlaneResolutionUnit = 0xA210,
- SubjectLocation = 0xA214,
- ExposureIndex = 0xA215,
- SensingMethod = 0xA217,
- FileSource = 0xA300,
- SceneType = 0xA301,
- CFAPattern = 0xA302,
- CustomRendered = 0xA401,
- ExposureMode = 0xA402,
- WhiteBalance = 0xA403,
- DigitalZoomRatio = 0xA404,
- FocalLengthIn35mmFilm = 0xA405,
- SceneCaptureType = 0xA406,
- GainControl = 0xA407,
- Contrast = 0xA408,
- Saturation = 0xA409,
- Sharpness = 0xA40A,
- DeviceSettingDescription = 0xA40B,
- SubjectDistanceRange = 0xA40C,
- ImageUniqueID = 0xA420,
-
- // GPS subifd items
- GPSVersionID = 0x0,
- GPSLatitudeRef = 0x1,
- GPSLatitude = 0x2,
- GPSLongitudeRef = 0x3,
- GPSLongitude = 0x4,
- GPSAltitudeRef = 0x5,
- GPSAltitude = 0x6,
- GPSTimeStamp = 0x7,
- GPSSatellites = 0x8,
- GPSStatus = 0x9,
- GPSMeasureMode = 0xA,
- GPSDOP = 0xB,
- GPSSpeedRef = 0xC,
- GPSSpeed = 0xD,
- GPSTrackRef = 0xE,
- GPSTrack = 0xF,
- GPSImgDirectionRef = 0x10,
- GPSImgDirection = 0x11,
- GPSMapDatum = 0x12,
- GPSDestLatitudeRef = 0x13,
- GPSDestLatitude = 0x14,
- GPSDestLongitudeRef = 0x15,
- GPSDestLongitude = 0x16,
- GPSDestBearingRef = 0x17,
- GPSDestBearing = 0x18,
- GPSDestDistanceRef = 0x19,
- GPSDestDistance = 0x1A,
- GPSProcessingMethod = 0x1B,
- GPSAreaInformation = 0x1C,
- GPSDateStamp = 0x1D,
- GPSDifferential = 0x1E
- }
-}
diff --git a/MediaBrowser.Providers/Photos/PhotoHelper.cs b/MediaBrowser.Providers/Photos/PhotoHelper.cs
index a5ce6f81f..2334c792e 100644
--- a/MediaBrowser.Providers/Photos/PhotoHelper.cs
+++ b/MediaBrowser.Providers/Photos/PhotoHelper.cs
@@ -1,25 +1,10 @@
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Collections.Generic;
+using System;
using System.Text;
namespace MediaBrowser.Providers.Photos
{
public static class PhotoHelper
{
- public static List ShuffleList(List list)
- {
- var rnd = new Random(DateTime.Now.Second);
- for (var i = 1; i < list.Count; i++)
- {
- var pos = rnd.Next(i + 1);
- var x = list[i];
- list[i] = list[pos];
- list[pos] = x;
- }
- return list;
- }
-
public static string Dec2Frac(double dbl)
{
char neg = ' ';
diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs
index c0f53e0c3..01d36b541 100644
--- a/MediaBrowser.Providers/Photos/PhotoProvider.cs
+++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs
@@ -5,9 +5,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
-using System.Globalization;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using TagLib;
+using TagLib.IFD;
+using TagLib.IFD.Entries;
+using TagLib.IFD.Tags;
namespace MediaBrowser.Providers.Photos
{
@@ -27,100 +31,101 @@ namespace MediaBrowser.Providers.Photos
item.SetImagePath(ImageType.Primary, item.Path);
item.SetImagePath(ImageType.Backdrop, item.Path);
- if (item.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || item.Path.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
+ // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
+
+ try
{
- try
+ var file = File.Create(item.Path);
+
+ var image = file as TagLib.Image.File;
+
+ var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
+
+ if (tag != null)
{
- using (var reader = new ExifReader(item.Path))
+ var structure = tag.Structure;
+
+ if (structure != null)
{
- double aperture = 0;
- double shutterSpeed = 0;
+ var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
- DateTime dateTaken;
-
- string manufacturer;
- string model;
-
- reader.GetTagValue(ExifTags.FNumber, out aperture);
- reader.GetTagValue(ExifTags.ExposureTime, out shutterSpeed);
- reader.GetTagValue(ExifTags.DateTimeOriginal, out dateTaken);
-
- reader.GetTagValue(ExifTags.Make, out manufacturer);
- reader.GetTagValue(ExifTags.Model, out model);
-
- if (dateTaken > DateTime.MinValue)
+ if (exif != null)
{
- item.DateCreated = dateTaken;
- item.PremiereDate = dateTaken;
- item.ProductionYear = dateTaken.Year;
+ var exifStructure = exif.Structure;
+
+ if (exifStructure != null)
+ {
+ var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
+
+ if (entry != null)
+ {
+ double val = entry.Value.Numerator;
+ val /= entry.Value.Denominator;
+ item.Aperture = val;
+ }
+
+ entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
+
+ if (entry != null)
+ {
+ double val = entry.Value.Numerator;
+ val /= entry.Value.Denominator;
+ item.ShutterSpeed = val;
+ }
+ }
}
-
- var cameraModel = manufacturer ?? string.Empty;
- cameraModel += " ";
- cameraModel += model ?? string.Empty;
-
- var size = _imageProcessor.GetImageSize(item.Path);
- var xResolution = size.Width;
- var yResolution = size.Height;
-
- item.Overview = "Taken " + dateTaken.ToString("F") + "\n" +
- (!string.IsNullOrWhiteSpace(cameraModel) ? "With a " + cameraModel : "") +
- (aperture > 0 && shutterSpeed > 0 ? " at f" + aperture.ToString(CultureInfo.InvariantCulture) + " and " + PhotoHelper.Dec2Frac(shutterSpeed) + "s" : "") + "\n"
- + (xResolution > 0 ? "\n
Resolution: " + xResolution + "x" + yResolution : "");
}
-
}
- catch (Exception e)
+
+ item.CameraManufacturer = image.ImageTag.Make;
+ item.CameraModel = image.ImageTag.Model;
+
+ var rating = image.ImageTag.Rating;
+ if (rating.HasValue)
{
- _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
+ item.CommunityRating = rating;
}
+ else
+ {
+ item.CommunityRating = null;
+ }
+
+ item.Overview = image.ImageTag.Comment;
+
+ if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
+ {
+ item.Name = image.ImageTag.Title;
+ }
+
+ var dateTaken = image.ImageTag.DateTime;
+ if (dateTaken.HasValue)
+ {
+ item.DateCreated = dateTaken.Value;
+ item.PremiereDate = dateTaken.Value;
+ item.ProductionYear = dateTaken.Value.Year;
+ }
+
+ var size = _imageProcessor.GetImageSize(item.Path);
+ item.Height = Convert.ToInt32(size.Height);
+ item.Width = Convert.ToInt32(size.Width);
+
+ item.Genres = image.ImageTag.Genres.ToList();
+ item.Tags = image.ImageTag.Keywords.ToList();
+ item.Software = image.ImageTag.Software;
+
+ Model.Drawing.ImageOrientation orientation;
+ if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
+ {
+ item.Orientation = orientation;
+ }
+
+ item.ExposureTime = image.ImageTag.ExposureTime;
+ item.FocalLength = image.ImageTag.FocalLength;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
}
-
- //// Get additional tags from xmp
- //try
- //{
- // using (var fs = new FileStream(item.Path, FileMode.Open, FileAccess.Read))
- // {
- // var bf = BitmapFrame.Create(fs);
-
- // if (bf != null)
- // {
- // var data = (BitmapMetadata)bf.Metadata;
- // if (data != null)
- // {
-
- // DateTime dateTaken;
- // var cameraModel = "";
-
- // DateTime.TryParse(data.DateTaken, out dateTaken);
- // if (dateTaken > DateTime.MinValue) item.DateCreated = dateTaken;
- // cameraModel = data.CameraModel;
-
- // item.PremiereDate = dateTaken;
- // item.ProductionYear = dateTaken.Year;
- // item.Overview = "Taken " + dateTaken.ToString("F") + "\n" +
- // (cameraModel != "" ? "With a " + cameraModel : "") +
- // (aperture > 0 && shutterSpeed > 0 ? " at f" + aperture.ToString(CultureInfo.InvariantCulture) + " and " + PhotoHelper.Dec2Frac(shutterSpeed) + "s" : "") + "\n"
- // + (bf.Width > 0 ? "\n
Resolution: " + (int)bf.Width + "x" + (int)bf.Height : "");
-
- // var photo = item as Photo;
- // if (data.Keywords != null) item.Genres = photo.Tags = new List(data.Keywords);
- // item.Name = !string.IsNullOrWhiteSpace(data.Title) ? data.Title : item.Name;
- // item.CommunityRating = data.Rating;
- // if (!string.IsNullOrWhiteSpace(data.Subject)) photo.AddTagline(data.Subject);
- // }
- // }
-
- // }
- //}
- //catch (NotSupportedException)
- //{
- // // No problem - move on
- //}
- //catch (Exception e)
- //{
- // _logger.ErrorException("Error trying to read extended data from {0}", e, item.Path);
- //}
const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;
return Task.FromResult(result);
diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config
index 9d3b60ff5..29a802156 100644
--- a/MediaBrowser.Providers/packages.config
+++ b/MediaBrowser.Providers/packages.config
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 42191a270..2d050d4a7 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
// Mono.Nat does never rise this event. The event is there however it is useless.
// You could remove it with no risk.
- // NatUtility.DeviceLost += NatUtility_DeviceLost;
+ NatUtility.DeviceLost += NatUtility_DeviceLost;
// it is hard to say what one should do when an unhandled exception is raised
@@ -71,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_isStarted = true;
- _timer = new Timer(s => _createdRules = new List(), null, TimeSpan.FromHours(6), TimeSpan.FromHours(6));
+ _timer = new Timer(s => _createdRules = new List(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
}
}
@@ -123,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
if (!_createdRules.Contains(address))
{
_createdRules.Add(address);
-
+
var info = _appHost.GetSystemInfo();
CreatePortMap(device, info.HttpServerPortNumber);
@@ -141,11 +141,11 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
}
// As I said before, this method will be never invoked. You can remove it.
- //void NatUtility_DeviceLost(object sender, DeviceEventArgs e)
- //{
- // var device = e.Device;
- // _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
- //}
+ void NatUtility_DeviceLost(object sender, DeviceEventArgs e)
+ {
+ var device = e.Device;
+ _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
+ }
public void Dispose()
{
@@ -167,7 +167,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
// This is not a significant improvement
NatUtility.StopDiscovery();
NatUtility.DeviceFound -= NatUtility_DeviceFound;
- //NatUtility.DeviceLost -= NatUtility_DeviceLost;
+ NatUtility.DeviceLost -= NatUtility_DeviceLost;
NatUtility.UnhandledException -= NatUtility_UnhandledException;
}
// Statements in try-block will no fail because StopDiscovery is a one-line
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 6b1f3d91a..0a9209bfe 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -1105,5 +1105,7 @@
"OptionDirector": "Director",
"OptionGuestStar": "Guest star",
"OptionProducer": "Producer",
- "OptionWriter": "Writer"
+ "OptionWriter": "Writer",
+ "LabelAirDays": "Air days:",
+ "LabelAirTime": "Air time:"
}
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
index 97a3ee190..7a74e9689 100644
--- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
+++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg
switch (arg)
{
case "Version":
- return "20140612";
+ return "20140827";
case "FFMpegFilename":
return "ffmpeg.exe";
case "FFProbeFilename":
@@ -111,9 +111,18 @@ namespace MediaBrowser.ServerApplication.FFMpeg
switch (pid)
{
case PlatformID.Win32NT:
+ if (PlatformDetection.IsX86_64)
+ {
+ return new[]
+ {
+ "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20140827-git-9e8ab36-win64-static.7z",
+ "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20140612-git-3a1c895-win32-static.7z"
+ };
+ }
+
return new[]
{
- "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140612-git-3a1c895-win32-static.7z",
+ "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140827-git-9e8ab36-win32-static.7z",
"https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20140612-git-3a1c895-win32-static.7z"
};