updated nuget

This commit is contained in:
Luke Pulverenti 2014-08-06 22:51:09 -04:00
parent 56e4d6730f
commit 5d5a0e3add
27 changed files with 200 additions and 92 deletions

View File

@ -341,7 +341,7 @@ namespace MediaBrowser.Api.Images
ImageIndex = imageIndex,
ImageType = info.Type,
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
Size = fileInfo.Length,
Size = info.Length ?? fileInfo.Length,
Width = Convert.ToInt32(size.Width),
Height = Convert.ToInt32(size.Height)
};

View File

@ -1309,7 +1309,8 @@ namespace MediaBrowser.Controller.Entities
{
Path = file.FullName,
Type = type,
DateModified = FileSystem.GetLastWriteTimeUtc(file)
DateModified = FileSystem.GetLastWriteTimeUtc(file),
Length = ((FileInfo)file).Length
});
}
else
@ -1420,11 +1421,14 @@ namespace MediaBrowser.Controller.Entities
return null;
}
var info = new FileInfo(path);
return new ItemImageInfo
{
Path = path,
DateModified = FileSystem.GetLastWriteTimeUtc(path),
Type = imageType
DateModified = FileSystem.GetLastWriteTimeUtc(info),
Type = imageType,
Length = info.Length
};
}

View File

@ -10,5 +10,7 @@ namespace MediaBrowser.Controller.Entities
public ImageType Type { get; set; }
public DateTime DateModified { get; set; }
public long? Length { get; set; }
}
}

View File

@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Playlists
public IEnumerable<BaseItem> GetManageableItems()
{
return GetLinkedChildren();
return GetPlaylistItems(MediaType, GetLinkedChildren(), null);
}
private IEnumerable<BaseItem> GetPlayableItems(User user)
@ -40,8 +40,12 @@ namespace MediaBrowser.Controller.Playlists
if (folder != null)
{
var items = folder.GetRecursiveChildren(user, true)
.Where(m => !m.IsFolder && string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
var items = user == null
? folder.GetRecursiveChildren()
: folder.GetRecursiveChildren(user, true);
items = items
.Where(m => !m.IsFolder);
if (!folder.IsPreSorted)
{
@ -52,7 +56,8 @@ namespace MediaBrowser.Controller.Playlists
}
return new[] { i };
});
}).Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
}
[IgnoreDataMember]

View File

@ -7,6 +7,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Dlna.Didl;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Dlna.Service;
@ -339,7 +340,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
}
else if (search.SearchType == SearchType.Playlist)
{
items = items.OfType<Playlist>();
}
items = SortItems(items, user, sort);
@ -495,7 +496,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
return items.Where(i =>
{
// Unplayable
if (i.LocationType == LocationType.Virtual)
if (i.LocationType == LocationType.Virtual && !i.IsFolder)
{
return false;
}
@ -507,7 +508,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
return false;
}
// Upnp renderers won't understand these
if (i is Game || i is Book)
{
return false;

View File

@ -148,14 +148,37 @@ namespace MediaBrowser.Dlna.Didl
private void AddSubtitleElement(XmlElement container, SubtitleStreamInfo info)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var subtitleProfile = _profile.SubtitleProfiles
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
res.InnerText = info.Url;
if (subtitleProfile == null)
{
return;
}
// TODO: Remove this hard-coding
res.SetAttribute("protocolInfo", "http-get:*:text/srt:*");
var subtitleMode = subtitleProfile.DidlMode;
container.AppendChild(res);
if (string.Equals(subtitleMode, "CaptionInfoEx", StringComparison.OrdinalIgnoreCase))
{
var res = container.OwnerDocument.CreateElement("SEC", "CaptionInfoEx");
res.InnerText = info.Url;
// TODO: attribute needs SEC:
res.SetAttribute("type", info.Format.ToLower());
container.AppendChild(res);
}
else
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
res.InnerText = info.Url;
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower());
res.SetAttribute("protocolInfo", protocolInfo);
container.AppendChild(res);
}
}
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
@ -496,7 +519,7 @@ namespace MediaBrowser.Dlna.Didl
}
else
{
throw new NotSupportedException();
objectClass.InnerText = "object.item";
}
return objectClass;
@ -611,7 +634,7 @@ namespace MediaBrowser.Dlna.Didl
icon.InnerText = iconUrlInfo.Url;
element.AppendChild(icon);
if (!_profile.EnableAlbumArtInDidl && !(item is Photo))
if (!_profile.EnableAlbumArtInDidl)
{
return;
}
@ -656,8 +679,8 @@ namespace MediaBrowser.Dlna.Didl
if (imageInfo.IsDirectStream)
{
// TODO: Add file size
//res.SetAttribute("size", imageInfo.Size.Value.ToString(_usCulture));
var length = imageInfo.ItemImageInfo.Length ?? new FileInfo(imageInfo.File).Length;
res.SetAttribute("size", length.ToString(_usCulture));
}
if (width.HasValue && height.HasValue)
@ -735,7 +758,8 @@ namespace MediaBrowser.Dlna.Didl
ImageTag = tag,
Width = width,
Height = height,
File = imageInfo.Path
File = imageInfo.Path,
ItemImageInfo = imageInfo
};
}
@ -751,6 +775,8 @@ namespace MediaBrowser.Dlna.Didl
internal bool IsDirectStream;
internal string File;
internal ItemImageInfo ItemImageInfo;
}
class ImageUrlInfo

