Преглед на файлове

display news on dashboard home page

Luke Pulverenti преди 11 години
родител
ревизия
7f51148130

+ 1 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -93,6 +93,7 @@
     <Compile Include="LiveTv\LiveTvService.cs" />
     <Compile Include="LiveTv\LiveTvService.cs" />
     <Compile Include="LocalizationService.cs" />
     <Compile Include="LocalizationService.cs" />
     <Compile Include="MoviesService.cs" />
     <Compile Include="MoviesService.cs" />
+    <Compile Include="NewsService.cs" />
     <Compile Include="NotificationsService.cs" />
     <Compile Include="NotificationsService.cs" />
     <Compile Include="PackageReviewService.cs" />
     <Compile Include="PackageReviewService.cs" />
     <Compile Include="PackageService.cs" />
     <Compile Include="PackageService.cs" />

+ 48 - 0
MediaBrowser.Api/NewsService.cs

@@ -0,0 +1,48 @@
+using MediaBrowser.Controller.News;
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Querying;
+using ServiceStack;
+
+namespace MediaBrowser.Api
+{
+    [Route("/News/Product", "GET")]
+    [Api(Description = "Gets search hints based on a search term")]
+    public class GetProductNews : IReturn<QueryResult<NewsItem>>
+    {
+        /// <summary>
+        /// Skips over a given number of items within the results. Use for paging.
+        /// </summary>
+        /// <value>The start index.</value>
+        [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? StartIndex { get; set; }
+
+        /// <summary>
+        /// The maximum number of items to return
+        /// </summary>
+        /// <value>The limit.</value>
+        [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? Limit { get; set; }
+    }
+    
+    public class NewsService : BaseApiService
+    {
+        private readonly INewsService _newsService;
+
+        public NewsService(INewsService newsService)
+        {
+            _newsService = newsService;
+        }
+
+        public object Get(GetProductNews request)
+        {
+            var result = _newsService.GetProductNews(new NewsQuery
+            {
+                 StartIndex = request.StartIndex,
+                 Limit = request.Limit
+
+            }).Result;
+
+            return ToOptimizedResult(result);
+        }
+    }
+}

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -136,6 +136,7 @@
     <Compile Include="Net\IHttpResultFactory.cs" />
     <Compile Include="Net\IHttpResultFactory.cs" />
     <Compile Include="Net\IHttpServer.cs" />
     <Compile Include="Net\IHttpServer.cs" />
     <Compile Include="Net\IRestfulService.cs" />
     <Compile Include="Net\IRestfulService.cs" />
+    <Compile Include="News\INewsService.cs" />
     <Compile Include="Notifications\INotificationsRepository.cs" />
     <Compile Include="Notifications\INotificationsRepository.cs" />
     <Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
     <Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
     <Compile Include="Persistence\MediaStreamQuery.cs" />
     <Compile Include="Persistence\MediaStreamQuery.cs" />

+ 19 - 0
MediaBrowser.Controller/News/INewsService.cs

@@ -0,0 +1,19 @@
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Querying;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.News
+{
+    /// <summary>
+    /// Interface INewsFeed
+    /// </summary>
+    public interface INewsService
+    {
+        /// <summary>
+        /// Gets the product news.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>IEnumerable{NewsItem}.</returns>
+        Task<QueryResult<NewsItem>> GetProductNews(NewsQuery query);
+    }
+}

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -290,6 +290,9 @@
     <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
     <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
       <Link>Net\WebSocketState.cs</Link>
       <Link>Net\WebSocketState.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\News\NewsItem.cs">
+      <Link>News\NewsItem.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
     <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
       <Link>Notifications\Notification.cs</Link>
       <Link>Notifications\Notification.cs</Link>
     </Compile>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -277,6 +277,9 @@
     <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
     <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
       <Link>Net\WebSocketState.cs</Link>
       <Link>Net\WebSocketState.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\News\NewsItem.cs">
+      <Link>News\NewsItem.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
     <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
       <Link>Notifications\Notification.cs</Link>
       <Link>Notifications\Notification.cs</Link>
     </Compile>
     </Compile>

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -80,6 +80,7 @@
     <Compile Include="LiveTv\RecordingStatus.cs" />
     <Compile Include="LiveTv\RecordingStatus.cs" />
     <Compile Include="LiveTv\SeriesTimerInfoDto.cs" />
     <Compile Include="LiveTv\SeriesTimerInfoDto.cs" />
     <Compile Include="LiveTv\TimerInfoDto.cs" />
     <Compile Include="LiveTv\TimerInfoDto.cs" />
+    <Compile Include="News\NewsItem.cs" />
     <Compile Include="Providers\ImageProviderInfo.cs" />
     <Compile Include="Providers\ImageProviderInfo.cs" />
     <Compile Include="Providers\RemoteImageInfo.cs" />
     <Compile Include="Providers\RemoteImageInfo.cs" />
     <Compile Include="Dto\StudioDto.cs" />
     <Compile Include="Dto\StudioDto.cs" />

+ 29 - 0
MediaBrowser.Model/News/NewsItem.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.News
+{
+    public class NewsChannel
+    {
+        public string Title { get; set; }
+        public string Link { get; set; }
+        public string Description { get; set; }
+        public List<NewsItem> Items { get; set; }
+    }
+
+    public class NewsItem
+    {
+        public string Title { get; set; }
+        public string Link { get; set; }
+        public string Description { get; set; }
+        public string Guid { get; set; }
+        public DateTime Date { get; set; }
+    }
+
+    public class NewsQuery
+    {
+        public int? StartIndex { get; set; }
+
+        public int? Limit { get; set; }
+    }
+}

+ 1 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -173,6 +173,7 @@
     <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
     <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
     <Compile Include="Localization\LocalizationManager.cs" />
     <Compile Include="Localization\LocalizationManager.cs" />
     <Compile Include="MediaEncoder\MediaEncoder.cs" />
     <Compile Include="MediaEncoder\MediaEncoder.cs" />
+    <Compile Include="News\NewsService.cs" />
     <Compile Include="Persistence\SqliteChapterRepository.cs" />
     <Compile Include="Persistence\SqliteChapterRepository.cs" />
     <Compile Include="Persistence\SqliteExtensions.cs" />
     <Compile Include="Persistence\SqliteExtensions.cs" />
     <Compile Include="Persistence\SqliteMediaStreamsRepository.cs" />
     <Compile Include="Persistence\SqliteMediaStreamsRepository.cs" />

+ 132 - 0
MediaBrowser.Server.Implementations/News/NewsService.cs

@@ -0,0 +1,132 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.News;
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace MediaBrowser.Server.Implementations.News
+{
+    public class NewsService : INewsService
+    {
+        private readonly IApplicationPaths _appPaths;
+        private readonly IFileSystem _fileSystem;
+        private readonly IHttpClient _httpClient;
+
+        public NewsService(IApplicationPaths appPaths, IFileSystem fileSystem, IHttpClient httpClient)
+        {
+            _appPaths = appPaths;
+            _fileSystem = fileSystem;
+            _httpClient = httpClient;
+        }
+
+        public async Task<QueryResult<NewsItem>> GetProductNews(NewsQuery query)
+        {
+            var path = Path.Combine(_appPaths.CachePath, "news.xml");
+
+            await EnsureNewsFile(path).ConfigureAwait(false);
+
+            var items = GetNewsItems(path);
+
+            var itemsArray = items.ToArray();
+            var count = itemsArray.Length;
+
+            if (query.StartIndex.HasValue)
+            {
+                itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
+            }
+
+            if (query.Limit.HasValue)
+            {
+                itemsArray = itemsArray.Take(query.Limit.Value).ToArray();
+            }
+
+            return new QueryResult<NewsItem>
+            {
+                Items = itemsArray,
+                TotalRecordCount = count
+            };
+        }
+
+        private IEnumerable<NewsItem> GetNewsItems(string path)
+        {
+            var xmlDoc = new XmlDocument();
+
+            xmlDoc.Load(path);
+
+            return ParseRssItems(xmlDoc);
+        }
+
+        private IEnumerable<NewsItem> ParseRssItems(XmlDocument xmlDoc)
+        {
+            var nodes = xmlDoc.SelectNodes("rss/channel/item");
+
+            if (nodes == null)
+            {
+                yield return null;
+            }
+
+            foreach (XmlNode node in nodes)
+            {
+                var newsItem = new NewsItem();
+
+                newsItem.Title = ParseDocElements(node, "title");
+
+                newsItem.Description = ParseDocElements(node, "description");
+
+                newsItem.Link = ParseDocElements(node, "link");
+
+                var date = ParseDocElements(node, "pubDate");
+                DateTime parsedDate;
+
+                if (DateTime.TryParse(date, out parsedDate))
+                {
+                    newsItem.Date = parsedDate;
+                }
+
+                yield return newsItem;
+            }
+        }
+
+        private string ParseDocElements(XmlNode parent, string xPath)
+        {
+            var node = parent.SelectSingleNode(xPath);
+
+            return node != null ? node.InnerText : "Unresolvable";
+        }
+
+
+        /// <summary>
+        /// Ensures the news file.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <returns>Task.</returns>
+        private async Task EnsureNewsFile(string path)
+        {
+            var info = _fileSystem.GetFileSystemInfo(path);
+
+            if (!info.Exists || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(info)).TotalHours > 24)
+            {
+                var requestOptions = new HttpRequestOptions
+                {
+                    Url = "http://mediabrowser3.com/community/index.php?/blog/rss/1-media-browser-developers-blog",
+                    Progress = new Progress<double>()
+                };
+
+                using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
+                {
+                    using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+                    {
+                        await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+                    }
+                }
+            }
+        }
+    }
+}

+ 5 - 0
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -19,6 +19,7 @@ using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.News;
 using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Plugins;
@@ -283,6 +284,10 @@ namespace MediaBrowser.ServerApplication
 
 
             DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
             DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
             RegisterSingleInstance(DtoService);
             RegisterSingleInstance(DtoService);
+
+            var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, FileSystemManager, HttpClient);
+            RegisterSingleInstance<INewsService>(newsService);
+
             progress.Report(15);
             progress.Report(15);
 
 
             var innerProgress = new ActionableProgress<double>();
             var innerProgress = new ActionableProgress<double>();

+ 13 - 0
MediaBrowser.WebDashboard/ApiClient.js

@@ -197,6 +197,19 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             return webSocket && (webSocket.readyState === WebSocket.OPEN || webSocket.readyState === WebSocket.CONNECTING);
             return webSocket && (webSocket.readyState === WebSocket.OPEN || webSocket.readyState === WebSocket.CONNECTING);
         };
         };
 
 
+        self.getProductNews = function (options) {
+
+            options = options || {};
+
+            var url = self.getUrl("News/Product", options);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
         /**
         /**
          * Gets an item from the server
          * Gets an item from the server
          * Omit itemId to get the root folder.
          * Omit itemId to get the root folder.

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.237" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.238" targetFramework="net45" />
 </packages>
 </packages>