diff --git a/PlexRequests.Core/IPlexReadOnlyDatabase.cs b/PlexRequests.Core/IPlexReadOnlyDatabase.cs new file mode 100644 index 000000000..93a4dc98f --- /dev/null +++ b/PlexRequests.Core/IPlexReadOnlyDatabase.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Core +{ + public interface IPlexReadOnlyDatabase + { + IEnumerable GetItemsAddedAfterDate(DateTime dateTime); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/PlexReadOnlyDatabase.cs b/PlexRequests.Core/PlexReadOnlyDatabase.cs new file mode 100644 index 000000000..ff4291756 --- /dev/null +++ b/PlexRequests.Core/PlexReadOnlyDatabase.cs @@ -0,0 +1,68 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexReadOnlyDatabase.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using PlexRequests.Core.SettingModels; +using PlexRequests.Store; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Core +{ + public class PlexReadOnlyDatabase : IPlexReadOnlyDatabase + { + public PlexReadOnlyDatabase(IPlexDatabase plexDatabase, ISettingsService plexSettings) + { + Plex = plexDatabase; + + var settings = plexSettings.GetSettings(); + + if (!string.IsNullOrEmpty(settings.PlexDatabaseLocationOverride)) + { + Plex.DbLocation = settings.PlexDatabaseLocationOverride; + } + else if (Type.GetType("Mono.Runtime") != null) + { + // Mono + Plex.DbLocation = Path.Combine("/var/lib/plexmediaserver/Library/Application Support/", "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db"); + } + else + { + // Default Windows + Plex.DbLocation = Path.Combine(Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%"), "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db"); + } + } + private IPlexDatabase Plex { get; } + + public IEnumerable GetItemsAddedAfterDate(DateTime dateTime) + { + return Plex.QueryMetadataItems("select * from metadata_items where added_at > @AddedAt", + new { AddedAt = dateTime }); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index 57f0e7dfc..143508e1e 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -69,6 +69,7 @@ + @@ -85,6 +86,7 @@ + diff --git a/PlexRequests.Core/SettingModels/PlexSettings.cs b/PlexRequests.Core/SettingModels/PlexSettings.cs index 09be5fb15..108edee19 100644 --- a/PlexRequests.Core/SettingModels/PlexSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexSettings.cs @@ -40,5 +40,6 @@ namespace PlexRequests.Core.SettingModels public string PlexAuthToken { get; set; } public string MachineIdentifier { get; set; } + public string PlexDatabaseLocationOverride { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/RecentlyAdded.cs b/PlexRequests.Services/Jobs/RecentlyAdded.cs index 6f0e76f3a..732d7be44 100644 --- a/PlexRequests.Services/Jobs/RecentlyAdded.cs +++ b/PlexRequests.Services/Jobs/RecentlyAdded.cs @@ -1,4 +1,5 @@ #region Copyright + // /************************************************************************ // Copyright (c) 2016 Jamie Rees // File: RecentlyAdded.cs @@ -23,6 +24,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ + #endregion using System; @@ -118,7 +120,7 @@ namespace PlexRequests.Services.Jobs recentlyAdded._children.Where(x => x.type.Equals("Movie", StringComparison.CurrentCultureIgnoreCase)); var tv = recentlyAdded._children.Where( - x => x.type.Equals("season", StringComparison.CurrentCultureIgnoreCase)) + x => x.type.Equals("season", StringComparison.CurrentCultureIgnoreCase)) .GroupBy(x => x.parentTitle) .Select(x => x.FirstOrDefault()); @@ -131,42 +133,61 @@ namespace PlexRequests.Services.Jobs Send(html, plexSettings, testEmail); } - private void GenerateMovieHtml(IEnumerable movies, PlexSettings plexSettings, ref StringBuilder sb) + private void GenerateMovieHtml(IEnumerable movies, PlexSettings plexSettings, + ref StringBuilder sb) { sb.Append("

New Movies:



"); - sb.Append(""); + sb.Append( + "
"); foreach (var movie in movies) { - var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - movie.ratingKey.ToString()); + var plexGUID = string.Empty; + try + { + var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, + movie.ratingKey.ToString()); - var imdbId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Video.Guid); - var info = _movieApi.GetMovieInformation(imdbId).Result; + plexGUID = metaData.Video.Guid; - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append( + ""); + if (info.Genres.Any()) + { + sb.AppendFormat( + "

Genre: {0}

", + string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())); + } + sb.AppendFormat( + "

{0}

", + info.Overview); + + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + sb.Append(""); + } + catch (Exception e) + { + Log.Error(e); + Log.Error("Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", plexGUID); + } } sb.Append("
"); - sb.AppendFormat("", info.BackdropPath); - sb.Append("
"); + var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID); + var info = _movieApi.GetMovieInformation(imdbId).Result; - sb.AppendFormat("

{1} {2}

", - info.ImdbId, info.Title, info.ReleaseDate?.ToString("yyyy") ?? string.Empty); + sb.Append("

"); + sb.AppendFormat( + "", + info.BackdropPath); + sb.Append("
"); - if (info.Genres.Any()) - { sb.AppendFormat( - "

Genre: {0}

", - string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())); - } - sb.AppendFormat("

{0}

", info.Overview); + "

{1} {2}

", + info.ImdbId, info.Title, info.ReleaseDate?.ToString("yyyy") ?? string.Empty); - sb.Append(""); - sb.Append("
"); - sb.Append("
"); - sb.Append("



"); @@ -176,38 +197,52 @@ namespace PlexRequests.Services.Jobs { // TV sb.Append("

New Episodes:



"); - sb.Append(""); + sb.Append( + "
"); foreach (var t in tv) { - var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - t.parentRatingKey.ToString()); + var plexGUID = string.Empty; + try + { + + var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, + t.parentRatingKey.ToString()); + + plexGUID = parentMetaData.Directory.Guid; - var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(parentMetaData.Directory.Guid))); - var banner = info.image?.original; - if (!string.IsNullOrEmpty(banner)) + var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID))); + + var banner = info.image?.original; + if (!string.IsNullOrEmpty(banner)) + { + banner = banner.Replace("http", "https"); // Always use the Https banners + } + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + } + catch (Exception e) { - banner = banner.Replace("http", "https"); // Always use the Https banners + Log.Error(e); + Log.Error("Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", plexGUID); } - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); } sb.Append("
"); + sb.AppendFormat("", banner); + sb.Append("
"); + + sb.AppendFormat("

{1} {2}

", + info.externals.imdb, info.name, info.premiered.Substring(0, 4)); // Only the year + + sb.AppendFormat("

Genre: {0}

", string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())); + sb.AppendFormat("

{0}

", + string.IsNullOrEmpty(parentMetaData.Directory.Summary) ? info.summary : parentMetaData.Directory.Summary); // Episode Summary + + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + sb.Append("

"); - sb.AppendFormat("", banner); - sb.Append("
"); - - sb.AppendFormat("

{1} {2}

", - info.externals.imdb, info.name, info.premiered.Substring(0, 4)); // Only the year - - sb.AppendFormat("

Genre: {0}

", string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())); - sb.AppendFormat("

{0}

", - string.IsNullOrEmpty(parentMetaData.Directory.Summary) ? info.summary : parentMetaData.Directory.Summary); // Episode Summary - - sb.Append(""); - sb.Append("
"); - sb.Append("
"); - sb.Append("



"); } diff --git a/PlexRequests.Store/IPlexDatabase.cs b/PlexRequests.Store/IPlexDatabase.cs new file mode 100644 index 000000000..ac8b6fbea --- /dev/null +++ b/PlexRequests.Store/IPlexDatabase.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Store +{ + public interface IPlexDatabase + { + IEnumerable GetMetadata(); + string DbLocation { get; set; } + Task> GetMetadataAsync(); + IEnumerable QueryMetadataItems(string query, object param); + } +} \ No newline at end of file diff --git a/PlexRequests.Store/Models/Plex/MetadataItems.cs b/PlexRequests.Store/Models/Plex/MetadataItems.cs new file mode 100644 index 000000000..179026c03 --- /dev/null +++ b/PlexRequests.Store/Models/Plex/MetadataItems.cs @@ -0,0 +1,95 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: MetadataItems.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Data.Linq.Mapping; + +namespace PlexRequests.Store.Models.Plex +{ + [Table(Name = "metadata_items")] + public class MetadataItems + { + [Column(IsPrimaryKey = true)] + public int Id { get; set; } + + [Column(Name = "library_section_id")] + public int LibrarySectionId { get; set; } + + [Column(Name = "parent_id")] + public int ParentId { get; set; } + + [Column(Name = "metadata_type")] + public int MetadataType { get; set; } + + [Column(Name = "guid")] + public string Guid { get; set; } + + [Column(Name = "media_item_count")] + public int MediaItemCount { get; set; } + + [Column(Name = "title")] + public string Title { get; set; } + + [Column(Name = "title_sort")] + public string TitleSort { get; set; } + + [Column(Name = "OriginalTitle")] + public string OriginalTitle { get; set; } + + [Column(Name = "studio")] + public string Studio { get; set; } + [Column(Name = "rating")] + public float Rating { get; set; } + [Column(Name = "rating_count")] + public int RatingCount { get; set; } + [Column(Name = "tagline")] + public string Tagline { get; set; } + [Column(Name = "summary")] + public string Summary { get; set; } + [Column(Name = "trivia")] + public string Trivia { get; set; } + [Column(Name = "quotes")] + public string Quotes { get; set; } + [Column(Name = "content_rating")] + public string ContentRating { get; set; } + [Column(Name = "content_rating_age")] + public int ContentRatingAge { get; set; } + [Column(Name = "Index")] + public int Index { get; set; } + // SKIP Until Date Times + + [Column(Name = "originally_available_at")] + public DateTime OriginallyAvailableAt { get; set; } + [Column(Name = "available_at")] + public DateTime AvailableAt { get; set; } + [Column(Name = "expires_at")] + public DateTime ExpiresAt { get; set; } + // Skip RefreshedAt and Year + [Column(Name = "added_at")] + public DateTime AddedAt { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexDatabase.cs b/PlexRequests.Store/PlexDatabase.cs new file mode 100644 index 000000000..edfc6cf22 --- /dev/null +++ b/PlexRequests.Store/PlexDatabase.cs @@ -0,0 +1,91 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexDatabase.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading.Tasks; +using Dapper; +using Dapper.Contrib.Extensions; +using Mono.Data.Sqlite; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Store +{ + /// + /// We should only ever READ, NEVER WRITE! + /// + public class PlexDatabase : IPlexDatabase + { + public PlexDatabase(SqliteFactory provider) + { + Factory = provider; + } + + private SqliteFactory Factory { get; } + /// + /// https://support.plex.tv/hc/en-us/articles/202915258-Where-is-the-Plex-Media-Server-data-directory-located- + /// + public string DbLocation { get; set; } + + private IDbConnection DbConnection() + { + var fact = Factory.CreateConnection(); + if (fact == null) + { + throw new SqliteException("Factory returned null"); + } + fact.ConnectionString = "Data Source=" + "Plex Path"; + return fact; + } + + public IEnumerable GetMetadata() + { + using (var con = DbConnection()) + { + return con.GetAll(); + } + } + + public async Task> GetMetadataAsync() + { + using (var con = DbConnection()) + { + return await con.GetAllAsync(); + } + } + + public IEnumerable QueryMetadataItems(string query, object param) + { + using (var con = DbConnection()) + { + return con.Query(query, param); + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexRequests.Store.csproj b/PlexRequests.Store/PlexRequests.Store.csproj index 1e6a22cb6..78f777dbc 100644 --- a/PlexRequests.Store/PlexRequests.Store.csproj +++ b/PlexRequests.Store/PlexRequests.Store.csproj @@ -64,12 +64,15 @@ + + + @@ -120,6 +123,7 @@ PlexRequests.Helpers +