View File

@ -344,7 +344,14 @@ namespace MediaBrowser.Dlna.Profiles
new SubtitleProfile
{
Format = "smi",
Method = SubtitleDeliveryMethod.External
Method = SubtitleDeliveryMethod.External,
DidlMode = "CaptionInfoEx"
},
new SubtitleProfile
{
Format = "srt",
Method = SubtitleDeliveryMethod.External,
DidlMode = "CaptionInfoEx"
}
};
}

View File

@ -107,6 +107,7 @@
</ResponseProfile>
</ResponseProfiles>
<SubtitleProfiles>
<SubtitleProfile format="smi" method="External" />
<SubtitleProfile format="smi" method="External" didlMode="CaptionInfoEx" />
<SubtitleProfile format="srt" method="External" didlMode="CaptionInfoEx" />
</SubtitleProfiles>
</Profile>

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -25,5 +26,10 @@ namespace MediaBrowser.Model.ApiClient
{
return apiClient.GetPublicUsersAsync(CancellationToken.None);
}
public static Task<ItemsResult> GetItemsAsync(this IApiClient apiClient, ItemQuery query)
{
return apiClient.GetItemsAsync(query, CancellationToken.None);
}
}
}

View File

@ -24,8 +24,9 @@ namespace MediaBrowser.Model.Dlna
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
@ -62,16 +63,17 @@ namespace MediaBrowser.Model.Dlna
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
if (isDirectStream)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_BYTE_BASED_SEEK;
}
else if (runtimeTicks.HasValue)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_TIME_BASED_SEEK;
flagValue = flagValue | DlnaFlags.ByteBasedSeek;
}
//else if (runtimeTicks.HasValue)
//{
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
//}
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
@ -120,16 +122,17 @@ namespace MediaBrowser.Model.Dlna
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
if (isDirectStream)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_BYTE_BASED_SEEK;
}
else if (runtimeTicks.HasValue)
{
//flagValue = flagValue | DlnaFlags.DLNA_ORG_FLAG_TIME_BASED_SEEK;
flagValue = flagValue | DlnaFlags.ByteBasedSeek;
}
//else if (runtimeTicks.HasValue)
//{
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
//}
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));

View File

@ -99,7 +99,8 @@ namespace MediaBrowser.Model.Dlna
ResponseProfiles = new ResponseProfile[] { };
CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { };
SubtitleProfiles = new SubtitleProfile[] { };
XmlRootAttributes = new XmlAttribute[] { };
SupportedMediaTypes = "Audio,Photo,Video";

View File

@ -5,17 +5,44 @@ namespace MediaBrowser.Model.Dlna
[Flags]
public enum DlnaFlags : ulong
{
/*! <i>Background</i> transfer mode.
For use with upload and download transfers to and from the server.
The primary difference between \ref DH_TransferMode_Interactive and
\ref DH_TransferMode_Bulk is that the latter assumes that the user
is not relying on the transfer for immediately rendering the content
and there are no issues with causing a buffer overflow if the
receiver uses TCP flow control to reduce total throughput.
*/
BackgroundTransferMode = (1 << 22),
ByteBasedSeek = (1 << 29),
ConnectionStall = (1 << 21),
DlnaV15 = (1 << 20),
/*! <i>Interactive</i> transfer mode.
For best effort transfer of images and non-real-time transfers.
URIs with image content usually support \ref DH_TransferMode_Bulk too.
The primary difference between \ref DH_TransferMode_Interactive and
\ref DH_TransferMode_Bulk is that the former assumes that the
transfer is intended for immediate rendering.
*/
InteractiveTransferMode = (1 << 23),
PlayContainer = (1 << 28),
RtspPause = (1 << 25),
S0Increase = (1 << 27),
SenderPaced = (1L << 31),
SnIncrease = (1 << 26),
/*! <i>Streaming</i> transfer mode.
The server transmits at a throughput sufficient for real-time playback of
audio or video. URIs with audio or video often support the
\ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes.
The most well-known exception to this general claim is for live streams.
*/
StreamingTransferMode = (1 << 24),
TimeBasedSeek = (1 << 30)
}
}

