diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index f147234fe0..a97e2b52ee 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -226,6 +226,18 @@ namespace MediaBrowser.Api.Library
public string TvdbId { get; set; }
}
+ [Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")]
+ [Authenticated(Roles = "download")]
+ public class GetDownload
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
///
/// Class LibraryService
///
@@ -273,7 +285,7 @@ namespace MediaBrowser.Api.Library
}
var dtoOptions = GetDtoOptions(request);
-
+
var result = new ItemsResult
{
TotalRecordCount = items.Count,
@@ -289,6 +301,28 @@ namespace MediaBrowser.Api.Library
Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None));
}
+ public object Get(GetDownload request)
+ {
+ var item = _libraryManager.GetItemById(request.Id);
+
+ if (!item.CanDelete())
+ {
+ throw new ArgumentException("Item does not support downloading");
+ }
+
+ var headers = new Dictionary();
+
+ // Quotes are valid in linux. They'll possibly cause issues here
+ var filename = Path.GetFileName(item.Path).Replace("\"", string.Empty);
+ headers["Content-Disposition"] = string.Format("inline; filename=\"{0}\"", filename);
+
+ return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
+ {
+ Path = item.Path,
+ ResponseHeaders = headers
+ });
+ }
+
public object Get(GetFile request)
{
var item = _libraryManager.GetItemById(request.Id);
@@ -347,7 +381,7 @@ namespace MediaBrowser.Api.Library
var dtoOptions = GetDtoOptions(request);
BaseItem parent = item.Parent;
-
+
while (parent != null)
{
if (user != null)
@@ -458,23 +492,9 @@ namespace MediaBrowser.Api.Library
var auth = _authContext.GetAuthorizationInfo(Request);
var user = _userManager.GetUserById(auth.UserId);
- if (item is Playlist || item is BoxSet)
+ if (!item.CanDelete(user))
{
- // For now this is allowed if user can see the playlist
- }
- else if (item is ILiveTvRecording)
- {
- if (!user.Policy.EnableLiveTvManagement)
- {
- throw new UnauthorizedAccessException();
- }
- }
- else
- {
- if (!user.Policy.EnableContentDeletion)
- {
- throw new UnauthorizedAccessException();
- }
+ throw new UnauthorizedAccessException();
}
var task = _libraryManager.DeleteItem(item);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 6d555e136a..d26ca27216 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -123,7 +123,7 @@ namespace MediaBrowser.Api.Playback
private string GetOutputFilePath(StreamState state)
{
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
-
+
var outputFileExtension = GetOutputFileExtension(state);
var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
@@ -323,13 +323,13 @@ namespace MediaBrowser.Api.Playback
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
- param += " -crf 23";
+ param += " -subq 0 -crf 23";
break;
case EncodingQuality.HighQuality:
- param += " -crf 20";
+ param += " -subq 3 -crf 20";
break;
case EncodingQuality.MaxQuality:
- param += " -crf 18";
+ param += " -subq 6 -crf 18";
break;
}
}
@@ -507,7 +507,7 @@ namespace MediaBrowser.Api.Playback
}
}
- return param;
+ return "-pix_fmt yuv420p " + param;
}
protected string GetAudioFilterParam(StreamState state, bool isHls)
@@ -918,7 +918,7 @@ namespace MediaBrowser.Api.Playback
if (SupportsThrottleWithStream)
{
var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/videos/" + state.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + state.Request.MediaSourceId;
-
+
url += "&transcodingJobId=" + transcodingJobId;
return string.Format("\"{0}\"", url);
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 8924bb38f6..7e86b867f6 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -97,6 +97,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase))
{
+ // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov";
}
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index 87d257f12e..a482f45fe0 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -67,5 +67,10 @@ namespace MediaBrowser.Controller.Channels
{
return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata");
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
index d9330f8a30..91b2407bee 100644
--- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
@@ -3,10 +3,10 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -89,5 +89,10 @@ namespace MediaBrowser.Controller.Channels
return list;
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
index dce9840eda..7ba73d126c 100644
--- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
@@ -1,11 +1,10 @@
-using System;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
+using System;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -76,5 +75,10 @@ namespace MediaBrowser.Controller.Channels
{
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
index e3fad56e90..d7d4483cd1 100644
--- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -119,5 +119,10 @@ namespace MediaBrowser.Controller.Channels
{
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index af2cca5e63..66a0d551b2 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -32,6 +32,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// The _virtual children
///
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 2dcea37bdf..a7b91b8681 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -113,6 +113,13 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
///
/// Gets or sets the artist.
///
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 038aa98aae..e65d3c0e78 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -1,14 +1,13 @@
-using System.Runtime.Serialization;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -35,6 +34,11 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
+ public override bool CanDelete()
+ {
+ return !IsAccessedByName;
+ }
+
protected override IEnumerable ActualChildren
{
get
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 9689d7cce2..ed09560732 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -39,6 +39,11 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 4925bcd8ad..1443d99d32 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -239,6 +239,38 @@ namespace MediaBrowser.Controller.Entities
get { return this.GetImagePath(ImageType.Primary); }
}
+ public virtual bool CanDelete()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
+ public virtual bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableContentDeletion;
+ }
+
+ public bool CanDelete(User user)
+ {
+ return CanDelete() && IsAuthorizedToDelete(user);
+ }
+
+ public virtual bool CanDownload()
+ {
+ return false;
+ }
+
+ public virtual bool IsAuthorizedToDownload(User user)
+ {
+ return user.Policy.EnableContentDownloading;
+ }
+
+ public bool CanDownload(User user)
+ {
+ return CanDownload() && IsAuthorizedToDownload(user);
+ }
+
///
/// Gets or sets the date created.
///
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index b30bd81b96..785c441d37 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -11,5 +11,10 @@ namespace MediaBrowser.Controller.Entities
{
get { return null; }
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 381b2101d2..e59db67a6a 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
@@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
Tags = new List();
}
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Book);
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index f82934a51b..a39357f2b0 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -35,6 +35,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
public string CollectionType { get; set; }
///
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index 71642ea902..899e5628f1 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -1,10 +1,10 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -38,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
public List LocalTrailerIds { get; set; }
public List RemoteTrailerIds { get; set; }
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
///
/// Gets or sets the tags.
///
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 41ce0c12ab..b246b9388f 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -43,6 +43,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
return inputItems.Where(GetItemFilter());
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index f581c55e83..e17a5c1d8b 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
index b48ad788fc..14b69b8fde 100644
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ b/MediaBrowser.Controller/Entities/IItemByName.cs
@@ -15,6 +15,10 @@ namespace MediaBrowser.Controller.Entities
/// IEnumerable{BaseItem}.
IEnumerable GetTaggedItems(IEnumerable inputItems);
+ ///
+ /// Gets the item filter.
+ ///
+ /// Func<BaseItem, System.Boolean>.
Func GetItemFilter();
}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 7f74e33793..d874046efd 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -74,6 +74,11 @@ namespace MediaBrowser.Controller.Entities.Movies
}
}
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return true;
+ }
+
///
/// Gets the trailer ids.
///
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 25509b1530..d8cb69ca17 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -45,6 +45,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 76193d6c42..31bbaf422e 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -40,6 +40,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index cd179eb428..5f7ca3d3f4 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -40,6 +40,11 @@ namespace MediaBrowser.Controller.Entities
return result.Items;
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
public override IEnumerable GetRecursiveChildren(User user, Func filter)
{
var result = GetItems(new InternalItemsQuery
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 3abaf095c8..12c377c906 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -64,6 +64,19 @@ namespace MediaBrowser.Controller.Entities
LinkedAlternateVersions = new List();
}
+ public override bool CanDownload()
+ {
+ if (VideoType == VideoType.HdDvd || VideoType == VideoType.Dvd ||
+ VideoType == VideoType.BluRay)
+ {
+ return false;
+ }
+
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 59a4bf16d3..cf3ad3b6ab 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index ba1cb30436..784cb6ea18 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -25,5 +25,9 @@ namespace MediaBrowser.Controller.LiveTv
Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
PlayAccess GetPlayAccess(User user);
+
+ bool CanDelete();
+
+ bool CanDelete(User user);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index 8faaa895c2..9815066efc 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -93,5 +94,10 @@ namespace MediaBrowser.Controller.LiveTv
{
return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
}
+
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableLiveTvManagement;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 1948ce0d50..eaea6cfa45 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -1,5 +1,4 @@
-using System.Runtime.Serialization;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -8,6 +7,7 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
@@ -135,5 +135,10 @@ namespace MediaBrowser.Controller.LiveTv
{
return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"), "metadata");
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index a66aaad6f8..ee85ce20b0 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,13 +1,13 @@
-using System.Runtime.Serialization;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Users;
using System;
+using System.Linq;
+using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using System.Linq;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -215,5 +215,10 @@ namespace MediaBrowser.Controller.LiveTv
{
return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index 34fe757a71..207684d55d 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -92,5 +92,10 @@ namespace MediaBrowser.Controller.LiveTv
{
return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
}
+
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableLiveTvManagement;
+ }
}
}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 0f0c6a97ed..3479902cbd 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -1,7 +1,5 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System;
@@ -40,6 +38,11 @@ namespace MediaBrowser.Controller.Playlists
}
}
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return true;
+ }
+
public override bool IsSaveLocalMetadataEnabled()
{
return true;
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index 344eb9fbdf..5a00c3d3fe 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -631,13 +631,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
- param += " -crf 23";
+ param += " -subq 0 -crf 23";
break;
case EncodingQuality.HighQuality:
- param += " -crf 20";
+ param += " -subq 3 -crf 20";
break;
case EncodingQuality.MaxQuality:
- param += " -crf 18";
+ param += " -subq 6 -crf 18";
break;
}
}
@@ -740,7 +740,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
param += " -level " + state.Options.Level.Value.ToString(UsCulture);
}
- return param;
+ return "-pix_fmt yuv420p " + param;
}
protected string GetVideoBitrateParam(EncodingJob state, string videoCodec, bool isHls)
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index c1a34a7f61..be3cd99be8 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -56,6 +56,8 @@ namespace MediaBrowser.Model.Dto
public int? AirsBeforeEpisodeNumber { get; set; }
public int? AbsoluteEpisodeNumber { get; set; }
public bool? DisplaySpecialsWithSeasons { get; set; }
+ public bool? CanDelete { get; set; }
+ public bool? CanDownload { get; set; }
public string PreferredMetadataLanguage { get; set; }
public string PreferredMetadataCountryCode { get; set; }
diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
index 15378a9af1..a6cd85d8d3 100644
--- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
@@ -99,6 +99,12 @@ namespace MediaBrowser.Model.LiveTv
/// The path.
public string Path { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this instance can delete.
+ ///
+ /// null if [can delete] contains no value, true if [can delete]; otherwise, false.
+ public bool? CanDelete { get; set; }
+
///
/// Overview of the recording.
///
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 5018f8e516..7a91d77fff 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -1,5 +1,4 @@
-
-namespace MediaBrowser.Model.Querying
+namespace MediaBrowser.Model.Querying
{
///
/// Used to control the data that gets attached to DtoBaseItems
@@ -26,6 +25,16 @@ namespace MediaBrowser.Model.Querying
///
Budget,
+ ///
+ /// The can delete
+ ///
+ CanDelete,
+
+ ///
+ /// The can download
+ ///
+ CanDownload,
+
///
/// The chapters
///
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index cdc5077b02..9606cbe3fe 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -42,7 +42,8 @@ namespace MediaBrowser.Model.Users
public bool EnableMediaPlayback { get; set; }
public bool EnableContentDeletion { get; set; }
-
+ public bool EnableContentDownloading { get; set; }
+
///
/// Gets or sets a value indicating whether [enable synchronize].
///
@@ -80,6 +81,8 @@ namespace MediaBrowser.Model.Users
EnabledDevices = new string[] { };
EnableAllDevices = true;
+
+ EnableContentDownloading = true;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 7b36321f18..75bddda723 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -284,6 +284,20 @@ namespace MediaBrowser.Server.Implementations.Dto
AttachLinkedChildImages(dto, playlist, user, options);
}
+ if (fields.Contains(ItemFields.CanDelete))
+ {
+ dto.CanDelete = user == null
+ ? item.CanDelete()
+ : item.CanDelete(user);
+ }
+
+ if (fields.Contains(ItemFields.CanDownload))
+ {
+ dto.CanDownload = user == null
+ ? item.CanDownload()
+ : item.CanDownload(user);
+ }
+
return dto;
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs
index c374a31b36..77953ee43b 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -74,7 +74,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
ValidateUserAccess(user, request, authAttribtues, auth);
}
- if (!IsExemptFromRoles(auth, authAttribtues))
+ var info = (AuthenticationInfo)request.Items["OriginalAuthenticationInfo"];
+
+ if (!IsExemptFromRoles(auth, authAttribtues, info))
{
var roles = authAttribtues.GetRoles().ToList();
@@ -142,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
StringComparer.OrdinalIgnoreCase);
}
- private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues)
+ private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, AuthenticationInfo tokenInfo)
{
if (!_config.Configuration.IsStartupWizardCompleted &&
authAttribtues.AllowBeforeStartupWizard)
@@ -150,6 +152,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
return true;
}
+ if (string.IsNullOrWhiteSpace(auth.Token))
+ {
+ return true;
+ }
+
+ if (tokenInfo != null && string.IsNullOrWhiteSpace(tokenInfo.UserId))
+ {
+ return true;
+ }
+
return false;
}
@@ -175,6 +187,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
};
}
}
+ if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
+ {
+ if (user == null || !user.Policy.EnableContentDownloading)
+ {
+ throw new SecurityException("User does not have download access.")
+ {
+ SecurityExceptionType = SecurityExceptionType.Unauthenticated
+ };
+ }
+ }
}
private bool IsValidConnectKey(string token)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
index b3066b460f..f1bb5c13ae 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -229,6 +229,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
ServerId = _appHost.SystemId
};
+ dto.CanDelete = user == null
+ ? recording.CanDelete()
+ : recording.CanDelete(user);
+
dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList();
if (info.Status == RecordingStatus.InProgress)
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
index 3fddf274c6..184a7f9a4e 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
@@ -48,6 +48,7 @@
"LabelFailed": "(failed)",
"ButtonHelp": "Help",
"ButtonSave": "Save",
+ "ButtonDownload": "Download",
"SyncJobStatusQueued": "Queued",
"SyncJobStatusConverting": "Converting",
"SyncJobStatusFailed": "Failed",
@@ -56,6 +57,7 @@
"SyncJobStatusReadyToTransfer": "Ready to Transfer",
"SyncJobStatusTransferring": "Transferring",
"SyncJobStatusCompletedWithError": "Synced with errors",
+ "SyncJobItemStatusReadyToTransfer": "Ready to Transfer",
"LabelCollection": "Collection",
"HeaderAddToCollection": "Add to Collection",
"NewCollectionNameExample": "Example: Star Wars Collection",
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index aaf05d1ff5..c280b32ea2 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -285,10 +285,10 @@
"ButtonHelp": "Help",
"OptionAllowUserToManageServer": "Allow this user to manage the server",
"HeaderFeatureAccess": "Feature Access",
- "OptionAllowMediaPlayback": "Allow media playback",
- "OptionAllowBrowsingLiveTv": "Allow browsing of live tv",
- "OptionAllowDeleteLibraryContent": "Allow deletion of library content",
- "OptionAllowManageLiveTv": "Allow management of live tv recordings",
+ "OptionAllowMediaPlayback": "Media playback",
+ "OptionAllowBrowsingLiveTv": "Live TV",
+ "OptionAllowDeleteLibraryContent": "Media deletion",
+ "OptionAllowManageLiveTv": "Live TV recording management",
"OptionAllowRemoteControlOthers": "Allow remote control of other users",
"OptionAllowRemoteSharedDevices": "Allow remote control of shared devices",
"OptionAllowRemoteSharedDevicesHelp": "Dlna devices are considered shared until a user begins controlling it.",
@@ -1133,7 +1133,7 @@
"ViewTypeLiveTvRecordingGroups": "Recordings",
"ViewTypeLiveTvChannels": "Channels",
"LabelEasyPinCode": "Easy pin code:",
- "EasyPasswordHelp": "Your easy pin code is used for offline access with supported Media Browser apps, and can also be used for easy in-network sign in.",
+ "EasyPasswordHelp": "Your easy pin code is used for offline access with supported Media Browser apps, and can also be used for easy in-network sign in.",
"LabelInNetworkSignInWithEasyPassword": "Enable in-network sign in with my easy pin code",
"LabelInNetworkSignInWithEasyPasswordHelp": "If enabled, you'll be able to use your easy pin code to sign in to Media Browser apps from inside your home network. Your regular password will only be needed away from home. If the pin code is left blank, you won't need a password within your home network.",
"HeaderPassword": "Password",
@@ -1362,7 +1362,8 @@
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
"TabActivity": "Activity",
"TitleSync": "Sync",
- "OptionAllowSyncContent": "Allow syncing media to devices",
+ "OptionAllowSyncContent": "Sync",
+ "OptionAllowContentDownloading": "Media downloading",
"NameSeasonUnknown": "Season Unknown",
"NameSeasonNumber": "Season {0}",
"LabelNewUserNameHelp": "Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)",
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
index e418448db7..9ecb80f3ba 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
@@ -311,8 +311,10 @@ namespace MediaBrowser.Server.Implementations.Sync
var itemByName = item as IItemByName;
if (itemByName != null)
{
+ var itemByNameFilter = itemByName.GetItemFilter();
+
return user.RootFolder
- .GetRecursiveChildren(user, itemByName.GetItemFilter());
+ .GetRecursiveChildren(user, i => !i.IsFolder && itemByNameFilter(i));
}
if (item.IsFolder)
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 3eb98e95d0..8bf0050219 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -414,7 +414,6 @@ namespace MediaBrowser.WebDashboard.Api
"indexpage.js",
"itembynamedetailpage.js",
"itemdetailpage.js",
- "itemgallery.js",
"itemlistpage.js",
"librarypathmapping.js",
"reports.js",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 34b0e9e88e..bc610b7b65 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -1656,9 +1656,6 @@
-
- PreserveNewest
-
PreserveNewest
@@ -1707,9 +1704,6 @@
PreserveNewest
-
- PreserveNewest
-
PreserveNewest