diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
19 files changed, 518 insertions, 39 deletions
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 1833b708f..02da776bd 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; @@ -1064,7 +1065,7 @@ namespace MediaBrowser.Server.Implementations.Dto dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary); } - dto.MediaSources = GetMediaSources(audio); + dto.MediaSources = GetAudioMediaSources(audio); dto.MediaSourceCount = 1; } @@ -1100,7 +1101,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (fields.Contains(ItemFields.MediaSources)) { - dto.MediaSources = GetMediaSources(video); + dto.MediaSources = GetVideoMediaSources(video); } if (fields.Contains(ItemFields.Chapters)) @@ -1266,9 +1267,48 @@ namespace MediaBrowser.Server.Implementations.Dto { SetBookProperties(dto, book); } + + var tvChannel = item as LiveTvChannel; + + if (tvChannel != null) + { + dto.MediaSources = GetMediaSources(tvChannel); + } + } + + public List<MediaSourceInfo> GetMediaSources(BaseItem item) + { + var video = item as Video; + + if (video != null) + { + return GetVideoMediaSources(video); + } + + var audio = item as Audio; + + if (audio != null) + { + return GetAudioMediaSources(audio); + } + + var result = new List<MediaSourceInfo> + { + new MediaSourceInfo + { + Id = item.Id.ToString("N"), + LocationType = item.LocationType, + Name = item.Name, + Path = GetMappedPath(item), + MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(), + RunTimeTicks = item.RunTimeTicks + } + }; + + return result; } - private List<MediaSourceInfo> GetMediaSources(Video item) + private List<MediaSourceInfo> GetVideoMediaSources(Video item) { var result = item.GetAlternateVersions().Select(GetVersionInfo).ToList(); @@ -1293,11 +1333,11 @@ namespace MediaBrowser.Server.Implementations.Dto .ToList(); } - private List<MediaSourceInfo> GetMediaSources(Audio item) + private List<MediaSourceInfo> GetAudioMediaSources(Audio item) { var result = new List<MediaSourceInfo> { - GetVersionInfo(item, true) + GetVersionInfo(item) }; return result; @@ -1321,7 +1361,7 @@ namespace MediaBrowser.Server.Implementations.Dto }; } - private MediaSourceInfo GetVersionInfo(Audio i, bool isPrimary) + private MediaSourceInfo GetVersionInfo(Audio i) { return new MediaSourceInfo { diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 9279fd8d7..5b65a60c8 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -257,8 +257,12 @@ namespace MediaBrowser.Server.Implementations.IO InternalBufferSize = 32767 }; - newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | - NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size; + newWatcher.NotifyFilter = NotifyFilters.CreationTime | + NotifyFilters.DirectoryName | + NotifyFilters.FileName | + NotifyFilters.LastWrite | + NotifyFilters.Size | + NotifyFilters.Attributes; newWatcher.Created += watcher_Changed; newWatcher.Deleted += watcher_Changed; @@ -349,13 +353,13 @@ namespace MediaBrowser.Server.Implementations.IO { try { - Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); + Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath); ReportFileSystemChanged(e.FullPath); } catch (Exception ex) { - Logger.ErrorException("Exception in watcher changed. Path: {0}", ex, e.FullPath); + Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath); } } @@ -397,14 +401,6 @@ namespace MediaBrowser.Server.Implementations.IO Logger.Debug("Ignoring change to {0}", path); return true; } - - // Go up another level - parent = Path.GetDirectoryName(i); - if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } } return false; diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs new file mode 100644 index 000000000..9d5826454 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -0,0 +1,67 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.Library +{ + public class MusicManager : IMusicManager + { + private readonly ILibraryManager _libraryManager; + + public MusicManager(ILibraryManager libraryManager) + { + _libraryManager = libraryManager; + } + + public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user) + { + return GetInstantMixFromGenres(item.Genres, user); + } + + public IEnumerable<Audio> GetInstantMixFromArtist(string name, User user) + { + var artist = _libraryManager.GetArtist(name); + + var genres = _libraryManager.RootFolder + .RecursiveChildren + .OfType<Audio>() + .Where(i => i.HasArtist(name)) + .SelectMany(i => i.Genres) + .Concat(artist.Genres) + .Distinct(StringComparer.OrdinalIgnoreCase); + + return GetInstantMixFromGenres(genres, user); + } + + public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user) + { + var genres = item + .RecursiveChildren + .OfType<Audio>() + .SelectMany(i => i.Genres) + .Concat(item.Genres) + .Distinct(StringComparer.OrdinalIgnoreCase); + + return GetInstantMixFromGenres(genres, user); + } + + public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user) + { + var inputItems = user.RootFolder.GetRecursiveChildren(user); + + var genresDictionary = genres.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + + return inputItems + .OfType<Audio>() + .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey))) + .OrderByDescending(i => i.Item2) + .ThenBy(i => Guid.NewGuid()) + .Select(i => i.Item1) + .Take(100) + .OrderBy(i => Guid.NewGuid()); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 67c011a1f..b92e82385 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -34,8 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio { var collectionType = args.GetCollectionType(); - if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || - string.IsNullOrEmpty(collectionType)) + if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) { return new Controller.Entities.Audio.Audio(); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 4e1bd860b..cf7358cce 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -222,13 +222,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv RunTimeTicks = (info.EndDate - info.StartDate).Ticks, OriginalAirDate = info.OriginalAirDate, - MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery - { - ItemId = recording.Id - - }).ToList() + MediaSources = _dtoService.GetMediaSources((BaseItem)recording) }; + dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList(); + if (info.Status == RecordingStatus.InProgress) { var now = DateTime.UtcNow.Ticks; @@ -317,7 +315,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv Type = info.GetClientTypeName(), Id = info.Id.ToString("N"), MediaType = info.MediaType, - ExternalId = info.ExternalId + ExternalId = info.ExternalId, + MediaSources = _dtoService.GetMediaSources(info) }; if (user != null) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index db8786d62..b828dc0de 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -134,6 +134,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv return number; }); + + if (query.IsFavorite.HasValue) + { + var val = query.IsFavorite.Value; + + channels = channels + .Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val); + } } channels = channels.OrderBy(i => diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json new file mode 100644 index 000000000..45f0d7e13 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json @@ -0,0 +1,21 @@ +{ + "SettingsSaved": "Settings saved.", + "AddUser": "Add User", + "Users": "Users", + "Delete": "Delete", + "Administrator": "Administrator", + "Password": "Password", + "CreatePassword": "Create Password", + "DeleteImage": "Delete Image", + "DeleteImageConfirmation": "Are you sure you wish to delete this image?", + "FileReadCancelled": "The file read has been cancelled.", + "FileNotFound": "File not found.", + "FileReadError": "An error occurred while reading the file.", + "DeleteUser": "Delete User", + "DeleteUserConfirmation": "Are you sure you wish to delete {0}?", + "PasswordResetHeader": "Password Reset", + "PasswordResetComplete": "The password has been reset.", + "PasswordResetConfirmation": "Are you sure you wish to reset the password?", + "PasswordSaved": "Password saved.", + "PasswordMatchError": "Password and password confirmation must match." +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json new file mode 100644 index 000000000..41f8508a5 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -0,0 +1,21 @@ +{ + "SettingsSaved": "Settings saved.", + "AddUser": "Add User", + "Users": "Users", + "Delete": "Delete", + "Administrator": "Administrator", + "Password": "Password", + "CreatePassword": "Create Password", + "DeleteImage": "Delete Image", + "DeleteImageConfirmation": "Are you sure you wish to delete this image?", + "FileReadCancelled": "The file read has been cancelled.", + "FileNotFound": "File not found.", + "FileReadError": "An error occurred while reading the file.", + "DeleteUser": "Delete User", + "DeleteUserConfirmation": "Are you sure you wish to delete {0}?", + "PasswordResetHeader": "Password Reset", + "PasswordResetComplete": "The password has been reset.", + "PasswordResetConfirmation": "Are you sure you wish to reset the password?", + "PasswordSaved": "Password saved.", + "PasswordMatchError": "Password and password confirmation must match." +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json new file mode 100644 index 000000000..25d3ff944 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json @@ -0,0 +1,21 @@ +{ + "SettingsSaved": "Configura\u00e7\u00f5es guardadas.", + "AddUser": "Adicionar Utilizador", + "Users": "Utilizadores", + "Delete": "Apagar", + "Administrator": "Administrador", + "Password": "Senha", + "CreatePassword": "Criar Senha", + "DeleteImage": "Apagar Imagem", + "DeleteImageConfirmation": "Tem a certeza que pretende apagar a imagem?", + "FileReadCancelled": "The file read has been cancelled.", + "FileNotFound": "Ficheiro n\u00e3o encontrado", + "FileReadError": "Ocorreu um erro ao ler o ficheiro.", + "DeleteUser": "Apagar Utilizador", + "DeleteUserConfirmation": "Tem a certeza que pretende apagar {0}?", + "PasswordResetHeader": "Password Reset", + "PasswordResetComplete": "The password has been reset.", + "PasswordResetConfirmation": "Are you sure you wish to reset the password?", + "PasswordSaved": "Senha guardada.", + "PasswordMatchError": "Password and password confirmation must match." +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index 0d428742c..6f39c6759 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -1,9 +1,9 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Serialization; using MoreLinq; using System; using System.Collections.Concurrent; @@ -11,6 +11,8 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.Server.Implementations.Localization { @@ -33,15 +35,17 @@ namespace MediaBrowser.Server.Implementations.Localization new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase); private readonly IFileSystem _fileSystem; - + private readonly IJsonSerializer _jsonSerializer; + /// <summary> /// Initializes a new instance of the <see cref="LocalizationManager"/> class. /// </summary> /// <param name="configurationManager">The configuration manager.</param> - public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem) + public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer) { _configurationManager = configurationManager; _fileSystem = fileSystem; + _jsonSerializer = jsonSerializer; ExtractAll(); } @@ -241,5 +245,112 @@ namespace MediaBrowser.Server.Implementations.Localization return value == null ? (int?)null : value.Value; } + + public string GetLocalizedString(string phrase) + { + return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture); + } + + public string GetLocalizedString(string phrase, string culture) + { + var dictionary = GetLocalizationDictionary(culture); + + string value; + + if (dictionary.TryGetValue(phrase, out value)) + { + return value; + } + + return phrase; + } + + private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries = + new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); + + public Dictionary<string, string> GetLocalizationDictionary(string culture) + { + const string prefix = "Server"; + var key = prefix + culture; + + return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "server.json")); + } + + public Dictionary<string, string> GetJavaScriptLocalizationDictionary(string culture) + { + const string prefix = "JavaScript"; + var key = prefix + culture; + + return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "javascript.json")); + } + + private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename) + { + var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + + var assembly = GetType().Assembly; + var namespaceName = GetType().Namespace + "." + prefix; + + CopyInto(dictionary, namespaceName + "." + baseFilename, assembly); + CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture), assembly); + + return dictionary; + } + + private void CopyInto(IDictionary<string, string> dictionary, string resourcePath, Assembly assembly) + { + using (var stream = assembly.GetManifestResourceStream(resourcePath)) + { + if (stream != null) + { + var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream); + + foreach (var key in dict.Keys) + { + dictionary[key] = dict[key]; + } + } + } + } + + private string GetResourceFilename(string culture) + { + var parts = culture.Split('-'); + + if (parts.Length == 2) + { + culture = parts[0].ToLower() + "_" + parts[1].ToUpper(); + } + else + { + culture = culture.ToLower(); + } + + return culture + ".json"; + } + + public IEnumerable<LocalizatonOption> GetLocalizationOptions() + { + return new List<LocalizatonOption> + { + new LocalizatonOption{ Name="English (United States)", Value="en-us"}, + new LocalizatonOption{ Name="German", Value="de"}, + new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"}, + new LocalizatonOption{ Name="Russian", Value="ru"} + + }.OrderBy(i => i.Name); + } + + public string LocalizeDocument(string document, string culture, Func<string,string> tokenBuilder) + { + foreach (var pair in GetLocalizationDictionary(culture).ToList()) + { + var token = tokenBuilder(pair.Key); + + document = document.Replace(token, pair.Value, StringComparison.Ordinal); + } + + return document; + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/de.json b/MediaBrowser.Server.Implementations/Localization/Server/de.json new file mode 100644 index 000000000..ab3271b50 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Server/de.json @@ -0,0 +1,17 @@ +{ + "LabelExit": "Ende", + "LabelVisitCommunity": "Besuche die Community", + "LabelGithubWiki": "Github Wiki", + "LabelSwagger": "Swagger", + "LabelStandard": "Standard", + "LabelViewApiDocumentation": "Zeige Api Dokumentation", + "LabelBrowseLibrary": "Durchsuche Bibliothek", + "LabelConfigureMediaBrowser": "Konfiguriere Media Browser", + "LabelOpenLibraryViewer": "\u00d6ffne Bibliothekenansicht", + "LabelRestartServer": "Server neustarten", + "LabelShowLogWindow": "Zeige Log Fenster", + "LabelPrevious": "Vorheriges", + "LabelFinish": "Ende", + "LabelNext": "N\u00e4chstes", + "LabelYoureDone": "Du bist fertig!" +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json new file mode 100644 index 000000000..f74cca1eb --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json @@ -0,0 +1,17 @@ +{ + "LabelExit": "Exit", + "LabelVisitCommunity": "Visit Community", + "LabelGithubWiki": "Github Wiki", + "LabelSwagger": "Swagger", + "LabelStandard": "Standard", + "LabelViewApiDocumentation": "View Api Documentation", + "LabelBrowseLibrary": "Browse Library", + "LabelConfigureMediaBrowser": "Configure Media Browser", + "LabelOpenLibraryViewer": "Open Library Viewer", + "LabelRestartServer": "Restart Server", + "LabelShowLogWindow": "Show Log Window", + "LabelPrevious": "Previous", + "LabelFinish": "Finish", + "LabelNext": "Next", + "LabelYoureDone": "You're Done!" +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json new file mode 100644 index 000000000..700314708 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json @@ -0,0 +1,17 @@ +{ + "LabelExit": "Sair", + "LabelVisitCommunity": "Visitar a Comunidade", + "LabelGithubWiki": "Wiki Github", + "LabelSwagger": "Swagger", + "LabelStandard": "Padr\u00e3o", + "LabelViewApiDocumentation": "Ver Documenta\u00e7\u00e3o da API", + "LabelBrowseLibrary": "Navegar na Biblioteca", + "LabelConfigureMediaBrowser": "Configurar Media Browser", + "LabelOpenLibraryViewer": "Abrir Visualizador da Biblioteca", + "LabelRestartServer": "Reiniciar Servidor", + "LabelShowLogWindow": "Mostrar Janela de Log", + "LabelPrevious": "Anterior", + "LabelFinish": "Terminar", + "LabelNext": "Seguinte", + "LabelYoureDone": "Concluiu!" +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ru.json b/MediaBrowser.Server.Implementations/Localization/Server/ru.json new file mode 100644 index 000000000..4cc6b90f7 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json @@ -0,0 +1,17 @@ +{ + "LabelExit": "\u0412\u044b\u0445\u043e\u0434", + "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0421\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e", + "LabelGithubWiki": "\u0412\u0438\u043a\u0438 \u043d\u0430 Github", + "LabelSwagger": "\u041e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u0435", + "LabelStandard": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439", + "LabelViewApiDocumentation": "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e API", + "LabelBrowseLibrary": "\u041e\u0431\u043e\u0437\u0440\u0435\u0432\u0430\u0442\u0435\u043b\u044c \u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0438", + "LabelConfigureMediaBrowser": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Media Browser", + "LabelOpenLibraryViewer": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0438", + "LabelRestartServer": "\u041f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440", + "LabelShowLogWindow": "\u041e\u043a\u043d\u043e \u0416\u0443\u0440\u043d\u0430\u043b\u0430", + "LabelPrevious": "\u041f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0435", + "LabelFinish": "\u0417\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c", + "LabelNext": "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435", + "LabelYoureDone": "\u0412\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438!" +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json new file mode 100644 index 000000000..62a418391 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -0,0 +1,17 @@ +{ + "LabelExit": "Exit", + "LabelVisitCommunity": "Visit Community", + "LabelGithubWiki": "Github Wiki", + "LabelSwagger": "Swagger", + "LabelStandard": "Standard", + "LabelViewApiDocumentation": "View Api Documentation", + "LabelBrowseLibrary": "Browse Library", + "LabelConfigureMediaBrowser": "Configure Media Browser", + "LabelOpenLibraryViewer": "Open Library Viewer", + "LabelRestartServer": "Restart Server", + "LabelShowLogWindow": "Show Log Window", + "LabelPrevious": "Previous", + "LabelFinish": "Finish", + "LabelNext": "Next", + "LabelYoureDone": "You're Done!" +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index ea7ef2ed6..539b968c7 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -141,6 +141,7 @@ <Compile Include="IO\LibraryMonitor.cs" /> <Compile Include="Library\CoreResolutionIgnoreRule.cs" /> <Compile Include="Library\LibraryManager.cs" /> + <Compile Include="Library\MusicManager.cs" /> <Compile Include="Library\Resolvers\PhotoResolver.cs" /> <Compile Include="Library\SearchEngine.cs" /> <Compile Include="Library\ResolverHelper.cs" /> @@ -281,6 +282,14 @@ <EmbeddedResource Include="Localization\Ratings\ru.txt" /> </ItemGroup> <ItemGroup> + <EmbeddedResource Include="Localization\JavaScript\javascript.json" /> + <EmbeddedResource Include="Localization\Server\server.json" /> + <EmbeddedResource Include="Localization\Server\de.json" /> + <EmbeddedResource Include="Localization\Server\pt_PT.json" /> + <EmbeddedResource Include="Localization\Server\ru.json" /> + <EmbeddedResource Include="Localization\JavaScript\en_US.json" /> + <EmbeddedResource Include="Localization\JavaScript\pt_PT.json" /> + <EmbeddedResource Include="Localization\Server\en_US.json" /> <None Include="packages.config" /> </ItemGroup> <ItemGroup> @@ -385,6 +394,7 @@ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(SolutionDir)\.nuget\nuget.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. diff --git a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs index 0e2f9e1b5..7c8d71b4f 100644 --- a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs +++ b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Roku public bool SupportsMediaRemoteControl { - get { return true; } + get { return false; } } public bool IsSessionActive @@ -146,5 +146,16 @@ namespace MediaBrowser.Server.Implementations.Roku RequestContentType = "application/json" }); } + + + public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) + { + return SendCommand(new WebSocketMessage<GenericCommand> + { + MessageType = "Command", + Data = command + + }, cancellationToken); + } } } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 9d405a175..00d2aa992 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Server.Implementations.Session private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; + private readonly IMusicManager _musicManager; /// <summary> /// Gets or sets the configuration manager. @@ -564,7 +565,7 @@ namespace MediaBrowser.Server.Implementations.Session return playedToCompletion; } - + /// <summary> /// Updates playstate position for an item but does not save /// </summary> @@ -666,7 +667,7 @@ namespace MediaBrowser.Server.Implementations.Session var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - + return session.SessionController.SendSystemCommand(command, cancellationToken); } @@ -676,7 +677,7 @@ namespace MediaBrowser.Server.Implementations.Session var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - + return session.SessionController.SendMessageCommand(command, cancellationToken); } @@ -684,14 +685,35 @@ namespace MediaBrowser.Server.Implementations.Session { var session = GetSessionForRemoteControl(sessionId); - var items = command.ItemIds.Select(i => _libraryManager.GetItemById(new Guid(i))) - .Where(i => i.LocationType != LocationType.Virtual) - .ToList(); + var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null; - if (session.UserId.HasValue) + List<BaseItem> items; + + if (command.PlayCommand == PlayCommand.PlayInstantMix) { - var user = _userManager.GetUserById(session.UserId.Value); + items = command.ItemIds.SelectMany(i => TranslateItemForInstantMix(i, user)) + .Where(i => i.LocationType != LocationType.Virtual) + .ToList(); + command.PlayCommand = PlayCommand.PlayNow; + } + else + { + items = command.ItemIds.SelectMany(i => TranslateItemForPlayback(i, user)) + .Where(i => i.LocationType != LocationType.Virtual) + .ToList(); + } + + if (command.PlayCommand == PlayCommand.PlayShuffle) + { + items = items.OrderBy(i => Guid.NewGuid()).ToList(); + command.PlayCommand = PlayCommand.PlayNow; + } + + command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray(); + + if (user != null) + { if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full)) { throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); @@ -723,13 +745,69 @@ namespace MediaBrowser.Server.Implementations.Session return session.SessionController.SendPlayCommand(command, cancellationToken); } + private IEnumerable<BaseItem> TranslateItemForPlayback(string id, User user) + { + var item = _libraryManager.GetItemById(new Guid(id)); + + if (item.IsFolder) + { + var folder = (Folder)item; + + var items = user == null ? folder.RecursiveChildren : + folder.GetRecursiveChildren(user); + + items = items.Where(i => !i.IsFolder); + + items = items.OrderBy(i => i.SortName); + + return items; + } + + return new[] { item }; + } + + private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user) + { + var item = _libraryManager.GetItemById(new Guid(id)); + + var audio = item as Audio; + + if (audio != null) + { + return _musicManager.GetInstantMixFromSong(audio, user); + } + + var artist = item as MusicArtist; + + if (artist != null) + { + return _musicManager.GetInstantMixFromArtist(artist.Name, user); + } + + var album = item as MusicAlbum; + + if (album != null) + { + return _musicManager.GetInstantMixFromAlbum(album, user); + } + + var genre = item as MusicGenre; + + if (genre != null) + { + return _musicManager.GetInstantMixFromGenres(new[] { genre.Name }, user); + } + + return new BaseItem[] { }; + } + public Task SendBrowseCommand(Guid controllingSessionId, Guid sessionId, BrowseRequest command, CancellationToken cancellationToken) { var session = GetSessionForRemoteControl(sessionId); var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - + return session.SessionController.SendBrowseCommand(command, cancellationToken); } diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index 70d7ac071..ddf4ec2ca 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs @@ -198,5 +198,17 @@ namespace MediaBrowser.Server.Implementations.Session }, cancellationToken); } + + public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) + { + var socket = GetActiveSocket(); + + return socket.SendAsync(new WebSocketMessage<GenericCommand> + { + MessageType = "Command", + Data = command + + }, cancellationToken); + } } } |