View File

@ -537,16 +537,6 @@ namespace MediaBrowser.Model.Dlna
return SubtitleDeliveryMethod.Encode;
}
private string NormalizeSubtitleFormat(string codec)
{
if (StringHelper.EqualsIgnoreCase(codec, "subrip"))
{
return SubtitleFormat.SRT;
}
return codec;
}
private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, SubtitleDeliveryMethod method, string[] formats)
{
foreach (SubtitleProfile profile in profiles)

View File

@ -172,7 +172,8 @@ namespace MediaBrowser.Model.Dlna
Url = url,
IsForced = stream.IsForced,
Language = stream.Language,
Name = stream.Language ?? "Unknown"
Name = stream.Language ?? "Unknown",
Format = SubtitleFormat
});
}
}
@ -512,5 +513,6 @@ namespace MediaBrowser.Model.Dlna
public string Language { get; set; }
public string Name { get; set; }
public bool IsForced { get; set; }
public string Format { get; set; }
}
}

View File

@ -7,10 +7,11 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("format")]
public string Format { get; set; }
[XmlAttribute("protocol")]
public string Protocol { get; set; }
[XmlAttribute("method")]
public SubtitleDeliveryMethod Method { get; set; }
[XmlAttribute("didlMode")]
public string DidlMode { get; set; }
}
}

View File

@ -135,7 +135,7 @@
"HeaderSelectTranscodingPath": "Select Transcoding Temporary Path",
"HeaderSelectImagesByNamePath": "Select Images By Name Path",
"HeaderSelectMetadataPath": "Select Metadata Path",
"HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable. The location of this folder will directly impact server performance and should ideally be placed on a solid state drive.",
"HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable.",
"HeaderSelectTranscodingPathHelp": "Browse or enter the path to use for transcoding temporary files. The folder must be writeable.",
"HeaderSelectImagesByNamePathHelp": "Browse or enter the path to your items by name folder. The folder must be writeable.",
"HeaderSelectMetadataPathHelp": "Browse or enter the path you'd like to store metadata within. The folder must be writeable.",

View File

@ -147,11 +147,11 @@ namespace MediaBrowser.Server.Implementations.Playlists
return path;
}
private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user)
private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
return Playlist.GetPlaylistItems(playlistMediaType, items, user);
return Playlist.GetPlaylistItems(playlistMediaType, items, null);
}
public async Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds)
@ -166,17 +166,11 @@ namespace MediaBrowser.Server.Implementations.Playlists
var list = new List<LinkedChild>();
var itemList = new List<BaseItem>();
foreach (var itemId in itemIds)
var items = GetPlaylistItems(itemIds, playlist.MediaType).ToList();
foreach (var item in items)
{
var item = _libraryManager.GetItemById(itemId);
if (item == null)
{
throw new ArgumentException("No item exists with the supplied Id");
}
itemList.Add(item);
list.Add(LinkedChild.Create(item));
}

View File

@ -1,6 +1,5 @@
using MediaBrowser.Controller.Security;
using System;
using System.Security.Cryptography;
using System.Text;
namespace MediaBrowser.Server.Implementations.Security
@ -17,11 +16,7 @@ namespace MediaBrowser.Server.Implementations.Security
{
if (value == null) throw new ArgumentNullException("value");
#if __MonoCS__
return EncryptStringUniversal(value);
#endif
return Encoding.Default.GetString(ProtectedData.Protect(Encoding.Default.GetBytes(value), null, DataProtectionScope.LocalMachine));
}
/// <summary>
@ -34,11 +29,7 @@ namespace MediaBrowser.Server.Implementations.Security
{
if (value == null) throw new ArgumentNullException("value");
#if __MonoCS__
return DecryptStringUniversal(value);
#endif
return Encoding.Default.GetString(ProtectedData.Unprotect(Encoding.Default.GetBytes(value), null, DataProtectionScope.LocalMachine));
}
private string EncryptStringUniversal(string value)

View File

@ -1,4 +1,11 @@
using MediaBrowser.Server.Mono;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.ServerApplication.Native
{
@ -56,5 +63,16 @@ namespace MediaBrowser.ServerApplication.Native
{
}
public async Task<CheckForUpdateResult> CheckForApplicationUpdate(Version currentVersion,
PackageVersionClass updateLevel,
IInstallationManager installationManager,
CancellationToken cancellationToken,
IProgress<double> progress)
{
var result = new CheckForUpdateResult { AvailableVersion = currentVersion.ToString(), IsUpdateAvailable = false };
return Task.FromResult(result);
}
}
}

View File

@ -1183,24 +1183,18 @@ namespace MediaBrowser.ServerApplication
/// <returns>Task{CheckForUpdateResult}.</returns>
public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
{
var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
var result = await NativeApp.CheckForApplicationUpdate(ApplicationVersion,
ConfigurationManager.CommonConfiguration.SystemUpdateLevel, InstallationManager,
cancellationToken, progress).ConfigureAwait(false);
var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, "MBServer", null, ApplicationVersion,
ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
HasUpdateAvailable = result.IsUpdateAvailable;
var versionObject = version == null || string.IsNullOrWhiteSpace(version.versionStr) ? null : new Version(version.versionStr);
var isUpdateAvailable = versionObject != null && versionObject > ApplicationVersion;
HasUpdateAvailable = isUpdateAvailable;
if (isUpdateAvailable)
if (result.IsUpdateAvailable)
{
Logger.Info("New application version is available: {0}", versionObject);
Logger.Info("New application version is available: {0}", result.AvailableVersion);
}
return versionObject != null ?
new CheckForUpdateResult { AvailableVersion = versionObject.ToString(), IsUpdateAvailable = isUpdateAvailable, Package = version } :
new CheckForUpdateResult { AvailableVersion = ApplicationVersion.ToString(), IsUpdateAvailable = false };
return result;
}
/// <summary>

View File

@ -1,4 +1,10 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.ServerApplication.Native
{
@ -84,5 +90,24 @@ namespace MediaBrowser.ServerApplication.Native
EXECUTION_STATE es = SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED);
}
}
public static async Task<CheckForUpdateResult> CheckForApplicationUpdate(Version currentVersion,
PackageVersionClass updateLevel,
IInstallationManager installationManager,
CancellationToken cancellationToken,
IProgress<double> progress)
{
var availablePackages = await installationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
var version = installationManager.GetLatestCompatibleVersion(availablePackages, "MBServer", null, currentVersion, updateLevel);
var versionObject = version == null || string.IsNullOrWhiteSpace(version.versionStr) ? null : new Version(version.versionStr);
var isUpdateAvailable = versionObject != null && versionObject > currentVersion;
return versionObject != null ?
new CheckForUpdateResult { AvailableVersion = versionObject.ToString(), IsUpdateAvailable = isUpdateAvailable, Package = version } :
new CheckForUpdateResult { AvailableVersion = currentVersion.ToString(), IsUpdateAvailable = false };
}
}
}

View File

@ -221,6 +221,16 @@ namespace MediaBrowser.WebDashboard.Api
var contentType = MimeTypes.GetMimeType(path);
var isHtml = IsHtml(path);
if (isHtml && !_serverConfigurationManager.Configuration.IsStartupWizardCompleted)
{
if (path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1)
{
Request.Response.Redirect("wizardstart.html");
return null;
}
}
var localizationCulture = GetLocalizationCulture();
// Don't cache if not configured to do so

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.423</version>
<version>3.0.424</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.423" />
<dependency id="MediaBrowser.Common" version="3.0.424" />
<dependency id="NLog" version="3.1.0.0" />
<dependency id="SimpleInjector" version="2.5.2" />
<dependency id="sharpcompress" version="0.10.2" />

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.423</version>
<version>3.0.424</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
<version>3.0.423</version>
<version>3.0.424</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.423</version>
<version>3.0.424</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.423" />
<dependency id="MediaBrowser.Common" version="3.0.424" />
</dependencies>
</metadata>
<files>

View File

@ -9,14 +9,15 @@ We have several client apps released and in production:
- [Android](https://play.google.com/store/apps/details?id=com.mb.android "Android")
- Html5
- [iOS](https://itunes.apple.com/us/app/media-browser-for-ios/id705058087 "iOS")
- [iPad](https://itunes.apple.com/us/app/media-browser-client/id879475585 "iPad")
- [iPhone](https://itunes.apple.com/us/app/media-browser-for-ios/id705058087?mt=8 "iPhone")
- [Media Portal](http://www.team-mediaportal.com/ "Media Portal")
- [Roku](http://www.roku.com/channels/#!details/34503/media-browser "Roku")
- Windows 7/8 Desktop
- Windows Media Center
- [Windows Phone](http://www.windowsphone.com/s?appid=f4971ed9-f651-4bf6-84bb-94fd98613b86 "Windows Phone")
- [Windows 8](http://apps.microsoft.com/windows/en-us/app/media-browser/ad55a2f0-9897-47bd-8944-bed3aefd5d06 "Windows 8.1")
- [Xbmc](http://addons.xbmc.org/show/plugin.video.xbmb3c "Xbmc")
- [Xbmc](http://mediabrowser.tv/download/ "Xbmc")
## New Users ##