diff --git a/.gitignore b/.gitignore index 771a7bacd..587f09568 100644 --- a/.gitignore +++ b/.gitignore @@ -243,3 +243,6 @@ _Pvt_Extensions # CAKE - C# Make /Tools/* *.db-journal + +# Ignore local vscode config +*.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index ea279eae7..655689bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4344 +1,1596 @@ # Changelog -## v3.0.3111 (2018-03-27) - -### **New Features** - -- Added the Recently Added Newsletter! You are welcome. [tidusjar] - -- Added a new scrollbar to Ombi. [tidusjar] - -- Added the ability to automatically generate the API Key on startup if it does not exist #2070. [tidusjar] - -- Updated npm dependancies. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added recently added stuff. [Jamie] - -- Added the recently added engine with some basic methods. [Jamie] - -- Added the ability to refresh out backend metadata (#2078) [Jamie] - -- Update README.md. [zobe123] - -### **Fixes** - -- Specific favicons for different platforms. [louis-lau] - -- MovieDbId was switched to string fron number so accomodated for change. [Anojh] - -- Removing duplicate functions. [Anojh Thayaparan] - -- Conflict resolving and adopting Jamie's new method. [Anojh] - -- Wrote new calls to just get poster and bg. [Anojh] - -- Fix for issue #1907, which is to add content poster and bg to issue details page. [Anojh] - -- Dynamic Background Animation. [Anojh] - -- Improved the message for #2037. [tidusjar] - -- Improved the way we use the notification variables, we have now split out the Username and Alias (Requested User is depricated but not removed) [tidusjar] - -- Removed redundant timers. [Anojh] - -- More optimizations by reducing requests. [Anojh] - -- Improved version. [Anojh] - -- Dynamic Background Animation. [Anojh] - -- Fixed #2055 and #1903. [Jamie] - -- Small changes to the auto updater, let's see how this works. [Jamie] - -- Fixed build. [Jamie] - -- Fixed the update check for the master build. [Jamie] - -- Fixed build. [Jamie] - -- Fixed #2074 and #2079. [Jamie] - - -## v3.0.3030 (2018-03-14) - -### **New Features** - -- Updated the .Net core dependancies #2072. [Jamie] - -### **Fixes** - -- Delete Ombi.testdb. [Jamie] - - -## v3.0.3020 (2018-03-13) - -### **Fixes** - -- Small memory improvements in the Plex Sync. [Jamie] - -- Fixed the sort issue on the user Management page. Also added sorting to the Movie Requests page. [tidusjar] - -- Downgraded the angular2-jwt library since it has a bug in it. #2064. [tidusjar] - -- Fixed an issue when Plex decideds to reuse the Plex Key for a different media item... #2038. [tidusjar] - -- Fixed an issue where we might show the Imdb link when we do not have a imdbid #1797. [tidusjar] - -- Fixed the issue where we can no longer select Pending Approval in the filters #2057. [tidusjar] - -- Fixed the API key not working when attempting to get requests #2058. [tidusjar] - -- Fixed #2056. [tidusjar] - -- Experimental, set the Webpack base root to the ombi base path if we have it. This should hopefully fix the reverse proxy issues. [Jamie] - -- Fixed #2056. [tidusjar] - - -## v3.0.3000 (2018-03-09) - -### **New Features** - -- Added the ability to override root and quality options in Sonarr (#2049) [Jamie] - -- Added Pending Approval into the filters list. [tidusjar] - -- Added the ability to hide requests that have not been made by that user (#2052) [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Louis Laureys] - -### **Fixes** - -- Fixed #2042. [Jamie] - - -## v3.0.0 (2018-03-04) - -### **New Features** - -- Update build.cake. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added a similar button to the movie searches. Makes movie discoverablility easier. [tidusjar] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [PotatoQuality] - -- Change the default templates to use {IssueUser} [Jamie] - -- Changed the base url validation. [tidusjar] - -- Added bulk editing (#1941) [Jamie] - -- Change the poster size to w300 #1932. [Jamie] - -- Added a default user agent on all API calls. [tidusjar] - -- Update request.service.ts. [Jamie] - -- Added a filter onto the movies requests page for some inital feedback. [Jamie] - -- Added ordering to the User Management screen. [Jamie] - -- Update README.md. [Jamie] - -- Added custom donation url (#1902) [m4tta] - -- Changed the url scheme to make it easier to parse. [Jamie] - -- Added Norwegian to the translation code, forgot to check this in. [Jamie] - -- Added Norwegian to the language dropdown. [Jamie] - -- Added the stuff needed for omBlur. [tidusjar] - -- Update README.md (#1872) [xnaas] - -- Update README.md. [Jamie] - -- Update plex.component.html. [Jamie] - -- Change plus to list in menu (#1855) [Louis Laureys] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added user request limits, We can now set the limit for a user. [tidusjar] - -- Updated the UI JWT framework. [Jamie] - -- Added missing migrations #1744. [Jamie] - -- Added the SickRage API integration. [Jamie] - -- Update the Emby Connect Username in the user importer. To update the emby connect email address we do it when the user logs in, since the only way to get that information is to use the users Username and Password, since we do not keep this information we cannot do it in the User Importer, but if they have successfully logged in via Emby Connect then we check if we need to update the email address on login. [Jamie] - -- Update the connectuserid for emby users. [tidusjar] - -- Added the ability to customize job scheudles. [Jamie] - -- Update README.md. [Jamie] - -- Added the option to import the plex admin. [tidusjar] - -- Added the route name as an Id on the container div #1698. [Jamie] - -- Updated packages including uglify-es and the package-lock #1683. [Jamie] - -- Updated to Angular5 with best practises. [Jamie] - -- Update README.md. [Jamie] - -- Added the new backgrounds for the requests pages. [tidusjar] - -- Added caching to the settings. [tidusjar] - -- Added some better handling when adding existing seasons to a tv show in the Plex cacher. [tidusjar] - -- Added Telegram Notification support, Not tested. [Jamie] - -- Added the new banner background for tv shows. [tidusjar] - -- Added a new customization option to provide a css link. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Changed to discord link. [tidusjar] - -- Added more translations. [Jamie] - -- Added more logging for #1643. [tidusjar] - -- Update README.md. [Jamie] - -- Update Crowdin configuration file. [Jamie] - -- Added more translations. [Jamie] - -- Update da.json (#1634) [Lixumos] - -- Added 32bit version of Ombi. [Jamie] - -- Added more translations. [Jamie] - -- Added translations. [Jamie] - -- Updated the npm packages. [Jamie.Rees] - -- Added four new endpoints to change the availability for TV shows and Movies #1601. [Jamie.Rees] - -- Added the ability to run a user defined update script #1460. [Jamie.Rees] - -- Added logging around creating the wizard user #1604. [tidusjar] - -- Added the option to run the content cacher from the settings page, it will no longer get triggered when we press save. [tidusjar] - -- Added the ability to specify how many episodes we should cache at a time. #1598. [tidusjar] - -- Added usersname and password option for the updater #1460. [Jamie.Rees] - -- Changed the way we download the .zip files in the auto updater #1460 This might make a difference to the permissions issue. but not 100% sure. [Jamie.Rees] - -- Changed cake. [Jamie.Rees] - -- Added feedback when we send a welcome email #1578. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Added some logging into the PlexCachers and set the log level to informational. [tidusjar] - -- Added Couchpotato support and fixed #1548. [tidusjar] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Update README.md. [Jamie] - -- Added the ability to use a cookie to login. Mainly for Org. [tidusjar] - -- Added support for a new TV and Movie provider. DogNZB. [tidusjar] - -- Added a PartlyAvailable flag for tv shows. [Jamie.Rees] - -- Added some feedback on the request buttons for movies #1536. [Jamie.Rees] - -- Added a fix for the poster path issue #1533. [Jamie.Rees] - -- Added back the log path writing #865. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Added support for email login #1513. [tidusjar] - -- Added some more logging around the plex user importer fixed the discord notification issue #1513. [tidusjar] - -- Update README.md. [PotatoQuality] - -- Update README.md. [PotatoQuality] - -- Update README.md. [PotatoQuality] - -- Update README.md. [PotatoQuality] - -- Update README.md. [PotatoQuality] - -- Update Readme for V3. [PotatoQuality] - -- Added some wiki pages and also made it so we cannot edit Plex Users email address (since they will get overwrote via the Importer) #865. [Jamie.Rees] - -- Added transparency to icon files (#1520) [Auwen] - -- Added an application URL in the customization settings #1513. [Jamie.Rees] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Added the ability to enable Plex User importing. We also allow you to exclude users #1456. [tidusjar] - -- Update Startup.cs. [Jamie] - -- Added an about page #865. [Jamie.Rees] - -- Changelog. [Jamie.Rees] - -- Changed the way we download the updates #865. [Jamie.Rees] - -- Updated packages and more logging. [Jamie.Rees] - -- Update versioning. [Jamie.Rees] - -- Update nuget packages and added logging to the Updater #865. [tidusjar] - -- Added the ForwardedHeaders middlewear for Reverse Proxy scenarios #865. [tidusjar] - -- Update build.cake. [Jamie] - -- Update DiscordApi.cs. [Jamie] - -- Update README.md. [PotatoQuality] - -- Added a authorization filter so we can see hangfire outisde of the local requests. [TidusJar] - -- Added more logging for the updater. [Jamie.Rees] - -- Added the emby episode cacher and the job to check if items are available on emby #1464 #865. [tidusjar] - -- Added the Emby Cacher, we now cache the Emby data! [tidusjar] - -- Updated CHangelog. [Jamie.Rees] - -- Updated changelog. [Jamie.Rees] - -- Updated assembly versions. [Jamie.Rees] - -- Added the logo in the email notifications to use the application image #1459. [Jamie.Rees] - -- Change Os to VS2015. [Jamie.Rees] - -- Added multiple emby server support and enabled it for Plex #865. [tidusjar] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Update README.md. [Jamie] - -- Added slack #1459 #865. [Jamie.Rees] - -- Added a checkbox to the usermanagement screen.. Does nothing yet #865 #1456. [Jamie.Rees] - -- Update build.cake. [Jamie] - -- Added swagger into the .zips. [Jamie.Rees] - -- Added Cake build #865. [Jamie.Rees] - -- Added Pushbullet notifications #1459 #865. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added the logging endpoint at /api/v1/Logging for the UI logs #1465. [tidusjar] - -- Change the RID. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Updated Changelog. [Jamie.Rees] - -- Added changelog. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Updated stuff. [Jamie.Rees] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Update ISSUE_TEMPLATE.md. [PotatoQuality] - -- Added the Ombi or customized logo onto the login page #865. [TidusJar] - -- Added new login page #865. [Jamie] - -- Added Migrations rather than a manual DB Script #865. [Jamie.Rees] - -- Updated all the packages. [tidusjar] - -- Added a skip options #865. [tidusjar] - -- Added the minimumAvailability #865. [tidusjar] - -- Updater build script. [Jamie.Rees] - -- Added the testing notifications and discord notification #865. [Jamie.Rees] - -- Added tests into the build. [Jamie.Rees] - -- Added Swagger. [Jamie.Rees] - -- Added Discord notification #865. [Jamie.Rees] - -- Added the Movie Sender, Movies will be sent to Radarr now #865. [Jamie.Rees] - -- Added SQLite storage for Hangfire. [Jamie.Rees] - -- Added the Radarr cacher #865. [tidusjar] - -- Added Radarr and Sonarr settings pages #865. [Jamie.Rees] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Updated build script. [tidusjar] - -- Added the Rules Engine Pattern and the Auto approve and request rules #865. [tidusjar] - -- Update .gitattributes. [Jamie] - -- Added the TraktApi back. [tidusjar] - -- Changes. [Jamie.Rees] - -- Added some sonarr stuff. [Jamie.Rees] - -- Added Hangfire #865. [tidusjar] - -- Update node again... [Jamie.Rees] - -- Update node. [Jamie.Rees] - -- Changes. [Jamie.Rees] - -- Update StringCipher.cs. [Jamie] - -### **Fixes** - -- New translations en.json (Norwegian) (#2020) [Jamie] - -- Publish 32bit build of windows. [tidusjar] - -- Fixing incorrect filter translation targets (#1987) [Jono Cairns] - -- New Crowdin translations (#2017) [Jamie] - -- Fixed #1997. [tidusjar] - -- We now show the digital release date in the search if available #1962. [tidusjar] - -- Css fixes (#2014) [Louis Laureys] - -- API improvements. [Jamie] - -- Fix #1599 (#2008) [Louis Laureys] - -- Issue button fix (#2006) [Louis Laureys] - -- Fixed #1886 #1865. [Jamie] - -- Fixed the outstanding issue on #1995. [Jamie] - -- Fixed an issue for #1951. [tidusjar] - -- Try and fuzzy match the title and release if we cannot get the tvdb id or imdbid (depends on the media agents in Plex) #1951. [tidusjar] - -- Fixed #1989 #1719. [Jamie] - -- Small changes that might fix #1985 but doubt it. [Jamie] - -- Should fix #1975. [tidusjar] - -- Fixed #1789. [tidusjar] - -- Fixed #1968. [tidusjar] - -- Fixed #1978. [tidusjar] - -- Fixed #1954. [tidusjar] - -- Small changes to the auto updater, let's see how this works. [Jamie] - -- Fixed build. [Jamie] - -- Fixed the update check for the master build. [Jamie] - -- Removed accidently merged files. [Jamie] - -- Create CODE_OF_CONDUCT.md. [Jamie] - -- Windows installation guide link update. [PotatoQuality] - -- Fixed the issue comment issue #1914 also added another variable for issues {IssueUser} which is the user that reported the issue. [Jamie] - -- Fix #1914. [tidusjar] - -- Fixed #1914. [tidusjar] - -- Fixed build and added logging. [TidusJar] - -- New Crowdin translations (#1934) [Jamie] - -- Potential fix for #1942. [Jamie] - -- Quick change to the Emby Availability rule to make it in line slightly with the Plex one. #1950. [Jamie] - -- Turn off mobile notifications. [tidusjar] - -- FIXED PLEX!!!!! [tidusjar] - -- Batch the PlexContentSync and increase the plex episode batch size. [tidusjar] - -- Fixed the migration issue, it's too difficult to migrate the tables. [tidusjar] - -- Fixed #1942. [tidusjar] - -- Fixed checkboxes style. [Jamie] - -- These are not the droids you are looking for. [Jamie] - -- Fixed the wrong translation and see if we can VACUUM the db. [tidusjar] - -- More translations and added a check on the baseurl to ensure it starts with a '/' [Jamie] - -- More translations. [Jamie] - -- Fixed #1878 and added a Request all button when selecting episodes. [Jamie] - -- New translations en.json (Dutch) [Jamie] - -- New translations en.json (German) [Jamie] - -- New translations en.json (German) [Jamie] - -- New translations en.json (Danish) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- New translations en.json (Italian) [Jamie] - -- New translations en.json (German) [Jamie] - -- New translations en.json (Dutch) [Jamie] - -- New translations en.json (Swedish) [Jamie] - -- New translations en.json (Spanish) [Jamie] - -- New translations en.json (French) [Jamie] - -- New translations en.json (Danish) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- New translations en.json (Italian) [Jamie] - -- New translations en.json (German) [Jamie] - -- New translations en.json (Dutch) [Jamie] - -- New translations en.json (Swedish) [Jamie] - -- New translations en.json (Spanish) [Jamie] - -- New translations en.json (French) [Jamie] - -- New translations en.json (Danish) [Jamie] - -- New translations en.json (Danish) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- New translations en.json (Italian) [Jamie] - -- New translations en.json (German) [Jamie] - -- New translations en.json (Dutch) [Jamie] - -- New translations en.json (Swedish) [Jamie] - -- New translations en.json (Spanish) [Jamie] - -- New translations en.json (French) [Jamie] - -- New translations en.json (Danish) [Jamie] - -- New translations en.json (Swedish) [Jamie] - -- New translations en.json (Norwegian) [Jamie] - -- Working on the movie matching. Stop dupes #1869. [tidusjar] - -- Delete plex episodes on every run due to a bug, need to spend quite a bit of time on this. [tidusjar] - -- Fixed the issue where we were always adding emby episodes. Also fixed #1933. [tidusjar] - -- New Crowdin translations (#1906) [Jamie] - -- Add plain password for emby login (#1925) [dorian ALKOUM] - -- Fixed #1924. [Jamie] - -- Fixed the issue where I knocked out the ordering of notifications, oops. [tidusjar] - -- #1914 for the issue resolved notification. [Jamie] - -- #1916. [Jamie] - -- Remove the placeholder. [Jamie] - -- Feature arm (#1909) [Jamie] - -- New Crowdin translations (#1897) [Jamie] - -- Fix logo cut off on login screen (#1896) [Louis Laureys] - -- E-Mails: Only add poster table row if img is set (#1899) [Louis Laureys] - -- New Crowdin translations (#1884) [Jamie] - -- Fix mobile layout (#1888) [Louis Laureys] - -- Smal changes to the api. [tidusjar] - -- OmBlur. [tidusjar] - -- Hide the password field if it's not needed #1815. [Jamie] - -- Should fix #1885. [Jamie] - -- Make user management table responsive (#1882) [Louis Laureys] - -- Fixed some stuff for omBlur. [Jamie] - -- Some work... No one take a look at this, it's a suprise. [Jamie] - -- New Crowdin translations (#1858) [Jamie] - -- When requesting Anime, we now mark it correctly as Anime in Sonarr. [tidusjar] - -- Fixed #1879 and added the spans. [tidusjar] - -- Some work on the auto updater #1460. [tidusjar] - -- Removed the potential locking. [tidusjar] - -- Fixed #1863. [tidusjar] - -- Moved the update check code from the External azure service into Ombi at /api/v1/update/BRANCH. [Jamie] - -- Fixed the UI erroring out, also dont show tv with no externals. [tidusjar] - -- More memory management and improvements. [tidusjar] - -- These are not needed, added accidentally (#1860) [Louis Laureys] - -- Some memory management improvements. [tidusjar] - -- Fixed #1857. [tidusjar] - -- Delete old v2 ombi from v3 branch. [tidusjar] - -- New Crowdin translations (#1840) [Jamie] - -- Better login backgrounds! (#1852) [Louis Laureys] - -- Fixed #1851. [tidusjar] - -- Fixed #1826. [tidusjar] - -- Redo change #1848. [tidusjar] - -- Fix the issue for welcome emails not sending. [tidusjar] - -- Fix typo (#1845) [Kyle Lucy] - -- Fix user mentions in Slack notifications (#1846) [Aljosa Asanovic] - -- If Radarr/Sonarr has noticed that the media is available, then mark it as available in the UI. [Jamie] - -- Fixed #1835. [Jamie] - -- Enable Multi MIME and add alt tags to images (#1838) [Louis Laureys] - -- New Crowdin translations (#1816) [Jamie] - -- Fixed #1832. [tidusjar] - -- Switch to use a single HTTPClient rather than a new one every request !dev. [tidusjar] - -- Fix non-admin rights (#1820) [Rob Gökemeijer] - -- Fix duplicated "Requests" element ID on new Issues link (#1817) [Shoghi Cervantes] - -- Add the Issue Reporting functionality (#1811) [Jamie] - -- Removed the forum. [tidusjar] - -- #1659 Made the option to ignore notifcations for auto approve. [Jamie] - -- New Crowdin translations (#1806) [Jamie] - -- Fixed a launch issue. [Jamie] - -- Allow users to login without a password. [Jamie] - -- Fixed the emby notifications not being sent. [Jamie] - -- #1802 and other small fixes. [tidusjar] - -- So... This sickrage thing should work now. [tidusjar] - -- Fixed emby connect login issue. [tidusjar] - -- Stop making unnecessary calls to the update service. [Jamie] - -- Fixed a bug where it blocked users with 0 limits. [Jamie] - -- Done #1788. [tidusjar] - -- More logging. [Jamie] - -- Fixed #1738. [Jamie] - -- Fixed build. [Jamie] - -- Fixed the issue where notifications were not sendind unless we restarted #1732. [tidusjar] - -- Fixed an issue with a trailing space in the subdir. [tidusjar] - -- Fixed #1774. [Jamie] - -- #1773. [Jamie] - -- Roll back rxjs (#1778) [bazhip] - -- Fixed build. [Jamie] - -- Fixed #1763. [Jamie] - -- Fix "content length error" on preview gif (#1768) [OoGuru] - -- New preview gif for Ombi V3 README (#1767) [OoGuru] - -- Remove debug code. [tidusjar] - -- Fix #1762. [tidusjar] - -- Fixed the preset themes not loading. [tidusjar] - -- Fixed #1760 and improvements on the auto updater. We may now support windows services... #1460. [Jamie] - -- Fixed #1754. [Jamie] - -- Hide the subject when it's not being used. [Jamie] - -- Error handling #1749. [Jamie] - -- New Crowdin translations (#1741) [Jamie] - -- #1732 #1722 #1711. [Jamie] - -- Fixed an issue with switching the preset themes. [Jamie] - -- Fixed #1743. [Jamie] - -- Fixed #1742. [tidusjar] - -- Fix #1742. [tidusjar] - -- Fixed landing page. [Jamie] - -- Fixed. [Jamie] - -- Translated the Requests page and fixed #1740. [Jamie] - -- Fix crash. [Jamie] - -- Sickrage done. Ish... So i've written all the code by looking at the API. the key there is i've looked at the api. I have not tested anything so expect this to fail. [Jamie] - -- SickRage settings UI. [Jamie] - -- Fixed #1721. [tidusjar] - -- Fixed the preset themes issue. [tidusjar] - -- New Crowdin translations (#1654) [Jamie] - -- Fix build. [Jamie] - -- #1460. [Jamie] - -- Fixed tests. [Jamie] - -- Return css as MIME text/css. [Jamie] - -- More added for the preset themes. [Jamie] - -- Moved around the custom styles. [Jamie] - -- More renames. [Jamie] - -- Renames. [Jamie] - -- Load the first 100 requests. [Jamie] - -- Reduce the memory consumption #1720. [Jamie] - -- Moved the schedules jobs into it's own database, see if it helps with the db locking #1720. [Jamie] - -- Fixed #1712. [tidusjar] - -- Potential fix for #1702. [tidusjar] - -- Fixed #1708. [tidusjar] - -- Fixed #1677. [tidusjar] - -- Fixed build. [tidusjar] - -- Potential fix for the DB locking issue #1720. [tidusjar] - -- #1698. [Jamie] - -- Fixed #1705. [tidusjar] - -- Fixed #1703. [tidusjar] - -- Finished adding preset themes. [Jamie] - -- Fixed #17000. [Jamie] - -- Remove the themes because waiting for a merge from lerams project. [Jamie] - -- Finsihed adding preset themes. [Jamie] - -- Fixed #1677. [Jamie] - -- Temp fix for #1683. [Jamie] - -- Fixed #1685. [Jamie] - -- Lossless Compression of images saves 83 KB (#1676) [Fish2] - -- Fixed the availability checker. [tidusjar] - -- Fixed build. [tidusjar] - -- Push out missing migration. [tidusjar] - -- Potential fix for #1674. [tidusjar] - -- Fixed an issue with the caching. [tidusjar] - -- Fixed telegram #1667. [tidusjar] - -- Fixed #1663. [tidusjar] - -- Should fix #1663. [tidusjar] - -- Stop logged in users going to the login page. [Jamie] - -- Fixed it not updating. Styles should be good now. [Jamie] - -- Re did some of the styling on the movie search page, let me know your thoughts. [Jamie] - -- Fixed #1657. [Jamie] - -- Fixed #1655. [Jamie] - -- Removed authentication resul. [Jamie] - -- New Crowdin translations (#1651) [Jamie] - -- New Crowdin translations (#1648) [Jamie] - -- New Crowdin translations (#1638) [Jamie] - -- Fixed #1644. [Jamie] - -- Moar logs #1643. [tidusjar] - -- Fixed #1640. [tidusjar] - -- Fixed the null ref exception #1460. [tidusjar] - -- Fixed landing page. [TidusJar] - -- Fixed #1641. [TidusJar] - -- Fixed #1641. [TidusJar] - -- New Crowdin translations (#1635) [Jamie] - -- Fixed #1631 and improved translation support Included startup args for the auto updater #1460 Mark TV requests as available #1632. [tidusjar] - -- Remove 32bit. [Jamie] - -- More 32bit support. [Jamie] - -- We now show "Available" for tv shows that is fully available #1602. [tidusjar] - -- Fixed the issue where we have got an episode but not the related series. #1620. [tidusjar] - -- Fixed the dropdown not working on iOS in the settings #1615. [tidusjar] - -- Fixed sonarr not monitoring the latest season #1534. [tidusjar] - -- Fixed the issue with firefox #1544. [tidusjar] - -- Fixed discord #1623. [tidusjar] - -- Add browserstack thanks (#1627) [Matt Jeanes] - -- Fix the exception #1613. [Jamie] - -- Found where we potentially are setting a new poster path, looks like the entity was being modified and being set as Tracked by entity framework, so the next time we called SaveChangesAsync() it would save the new posterpath on the entity. [Jamie] - -- Small modifications. [Jamie] - -- Fixed #1622. [Jamie] - -- Various improvements to webpack/gulp/vscode support (#1617) [Matt Jeanes] - -- Episodes in requests are now in order #1597 (#1614) [masterhuck] - -- Fixed a null reference issue in the Plex Content Cacher. [Jamie.Rees] - -- Fixed #1610. [tidusjar] - -- Really fixed the build this time. [tidusjar] - -- Fixed build. [tidusjar] - -- Made the updater work again #1460. [tidusjar] - -- Adding logging into the auto updater and also added more logging around the create inital user for #1604. [tidusjar] - -- Fixed the issue where we did not check if they are already in sonarr when choosing certain options #1540. [tidusjar] - -- We can now delete tv child requests and the parent will get remove #1603. [tidusjar] - -- Finished the api changes requested #1601. [tidusjar] - -- Fixed the Hangfire server timeout issue #1605. [tidusjar] - -- Fixed notifications not sending #1594. [tidusjar] - -- Fixed #1583 you can now delete users. Fixed the issue where the requested by was not showing. Finally fixed the broken poster paths. [tidusjar] - -- Fixed the issue where movie requests were no longer being requested. [tidusjar] - -- Started adding some more unit tests #1596. [Jamie.Rees] - -- #1588 When we make changes to any requests that we can trigger a notification, always send it to all notification agents, even if the user wont recieve it. [Jamie.Rees] - -- Add a message when email notifications are not setup when requesting a password reset. #1590. [Jamie.Rees] - -- Removed text that we no longer need. [Jamie.Rees] - -- Fixed #1574. [Jamie.Rees] - -- #1460 looks like the permissions issue has been resolved. Just need to make sure the Ombi process is terminated. [Jamie.Rees] - -- Put back the old download code. [Jamie.Rees] - -- Test. [Jamie] - -- Build sln. [Jamie.Rees] - -- Order by the username #1581. [Jamie.Rees] - -- Remove sonarr episodes from the cache table. [Jamie.Rees] - -- Couchpotato finished. [tidusjar] - -- Disable run import button if no import options are selected. [tidusjar] - -- Fixed #1574. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixes the issue with non windows systems unable to unzip the tarball #1460. [tidusjar] - -- Finished the couchpotato settings. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed #1570 #1571. [tidusjar] - -- Fixed #1547. [tidusjar] - -- Should fix #1538. [tidusjar] - -- Fixed #1553. [tidusjar] - -- Fixed #1546. [tidusjar] - -- Fixed #1543. [tidusjar] - -- Fixes an issue with Movie caching not working on develop branch of Radarr (#1567) [Jeffrey Peters] - -- This adds two fields to the Email Notifications settings page. It allows for the disabling of TLS/SSL as well as the ability to disable certificate validation when sending notification emails. (#1552) [Jeffrey Peters] - -- Fixed typo (#1551) [Codehhh] - -- Use Sqlite storage for Hangfire. [tidusjar] - -- Fixed the overrides #1539 also display it on screen now too. [tidusjar] - -- Fixed #1542 also added VSCode support. [tidusjar] - -- Fixed some cosmetic issues #865. [Jamie.Rees] - -- Fixed #1531. [Jamie.Rees] - -- Small fixes #865. [Jamie.Rees] - -- Some errors fixed and some ui improvements #865. [tidusjar] - -- Auto-scale large images down to container size (#1529) [Avi] - -- Fix logo on login page. (#1528) [Avi] - -- Another potential issue? :/ [tidusjar] - -- Real fix. [tidusjar] - -- #1513 Added storage path. [Jamie.Rees] - -- Fixed the discord issue relating to images #1513. [Jamie.Rees] - -- Fixed the issue sending movies to Radarr #1513 Fixed typo #1524. [Jamie.Rees] - -- Fixed logo on reset password pages fixed the run importer button on the user management settings. [Jamie.Rees] - -- Fixed crash/error #865. [tidusjar] - -- #1513 fixed the landing page and also the reverse proxy images. [tidusjar] - -- #1513 correctly set the child requests as approved. [tidusjar] - -- Fixed an issue that potentially causes as issue when siging into plex #865. [tidusjar] - -- Remove dev branch. [PotatoQuality] - -- Prepare readme for upcoming beta. [PotatoQuality] - -- #1513 partially fixed a bug. [tidusjar] - -- Fixed the exception. [tidusjar] - -- Fixed the application url not saving #1513. [tidusjar] - -- Fixed liniting. [tidusjar] - -- REVERSE PROXY BITCH! #1513. [tidusjar] - -- Fixed a bug where we were marking the wrong episodes as available #1513 #865. [Jamie.Rees] - -- Fixed an issue where we messed up the pages and routing. [Jamie.Rees] - -- Emby user importer is now therer! #1456. [tidusjar] - -- #1513 Added the update available icon. [tidusjar] - -- Fixed the issue of it showing as not requested when we find it in Radarr. Made the tv shows match a bit more to the movie requests Added the ability for plex and emby users to login Improved the welcome email, will only show for users that have not logged in Fixed discord notifications the about screen now checks if there is an update ready #1513. [tidusjar] - -- Support email addresses as usernames #1513. [Jamie.Rees] - -- Link to issue treath. [PotatoQuality] - -- Give correct feedback when testing email notifications #1513. [Jamie.Rees] - -- Report issue removed and the deny dropdown removed #1513. [Jamie.Rees] - -- #1513 removed the discord text when testing pushbullet. [Jamie.Rees] - -- Made a lot of changes around the notifcations to support the custom app name also started on the welcome email ##1456. [Jamie.Rees] - -- Fixed the bug where we were displaying shows where we do not have enough information to request #1513. [Jamie.Rees] - -- #1513 added the network to tv shows. [Jamie.Rees] - -- Fixed the whitespace issue #1513. [Jamie.Rees] - -- Fixed the swagger endpoint #865 #1513 Fixed the custom image issue on the login page Fixed the bug when clicking on the tab on the requests page it would switch to the wrong one Swagger is now back @ /swagger. [tidusjar] - -- Optimized images, Update old compressed image with a new lossless one. (#1514) [camjac251] - -- #1513 #865 Fixed the issue where we do not send the requests to Radarr/Sonarr when approving. [tidusjar] - -- #1506 #865 Fixed an issue with the test buttons not working correctly. [tidusjar] - -- #865 Added donation link. [tidusjar] - -- Fixed a bunch of issues on #1513. [tidusjar] - -- #1460 Added the Updater, it all seems to be working correctly. #865. [Jamie.Rees] - -- Removed percentage. [Jamie.Rees] - -- Fixed linter. [Jamie.Rees] - -- Fixed some bugs in the UI #865. [Jamie.Rees] - -- Improved the search buttons #865. [Jamie.Rees] - -- More logging #865. [Jamie.Rees] - -- Made build faster. [Jamie.Rees] - -- More logging. [Jamie.Rees] - -- Set debug level to Debug for now. [Jamie.Rees] - -- Add linting and indexes for interfaces/services (#1510) [Matt Jeanes] - -- Fixed the issue with the tv search not working #1463. [Jamie.Rees] - -- Latest practices... also probably broke some styles - sorry (#1508) [Matt Jeanes] - -- Build with the branch version. [tidusjar] - -- Build fix. [tidusjar] - -- Fixed build. [tidusjar] - -- Omgwtf so many changes. #865. [tidusjar] - -- Tests. [Jamie.Rees] - -- #1456 Started on the User Importer Also added the remember me button. [Jamie.Rees] - -- Made some UI changes, reworked the Emby and Plex screens to make them more user friendly and no so fugly. #865 Also made the login page placeholder text slightly lighter. [Jamie.Rees] - -- Cake skip verification build stuff #865. [Jamie.Rees] - -- Some fixes around the UI and managing requests #865. [tidusjar] - -- #1486. [Jamie.Rees] - -- #1486. [Jamie.Rees] - -- Upgraded to .net core 2.0 #1486. [Jamie.Rees] - -- #865 Finished the landing page, we now check the server's status. [Jamie.Rees] - -- Fixed build. [TidusJar] - -- Removed the telegram api. [Jamie.Rees] - -- Small changes on the updater #1460 #865. [Jamie.Rees] - -- Remove unused functions. [Dhruv Bhavsar] - -- Make Episode picker similar to Requests Child view. #1457 #1463. [Dhruv Bhavsar] - -- Fix merge conflict for TvRequests component. [Dhruv Bhavsar] - -- Upstream Changes... [Dhruv Bhavsar] - -- Clean up Requests page code by moving children request to old component, remove additional REST calls when merging and update component names to make more sense. [Dhruv Bhavsar] - -- Lots of different UI enhancements and fixes #865. [tidusjar] - -- Gitchangelog. [tidusjar] - -- Fixed the issue where we were using the wrong availability options. [tidusjar] - -- Fixed a bunch of bugs in Ombi #865. [tidusjar] - -- Build versioning. [Jamie.Rees] - -- #1460 The assembly versioning seems to work correctly now. [Jamie.Rees] - -- More build versioning changes #865. [tidusjar] - -- Fixed cake script. [Jamie.Rees] - -- WIP on the build versioning for the Updater #1460 #865. [Jamie.Rees] - -- Versioning. [Jamie.Rees] - -- Package versions. [Jamie.Rees] - -- #1460 #865 working on the auto updater. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Small changes around the roles #865. [tidusjar] - -- Improvements to the UI and also finished the availability checker #865 #1464. [Jamie.Rees] - -- Availability Checker #1464 #865. [Jamie.Rees] - -- Fixed ##1492 and finished the episode searcher for #1464. [Jamie.Rees] - -- #1464. [tidusjar] - -- Reload the settings #1464 #865. [Jamie.Rees] - -- #1464 added the Plex episode cacher #865. [Jamie.Rees] - -- Fixed some issues around the tv requests area Added mattermost and telegram notifications #1459 #865 #1457. [tidusjar] - -- Fix global.json. [Dhruv Bhavsar] - -- Working UI for Requests. Approval/Deny does not work as it doesn't in your code either. [Dhruv Bhavsar] - -- Enable diagnostic on build #865. [Jamie.Rees] - -- Fixed the user token issue #865. [Jamie.Rees] - -- Some small refresh token work #865. [Jamie.Rees] - -- Initial TV Requests UI rebuild. [Dhruv Bhavsar] - -- Made a start on supporting multiple emby servers, the UI needs rework #865. [Jamie.Rees] - -- #865 #1459 Added the Sender From field for email notifcations. We can now have "Friendly Names" for email notifications. [Jamie.Rees] - -- Redirect to the landing page when enabled #1458 #865. [Jamie.Rees] - -- Removed IdentityServer, it was overkill #865. [Jamie.Rees] - -- Fixed another bug with identity. #865 I'm thinking about removing it. Causing more hassle than it's worth. [tidusjar] - -- #1460 #865. [tidusjar] - -- Delete appveyor_old.yml. [Jamie] - -- Fixed path. [Jamie.Rees] - -- Silent build level. [Jamie.Rees] - -- #1459 Forgot to get the Pushbullet agent to look up the pusbullet templates rather than the Discord ones. Updated the Gitchange log. [Jamie.Rees] - -- Made the placeholder color on the login page a bit lighter #865. [Jamie.Rees] - -- Landing and login page changes #865 #1485. [tidusjar] - -- #1458 #865 More work on landing. [Jamie.Rees] - -- Working on the landing page #1458 #865. [tidusjar] - -- A lot of clean up and added a new Image api #865. [Jamie.Rees] - -- Cleaned up the Logging API slightly #1465 #865. [Jamie.Rees] - -- Fixed the Identity Server discovery bug #1456 #865. [tidusjar] - -- Fixed the issue with the Identity Server running on a different port, we can now use -url #865. [Jamie.Rees] - -- Try again. [TidusJar] - -- Publish ubuntu 16.04. [Jamie.Rees] - -- Chnaged the updater job from Minutely to Hourly. [Jamie.Rees] - -- Some work around the Auto Updater and other small changes #1460 #865. [Jamie.Rees] - -- Missed a file. [tidusjar] - -- Fixed the swagger issue. [tidusjar] - -- RDP issues. [tidusjar] - -- Appveyor build rdp investigation. [tidusjar] - -- Working on the requests page #1457 #865. [tidusjar] - -- Made the password reset email style the same as other email notifications #1456 #865. [Jamie.Rees] - -- Fixed some bugs around the authentication #1456 #865. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Fixed build #1456. [Jamie.Rees] - -- #1456 #865 Started on allowing Plex Users to sign in through the new authentication server. [Jamie.Rees] - -- Removed covalent. [Jamie.Rees] - -- #1456 Reset Password stuff #865. [Jamie.Rees] - -- Finished implimenting Identity with IdentityServer4. #865 #1456. [Jamie.Rees] - -- Moved over to using Identity Server with Asp.Net Core Identity #1456 #865. [Jamie.Rees] - -- Started on the requests rework #865. [Jamie.Rees] - -- Extended the Emby API. [Jamie.Rees] - -- Started reworking the usermanagement page #1456 #865. [tidusjar] - -- Lots of refactoring #865. [Jamie.Rees] - -- Created an individual user api endpoint so we can make the user management pages better #865. [TidusJar] - -- Lot's of refactoring. [Jamie.Rees] - -- #1462 #865 Had to refactor how we use notificaitons. So we now have more notification fields about the request. [Jamie.Rees] - -- Looks like Sonarr is finished and works. A lot simplier this time around. #865. [tidusjar] - -- More work on the Sonarr Api Integration #865. [tidusjar] - -- Started on sonarr #865. [tidusjar] - -- Small changes #865. [tidusjar] - -- Damn son. So many changes... Fixed alot of stuff around tv episodes with the new DB model #865. [tidusjar] - -- Fixed the TV Requests issue #865. [Jamie.Rees] - -- Fixed a load of bugs need to figure out what is wrong with tv requests #865. [tidusjar] - -- #865 rework the backend data. Actually use real models rather than a JSON store. [Jamie.Rees] - -- Fixed the build issue #865. [tidusjar] - -- Allow us to use Emby as a media server. [tidusjar] - -- More Update #865. [Jamie.Rees] - -- Deployment changes. [Jamie.Rees] - -- More work on the Updater. [Jamie.Rees] - -- Lots of fixes. Becoming more stable now. #865. [tidusjar] - -- Small fixes around the searching. [Jamie.Rees] - -- Some rules #865. [Jamie.Rees] - -- Oops. [TidusJar] - -- Started on the Discord API settings page. [TidusJar] - -- Email Notifications are now fully customizable and work! #865. [Jamie.Rees] - -- Small changes and fixed some stylingon the plex page #865. [Jamie.Rees] - -- More on #865 TODO, Find out whats going on with the notifications and why exceptions are being thrown. [Jamie.Rees] - -- Oops. [Jamie.Rees] - -- Ok #865 fixed the published exe. [Jamie.Rees] - -- Fixed errors. [Jamie.Rees] - -- Fixed build script. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Some more #865. [Jamie.Rees] - -- Create appveyor.yml. [Jamie] - -- The Approving child requests now work! [Jamie.Rees] - -- Fixed many bugs #865. [Jamie.Rees] - -- Loads of changes, improved the movie search stylings is back. [Jamie.Rees] - -- Moved to webpack and started on new style. [Jamie.Rees] - -- Fixed the TV search via Trakt not returning Images anymore. #865. [Jamie.Rees] - -- Rules changes and rework. [Jamie.Rees] - -- Request Grid test. [Jamie.Rees] - -- Small cleanup #865. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Started the Radarr Settings #865. [Jamie.Rees] - -- Massive amount of rework on the plex settings page. It's pretty decent now! #865. [tidusjar] - -- Fixed build. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Rules #865. [tidusjar] - -- Stuff. [Jamie.Rees] - -- Forgot to uncomment. [Jamie.Rees] - -- Tetsd. [Jamie.Rees] - -- Build task changes. [Jamie.Rees] - -- Adsa. [Jamie.Rees] - -- Appveyor. [Jamie.Rees] - -- Stuff around tokens and also builds. [Jamie.Rees] - -- Finished the Plex Content Cacher. Need to do the episodes part but things are now showing as available! #865. [tidusjar] - -- Small user changes #865. [Jamie.Rees] - -- Stuff #865 need to work on the claims correctly. [Jamie.Rees] - -- Reworked the TV model AGAIN #865. [Jamie.Rees] - -- The move! [Jamie.Rees] - -- Fixed build #865. [Jamie.Rees] - -- Fixed the user management #865. [Jamie.Rees] - -- #865 Added support for multiple plex servers. [Jamie.Rees] - -- Bleh. [tidusjar] - -- Small changes. [Jamie.Rees] - -- Fixed the build. [Jamie.Rees] - -- Fixes. [Jamie.Rees] - -- Bundling changes. [Jamie.Rees] - -- Some series information stuff, changes the pace theme too. [Jamie.Rees] - -- Docker support and more, redesign the episodes. [tidusjar] - -- Stuff around episode/season searching/requesting. [Jamie.Rees] - -- Removed redundant folders. [tidusjar] - -- Lots of backend work. [tidusjar] - -- Fixed build. [Jamie.Rees] - -- TV Request stuff. [Jamie.Rees] - -- Work around the user management. [tidusjar] - -- More. [Jamie.Rees] - -- Lots and Lots of work. [Jamie.Rees] - -- Diagnostic changes. [tidusjar] - -- Fixed hangfire exception. [tidusjar] - -- Remove xunit. [tidusjar] - -- Lots more work :( [Jamie.Rees] - -- More changes. [tidusjar] - -- #865. [Jamie.Rees] - -- Small changes. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- More mapping. [Jamie.Rees] - -- Mapping mainly. [Jamie.Rees] - -- Fix systemjs config not being included. [Matt Jeanes] - -- Fixed bundling and various improvements. [Matt Jeanes] - -- Finished the emby wizard #865. [tidusjar] - -- Finished the wizard #865 (For Plex Anyway) [tidusjar] - -- Small changes. [tidusjar] - -- More work on Wizard and Plex API #865. [tidusjar] - -- Settings. [Jamie.Rees] - -- Settings for Ombi. [Jamie.Rees] - -- Fixed some issues around the identity. [Jamie.Rees] - -- #865 more for the authentication. [tidusjar] - -- Auth. [Jamie.Rees] - -- More on the search and requests page. It's almost there for movies. Need to add some filtering logic #865. [tidusjar] - -- #865. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Messing around with the settings. [tidusjar] - -- Fixed the yml. [Jamie.Rees] - -- Remove unneeded bundle config. [Matt Jeanes] - -- Redo dotnet publish targets. [Jamie.Rees] - -- Bundling changes. [Jamie.Rees] - -- Stuff. [Jamie.Rees] - -- Move app into wwwroot. [Jamie.Rees] - -- Put uglify back in! [Jamie.Rees] - -- Wrong line. [Jamie.Rees] - -- Matt is helping. [Jamie.Rees] - -- Revert. [tidusjar] - -- Small tweaks. [tidusjar] - -- Upgrade to .Net Standard 1.6. [tidusjar] - - -## v2.2.1 (2017-04-09) - -### **New Features** - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added the forums. [tidusjar] - -- Updates. [tidusjar] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added a retry policy around the emby newsletter. [Jamie.Rees] - -### **Fixes** - -- Revert "Merge branch 'DotNetCore' into dev" [tidusjar] - -- More borken build. [Jamie.Rees] - -- Started adding requesting. [Jamie.Rees] - -- Done the movie searching. [tidusjar] - -- #865. [tidusjar] - -- More. [tidusjar] - -- Moar. [tidusjar] - -- Small changes. [tidusjar] - -- Styling. [Jamie.Rees] - -- MOre changes. [Jamie.Rees] - -- Spacing. [Jamie.Rees] - -- Try again. [Jamie.Rees] - -- More. [Jamie.Rees] - -- Again. [Jamie.Rees] - -- Anbother. [Jamie.Rees] - -- Another. [Jamie.Rees] - -- Another. [Jamie.Rees] - -- Retry. [Jamie.Rees] - -- A. [Jamie.Rees] - -- Fixed. [Jamie.Rees] - -- Cahnge 2. [Jamie.Rees] - -- Appveyor change. [Jamie.Rees] - -- The start of a new world. [Jamie.Rees] - -- Fixed the migration number and order by the added date for the newsletter #1264. [tidusjar] - -- Forgot this change. [tidusjar] - -- Also fixed the issue for the Emby Newsletter where episodes were not getting added :( [tidusjar] - -- #1264 "They may take our lives, but they'll never take our freedom!" [tidusjar] - -- Finished reworking the Sonarr Integration. Seems to be working as expected, faster and most stable. It's Not A Toomah! [tidusjar] - -- Small bit of work. [Jamie.Rees] - -- Made a start on the new Sonarr integration. [tidusjar] - -- For test emails, if there is no new content then just grab some old data. [tidusjar] - -- Fixed an issue where the emby newsletter was always showing series. [tidusjar] - - -## v2.2.0 (2017-03-30) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added a new setting for the Netflix option, we can now disable it appearing in the search. [tidusjar] - -- Update German Translation. [Marius Schiffer] - -- Added a release notes page, you can access via Admin>Updates>Recent Changes tab. Note to self, need to put better comments in for users to understand! [Jamie.Rees] - -- Added gravitar image. [Jamie.Rees] - -- Added a missing `await` for an HP AddArtist call. Added some more Trace logging. [smcpeck] - -- Added a missing `await` for an HP AddArtist call. Added some more Trace logging. [smcpeck] - -- Added some logging around API calls. [smcpeck] - -- Changed IEmbyAvailabilityChecker to use IEnumberables + checking actor search against Emby content + PR feedback. [smcpeck] - -- Changed actor searching to support non-actors too. [smcpeck] - -- Added a 10 second timer to refresh some new caching I put in. [smcpeck] - -- Added root folder and approving quality profiles in radarr #1065. [tidusjar] - -- Added some debugging code around the newsletter for Emby #1116. [tidusjar] - -- Added a TMDB Rate limiter for the newsletter. [tidusjar] - -- Added port check in wizard. also fixed favicon. [tidusjar] - -- Update Radarr placeholder. [d2dyno] - -- Added the user login for emby users #435. [tidusjar] - -- Added User Management support for Emby #435. [tidusjar] - -- Added emby to the sidebar #435. [tidusjar] - -- Added API endpoint for /actor/new/ to support searching for movies not already available/requested. [smcpeck] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -### **Fixes** - -- Translation changes. [Jamie.Rees] - -- Syntax error. [tidusjar] - -- Fixed an issue where we were retrying the API call when the Plex users login creds were invalid. #1217. [tidusjar] - -- Slightly increased the wait time for the emby newsletter also fixed a potential error in the plex user checker. [Jamie.Rees] - -- Fixed an issue where we were not notifiying emby users. [Jamie.Rees] - -- Fixed the issue where the recent changes page was not showing the correct date. #1296. [Jamie.Rees] - -- Fixed #1252 (Show the correct user type on the management page for Plex Users) [Jamie.Rees] - -- Fixed the casting error #1292. [Jamie.Rees] - -- Fix test newletter not sending when empty. [Dhruv Bhavsar] - -- Quick fix for email false positive message. ISSUE: #1286. [Dhruv Bhavsar] - -- Fixes around the newsletter. We will now correctly show newly added shows and also newly added episodes. #1163. [tidusjar] - -- Fixed a sonarr deseralization error. [tidusjar] - -- Increased the delay for the Episode information api calls. #1163. [tidusjar] - -- Looks like we were overloading emby with out api calls. [tidusjar] - -- Fixed the root path escaping issue for Radarr too! [tidusjar] - -- Some small backend newsletter changes, we can now detect if there are any movies and/or tv shows, if there are none then we will no longer send out an empty newsletter. [Jamie.Rees] - -- Remoddeled the notificaiton settings to make it easier to add more. This is some techinical changes that no one except me will ever notice :( [Jamie.Rees] - -- Fixed #1234. [Jamie.Rees] - -- A fix to the about page and also started to rework the notification backend slightly to easily add more notifications. [Jamie.Rees] - -- Adding more logging into the Plex Cacher. [Jamie.Rees] - -- #1218 changed the text when we cannot display release notes for dev and EAP branches. [Jamie.Rees] - -- Fix for #1236. [SuperPotatoMen] - -- Tooltips. [Jamie.Rees] - -- #236. [Jamie.Rees] - -- #1102. [Jamie.Rees] - -- Done #1012. [Jamie.Rees] - -- Oops #1134. [Jamie.Rees] - -- Fixed #1121. [Jamie.Rees] - -- Fixed #1210. [Jamie.Rees] - -- Fixed typo #1134. [Jamie.Rees] - -- Fixed #1223. [Jamie.Rees] - -- Another newsletter fix attempt #1163 #1116. [tidusjar] - -- Fixup! Reset the branch on v2.1.0 tag to get to a shared state between dev and Master. [distaula] - -- Fixed a bug in the Plex Newsletter. [tidusjar] - -- Typo. [tidusjar] - -- Fixed around the newsletter and a small feature around the permissions/features (#1215) [Jamie] - -- Fixed #1189. [tidusjar] - -- Fixed #1195. [Jamie.Rees] - -- Fixed #1195. [Jamie.Rees] - -- Fixed #1192. [Jamie.Rees] - -- Fixed issue where we could get null rating keys on Plex. [tidusjar] - -- Needed to treat a 201 as success, too. + removed some commented out code. [Shaun McPeck] - -- Normalized spacing/tabs. [smcpeck] - -- Move local user login to be the first thing checked; renamed old Api variable to PlexApi now that Emby is in play. [smcpeck] - -- Remove all the polling/retry logic around HP requests. This was a problem do to not properly awaiting the initial AddArtist API call being sent to HP. Also fix SetAlbumStatus to use ReleaseId instead of MusicBrainsId (same fix previously applied to AddArtist). [smcpeck] - -- Restore checking of HTTP StatusCode on ApiRequests; remove checking of response.ErrorException. [smcpeck] - -- Reverted (for now) non-200 response handling; added some extra logging. [smcpeck] - -- Tweaked ApiRequest behavior on non-200 responses; think it was breaking login. :-" [smcpeck] - -- Only deserialize response payload in ApiRequest when StatusCode == 200. Will a default return value in other cases cause other issues? [smcpeck] - -- Headphones - added releaseID to generic RequestedModel and passing that through to HP request. Their API doesn't request via the MusicBrainzId. [smcpeck] - -- Fixed #1038. [tidusjar] - -- Fixed a slight issue where we could click the change folders button rather than the dropdown arrow #1189. [tidusjar] - -- Bunch of updater files. [tidusjar] - -- #1163 #117. [tidusjar] - -- Removed some unnecessary 'ConfigureAwait` uses. [smcpeck] - -- Remove meaningless html class from actor searching checkbox. [smcpeck] - -- Fixed an issue where we were not always showing movies from external programs. [tidusjar] - -- Remove extra delay when filtering out existing movies. [smcpeck] - -- Post merge build fixes. [smcpeck] - -- Fix. [tidusjar] - -- Fixed #1177. [tidusjar] - -- Fixed #1152. [tidusjar] - -- Fixed #1123. [tidusjar] - -- Fixed a bug when sending to radarr. [tidusjar] - -- Fixed #1133. [tidusjar] - -- Fixed issues img. [Jamie.Rees] - -- Stop Plex being enabled on the first time installing #1048. [Jamie.Rees] - -- The landing page now works for emby #435. [tidusjar] - -- Fixed #1104. [tidusjar] - -- Fixed #1090. [tidusjar] - -- Fixed #1103. [tidusjar] - -- Small changes. [tidusjar] - -- Break out Mass Email feature into its own tab, upgrade Font Awesome and clean up some comments. [dhruvb14] - -- Fix typo. [Travis Bybee] - -- Fixed #1066. [Jamie.Rees] - -- Fixed broken builds. [Jamie.Rees] - -- Fixed #1083. [Jamie.Rees] - -- #1049. [tidusjar] - -- Fixed #1071. [tidusjar] - -- Fixed #1048 #1081. [tidusjar] - -- #1074. [Jamie.Rees] - -- #1069. [Jamie.Rees] - -- Fix for #1068. [tidusjar] - -- Remove duplciate tv show status. [tidusjar] - -- Some request ui changes. [tidusjar] - -- Removed references to Plex. [tidusjar] - -- Removed plex from the scheduled jobs ui. [tidusjar] - -- First run of the newsletter set it to a test. [tidusjar] - -- Reworked the newsletter for Emby! Need to rework it for Plex and use the new way to do it. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed the mass email, it was only being set to users with the newsletter feature #358. [tidusjar] - -- Removed Plex Request from the notifications. [tidusjar] - -- Finish implementing mass email feature. [dhruvb14] - -- @tidusjar pointed out runtime error!! [dhruvb14] - -- Does not compile, need to get data from UI into nancy somehow and figure out why IMassEmail is not initializing. [dhruvb14] - -- Begin Implementing Mass Email Section. [dhruvb14] - -- Hide the auto update btn #236 Fixed where we were not populating the emby episodes #435. [tidusjar] - -- Fix Radarr labels. [d2dyno] - -- Fixed pace loader. [Jamie.Rees] - -- Check if Emby/Plex is enabled before starting the job. [Jamie.Rees] - -- Fixed #1036. [Jamie.Rees] - -- Fixed a typo and changed wording. [Torkil Liseth] - -- Fixed #1035. [Jamie.Rees] - -- Fix for #1026. [Jamie.Rees] - -- Fixed #1042. [tidusjar] - -- #435. [Jamie.Rees] - -- #435 Started the wizard. [Jamie.Rees] - -- Removed. [tidusjar] - -- Final Fixes. [dhruvb14] - -- Partial fix for broken HR tag's in Email... [dhruvb14] - -- DAMN! #435 that's a lot of code! [tidusjar] - -- Started adding Emby, Lots of backend work done. Need a few more services done and login and user management. #435. [tidusjar] - -- UI changes to add checkbox and support searching for only new matches via new API. [smcpeck] - -- REFACTOR: IAvailabilityChecker - changed arrays to IEnumerables. [smcpeck] - -- UI changes to consume actor searching API. [smcpeck] - -- API changes to allow for searching movies by actor. [smcpeck] - -- Enforcing async/await in synchronous methods that were marked async. [smcpeck] - - -## v2.1.0 (2017-01-31) - -### **New Features** - -- Update README.md. [Jamie] - -- Update .gitattributes. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added the new labels to the search. [tidusjar] - -- Added a switch to use the new search or not, just in case people do not like it. added a migration to turn on the new search. [Jamie.Rees] - -- Added a bunch of categories for tv search similar to what we have for movies. [Jamie.Rees] - -### **Fixes** - -- Fixed typos. [Haries Ramdhani] - -- Fix typo in readme. [tdorsey] - -- Fixed #985. [Jamie.Rees] - -- FIxed #978. [tidusjar] - -- Fixed the approval issue for #939. [tidusjar] - -- Some general improvements. [tidusjar] - -- Turned off migration for now. [tidusjar] - -- Fixed #998. [tidusjar] - -- Additional movie information. [Jamie.Rees] - -- Debug info around the notifications. [Jamie.Rees] - -- Small changes. [tidusjar] - -- Fixed #995. [tidusjar] - -- Fix for #978. [tidusjar] - -- Fixed #991. [tidusjar] - -- Fixed the login issue and pass Radarr the year #990. [Jamie.Rees] - -- More small tweaks around the username/alias. [Jamie.Rees] - -- Possible issue with the empty username. [Jamie.Rees] - -- Potential Fix for #985. [Jamie.Rees] - -- Small changed to the sidebar. [Jamie.Rees] - -- Small changes. [Jamie.Rees] - -- Done #627. [Jamie.Rees] - -- Finished #535 #445 #170. [tidusjar] - -- Fixed tests. [Jamie.Rees] - -- Started to add the specify Sonarr root folders. [Jamie.Rees] - -- Fixed #968. [Jamie.Rees] - -- Fixed #970. [Jamie.Rees] - -- Done #924. [Jamie.Rees] - -- Fixed. [Jamie.Rees] - -- Fixed #955. [Jamie.Rees] - -- #956. [Jamie.Rees] - -- Fixed #947. [Jamie.Rees] - -- #951. [tidusjar] - -- Finished #923 !!! [tidusjar] - -- More for #923. [Jamie.Rees] - -- Radarr integartion in progress #923. [tidusjar] - -- Fixed #940 don't show any shows without a tvdb id. [tidusjar] - -- Finished #739. [Jamie.Rees] - -- Initial impliementation of #739. [Jamie.Rees] - -- Improved the search UI and made it more consistant. Finished the Netflix API Part #884. [Jamie.Rees] - - -## v2.0.1 (2017-01-16) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added a netflix api. [Jamie.Rees] - -### **Fixes** - -- Fixed #934. [Jamie.Rees] - - -## v2.0 (2017-01-14) - -### **New Features** - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Added the settings for #925 but need to apply the settings to the UI. [Jamie.Rees] - -- Changed the settings name from Plex Requests to Ombi. [Jamie.Rees] - -- Added support for Managed Users #811. [Jamie.Rees] - -- Change solution name in travis. [mhann] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -### **Fixes** - -- Finished #925. [Jamie.Rees] - -- Some TODO's. [Jamie.Rees] - -- Fixed #915. [Jamie.Rees] - -- Fixed the issue where notifications are not being sent to users with Aliases #912. [Jamie.Rees] - -- Fixed #891. [Jamie.Rees] - -- Fix indentation issue. [Marcus Hann] - -- Implement simple button. [Marcus Hann] - -- Plex Username Case Sensitivity Fix. [thegame3202] - -- Fixed #882. [Jamie.Rees] - -- Api changed again, so more fixes for #878. [Jamie.Rees] - -- Possible fix for #893. [Jamie.Rees] - -- Fixed #898. [Jamie.Rees] - -- Fixed #878. [TidusJar] - -- * userManagementController.js: fixed #881. [TidusJar] - -- More work on watcher, should all be good now. #878. [Jamie.Rees] - -- Delete PlexRequests.sln.DotSettings. [Jamie] - -- Fixed #862. [Jamie.Rees] - -- #399 and #398 finished. [Jamie.Rees] - -- More work on #399. [Jamie.Rees] - -- Finished #884. [Jamie.Rees] - -- More for #844. [Jamie.Rees] - -- Another #844. [Jamie.Rees] - -- Fixed a dependancy issue with #844. [Jamie.Rees] - -- Finished the main part of #844 just need testing. [Jamie.Rees] - -- Fixed #832. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Fix tiny readme typo. [mhann] - -- Fixed #850 also started #844 (Wrote the API interaction) [Jamie.Rees] - -- #801 #292 done. [Jamie.Rees] - -- Should fix #841 #835 #810. [Jamie.Rees] - -- Fix small typo in ticket overview page. [mhann] - -- More work on the combined login. [Jamie.Rees] - -- Fixed db issue. [Jamie.Rees] - -- Name changes. [Jamie.Rees] - -- All Sln changes. [tidusjar] - -- Moved API Sln dir. [tidusjar] - -- Fixed build. [tidusjar] - -- Moved namespaces. [tidusjar] - -- Renamed zip. [tidusjar] - -- Product name change. [tidusjar] - - -## v1.10.1 (2016-12-17) - -### **Fixes** - -- #788 fixed! [tidusjar] - -- Fixed #788 and #791. [tidusjar] - -- #399 #398. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Small refactorings. [Jamie.Rees] - -- #782. [Jamie.Rees] - -- #785. [Jamie.Rees] - - -## v1.10.0 (2016-12-15) - -### **New Features** - -- Update README.md. [Jamie] - -- Added optional launch args for the auto updater. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update _Navbar.cshtml. [Jamie] - -- Update README.md. [Jamie] - -- Update _Navbar.cshtml. [Jamie] - -- Update README.md. [Jamie] - -- Added a new permission to bypass the request limit. [Jamie.Rees] - -- Update Version1100.cs. [SuperPotatoMen] - -- Added logging around the Newsletter #717. [Jamie.Rees] - -- Added missing migration. [tidusjar] - -- Added loading spinner. [Jamie.Rees] - -- Update UI.resx. [SuperPotatoMen] - -- Update Version1100.cs. [Jamie] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -### **Fixes** - -- Fixed an issue where the HTML in the newsletter was incorrect. [Jamie.Rees] - -- Fixed #201. [Jamie.Rees] - -- Fixed #720 and added better error handling around the migrations. [Jamie.Rees] - -- Fixed #769. [Jamie.Rees] - -- Write out the actual file version. [Jamie.Rees] - -- Error checking around GA. [TidusJar] - -- Fixed #761. [tidusjar] - -- Fixed #749 Fixed an issue where we were adding the read only permission when creating the admin. [tidusjar] - -- Fixed #757. [tidusjar] - -- Fixes around the server admin #754. [Jamie.Rees] - -- Removed the trace option from the UI, it is only accessible when appending the url with "?developer" #753. [Jamie.Rees] - -- Fixed an issue where the admin could not be updated. [Jamie.Rees] - -- Fixed #745. [Jamie.Rees] - -- Fixed #748. [Jamie.Rees] - -- Workaround for #748. [SuperPotatoMen] - -- Another attempt to fix #717. [tidusjar] - -- Fixed #744. [Jamie.Rees] - -- Tidied up the warnings. [Jamie.Rees] - -- Small bit of analytics. [Jamie.Rees] - -- Removed the whitelist. [Jamie.Rees] - -- Should fix #696 Fixed an issue with the scheduled jobs where it could use a different trigger if the order between the schedules and the triggers were in different positions in the array... Stupid me, ordering both arrays by the name now. [tidusjar] - -- Some better null object handling #731. [Jamie.Rees] - -- Small tweaks. [Jamie.Rees] - -- Fixed #728. [Jamie.Rees] - -- Fixed #727. [Jamie.Rees] - -- Fixed admin redirect issue. [tidusjar] - -- Fixed #718. [tidusjar] - -- Lots of small fixes and tweaks. [Jamie.Rees] - -- Tidied up some of the angular code, split the UI into it's own directives for easier maintainability. [Jamie.Rees] - -- Small tweaks to the Request Page. [Jamie.Rees] - -- Reverted the PR that may have caused #619. [Jamie.Rees] - -- Some small tweaks around #218 Just added the link to the settings and some angular improvements. [Jamie.Rees] - -- Test. [Jamie.Rees] - -- Attempt at fixing #686. [Jamie.Rees] - -- Finished #707. [Jamie.Rees] - -- Fixed #704. [Jamie.Rees] - -- Fixed #705. [Jamie.Rees] - -- Fixed #706. [Jamie.Rees] - -- #547. [Jamie.Rees] - -- Default tabs #304. [Jamie.Rees] - -- Fixed #703. [Jamie.Rees] - -- #233. [Jamie.Rees] - -- Done #678. [Jamie.Rees] - -- Fixed #670. [Jamie.Rees] - -- #456 Update all the requests when we identify that the username changes. [Jamie.Rees] - -- More user management. [Jamie.Rees] - -- #218. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Implimented the features #218. [tidusjar] - -- Use the user alias everywhere if it is set #218. [tidusjar] - -- Implimented auto approve permissions #218. [tidusjar] - -- A. [tidusjar] - -- Fixed an IOC issue. [tidusjar] - -- Fixed the issue with user management, needed to implement our own authentication provider. [Jamie.Rees] - -- Small changes including #666. [Jamie.Rees] - -- Done #679. [tidusjar] - -- Reduce the retry time. [Jamie.Rees] - -- Remove all references to the claims. [Jamie.Rees] - -- Lots of fixed and stuff. [Jamie.Rees] - -- Fixed potential crash #683. [Jamie.Rees] - -- Fixed #681. [Jamie.Rees] - -- More user management. [Jamie.Rees] - -- Finishing off the user management page #218 #359 #195. [Jamie.Rees] - -- Finished #646 and fixed #664. [Jamie.Rees] - -- Started on #646. Fixed #657. [Jamie.Rees] - -- Fixed #665. [Jamie.Rees] - -- Migrate users. [TidusJar] - -- Fixed build. [Jamie.Rees] - -- Convert the for to foreach for better readability. Still need to rework this area. [Jamie.Rees] - -- Final Tweaks #483. [Jamie.Rees] - -- Finished #483. [Jamie.Rees] - -- Finished the queue #483. [Jamie.Rees] - -- Started on the queue for requests #483 TV Requests with missing information has been completed. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Finished the notification for the fault queue. [Jamie.Rees] - -- Finished #556. [Jamie.Rees] - -- Finished #633 (First part of the queuing) [Jamie.Rees] - -- Finished #659 #236 has been modified slightly. Needs testing on Different systems. [Jamie.Rees] - -- Almost finished #659. [Jamie.Rees] - -- Started on #483. [Jamie.Rees] - -- #544. [Jamie.Rees] - -- Fixed #656 and more work on #218. [Jamie.Rees] - -- Fixed some issues with the user management work. [TidusJar] - -- Fixed build issue. [TidusJar] - -- User perms. [Jamie.Rees] - - -## v1.9.7 (2016-11-02) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Potential fix for #629. [TidusJar] - -- Fixed an issue to stop blatting over the base url. [tidusjar] - -- Fixed #643. [TidusJar] - -- Fixed #622. [TidusJar] - - -## v1.9.6 (2016-10-28) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Fixed #586. [Jamie.Rees] - -- Fixed #622. [Jamie.Rees] - -- Fixed #621. [Jamie.Rees] - - -## v1.9.5 (2016-10-27) - -### **New Features** - -- Added our own custom migrations, a lot easier to migrate DB versions now. [tidusjar] - -### **Fixes** - -- Bump version. [Jamie.Rees] - -- Small bit of work on the user claims. [Jamie.Rees] - -- Fix #612 again. [Jamie.Rees] - -- User management styling. [Jamie.Rees] - -- Fixed #608 and some other small stuff. [tidusjar] - -- More user mapping. [tidusjar] - -- Fixed #615. [tidusjar] - -- Fixed #610. [tidusjar] - -- User management stuff. [Jamie.Rees] - -- User management work. [Jamie.Rees] - -- Revert the TVSender to use the old code. [Jamie.Rees] - -- Fixed the view issue. [tidusjar] - -- S582: admin improvements part 2. [Jim MacKenzie] - -- Fix #612. [Jamie.Rees] - -- User management, migration and newsletter. [Jamie.Rees] - -- #602 recently added improvements. [tidusjar] - -- Revert "Sorting out the current state of migrations" [Jamie.Rees] - -- Sorting out the current state of migrations. [Jamie.Rees] - -- Marked as obsolete. [Jim MacKenzie] - -- Migration setup. [Jim MacKenzie] - -- Removed extra line breaks. [Jim MacKenzie] - -- Moved Newsletter Settings to its own page. [Jim MacKenzie] - -- Reverted TMDB package. [Jamie.Rees] - -- Remove DB Option. [Jamie.Rees] - -- Upgrade the movie DB package and fixed #370 To fix this I had to make another API call... It slows down the search... [tidusjar] - -- Lots of small fixes including #475. [tidusjar] - -- A better fix for #587. [tidusjar] - -- Fixed #553. [tidusjar] - -- #601. [Jamie.Rees] - -- More rework to use the Plex DB. [Jamie.Rees] - -- More work around using the PlexDatabase. [Jamie.Rees] - -- Plex DB. [Jamie.Rees] - -- Allow to process even know we had an error #578. [Jamie.Rees] - -- Fix boostrapper-datetimepicker imports (#586) [David Torosyan] - -- Potential work around for #587. [tidusjar] - -- Fixed #589. [tidusjar] - - -## v1.9.4 (2016-10-10) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added Paypalme options, no UI yet (#568) [Jim MacKenize] - -- Update appveyor.yml. [Jamie] - -- Update README.md. [Jamie] - -### **Fixes** - -- Reverted. [tidusjar] - -- Make sure it's enabled before sending the recently added. [tidusjar] - -- Moved the HR inside the table for TV Shows. [Jamie.Rees] - -- FIXED!!!!! YES BITCH! #550. [tidusjar] - -- Moved the horizontal rules inside the table row. [tidusjar] - - -## v1.9.3 (2016-10-09) - -### **New Features** - -- Added properties to disable tv requests for specific episodes or seasons and wired up to admin settings. [Matt McHughes] - -- Added different sonarr search commands. [tidusjar] - -### **Fixes** - -- Fixed #515. [tidusjar] - -- Fixed #561 and a small bit of work on #569. [tidusjar] - -- #569. [tidusjar] - -- Fixed case typo. [Matt McHughes] - -- Finished wiring tv request settings to tv search. [Matt McHughes] - -- WIP hide tv request options based on admin settings. [Matt McHughes] - -- Set meta charset to be utf-8. [Madeleine Schönemann] - -- F#552: updated labels text. [Jim MacKenize] - -- F#552: Re-design lables. [Jim MacKenzie] - -- Last correction.. Now the translation is ready to be used. [Michael Reber] - -- Forgot to correct two incorrect translations. [Michael Reber] - -- Correction of the German translation. [Michael Reber] - -- Notification improvements. [tidusjar] - -- #515. [tidusjar] - - -## v1.9.2 (2016-09-18) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update CouchPotatoCacher.cs. [Jamie] - -- Added some error handing around the GetMovie area #517. [tidusjar] - -- Added a version endpoint in "/api/version" #529. [tidusjar] +## (unreleased) ### **Fixes** -- Trying to fix the auto CP. [tidusjar] - -- Increase the notice message text box #527. [tidusjar] - -- #536 this should fix notification settings when it is being unsubscribed when testing. [tidusjar] - -- Improved how the TV search looks and feels. [tidusjar] - -- Fix for reverse proxy when using the wizard. [Devin Buhl] - -- Fixed #532. [tidusjar] - -- This should fix some issues with the episode requests #514. [tidusjar] - -- Small changes around existing series. [tidusjar] - -- Fixed #514 and the unit tests. [tidusjar] - -- If there is a bad password when changing it, we now inform the user. [tidusjar] +- Fixed the issue with notifications not sending. [Jamie] -- When logging out as admin remove the username from the session. [tidusjar] +- Removes Legacy command result variables. [Qstick] -- Sorted out some of the UI for #18. [tidusjar] -- Finished #18. [tidusjar] - - -## v1.9.1 (2016-08-30) +## v3.0.3786 (2018-09-22) ### **New Features** -- Update appveyor.yml. [Jamie] - -- Added french to the navbar. [tidusjar] - -- Changed the way we use the setTimeout function. Should fix #403 #491 #492. [tidusjar] - -- Change the redirection to use a relative uri redirect #473. [tidusjar] +- Update CHANGELOG.md. [Jamie] ### **Fixes** -- Fixed tests. [tidusjar] - -- Fixed #491 and added more logging around the email messages under the Info level. [tidusjar] - -- Finished #415. [tidusjar] - -- Fixed an issue where there were some JS errors on the landing page settings and stopped us being redirected to the login sometimes as an admin. [tidusjar] - -- Fixed #480. [tidusjar] - -- User management. [tidusjar] - -- Fixed #505. [tidusjar] - -- Append the application version to the end of our JS/CSS files. [tidusjar] - -- Fixed issue #487. [tidusjar] - -- Remove the datetime picker css from the main assets block and only load it on the pages it needs. #493. [tidusjar] - -- Redirect to search if we are already logged in #488. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed an issue where you could set the base url as requests #479. [tidusjar] - -- Working on the beta releases page and also the user management. [tidusjar] - -- User work. [tidusjar] - - -## v1.9.0 (2016-08-18) - -### **New Features** - -- Update the availability checker to search for TV Episodes. [tidusjar] - -- Changed the no TVMazeid message. [tidusjar] - -- Added an option to disable/enable the Plex episode cacher. [tidusjar] - -- Updated the episode cacher to have a minimum of 11 hours before it runs again. [tidusjar] - -- Added some useful analytical infomation around the wizard. [tidusjar] - -- Updated the German translations #402. [tidusjar] - -- Added some code to shrink the DB. reworked the search to speed it up. [tidusjar] - -- Change to use the GrandparentTitle rather than the thumbnail.... facepalm. [tidusjar] - -- Change the interval to hours! [tidusjar] - -- Added the transaction back into the DB. Do not run the episode cacher if it's been run in the last hour. [tidusjar] - -- Added logging. [tidusjar] - -- Changed the query slightly. [tidusjar] - -- Updated Newtonsoft.Json, Autofixture, Nlog and Dapper packages. [tidusjar] - -- Added the Sonarr check for episodes #254. [tidusjar] - -- Added unit tests. [tidusjar] - -- Added #436. [tidusjar] - -- Update build no. [tidusjar] - -- Updated translations for #402. [tidusjar] - -- Added a beta module. [tidusjar] - -- Added a custom debug root path provider, this means we do not have to recompile the views every time we make a view change. [tidusjar] - -- Update .gitignore. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added tests for the string hash. [tidusjar] - -- Added code to request the api key for CouchPotato. [tidusjar] - -- Added the file version to the layout. [tidusjar] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added automation tests. [tidusjar] - -- Update appveyor.yml. [Jamie] - -- Updated packages. [tidusjar] - -- Updated Polly. [tidusjar] - -- Updated Fr, IT and NL translations #402. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Changed the way the donate button works for #414. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Added MediatR. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -### **Fixes** +- New translations en.json (Polish) [Jamie] -- User management stuff. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- Fixed! [tidusjar] +- New translations en.json (Italian) [Jamie] -- Small amount of work on the user management. [tidusjar] +- New translations en.json (German) [Jamie] -- Fixed #466. [tidusjar] +- New translations en.json (French) [Jamie] -- Fixes. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Made the episode request better. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Removed commented out tests. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Fixed the bad test after the merge. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Reworked #466. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- More unit tests around the login and also the core Plex Checker. [tidusjar] +- New translations en.json (Polish) [Jamie] -- Potentially fixed the issue where we were requesting everything that was also available now. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- This should fix #466. [tidusjar] +- New translations en.json (Italian) [Jamie] -- Attempt at fixing a potential bug found from #466. [tidusjar] +- New translations en.json (German) [Jamie] -- #464 fixed. [tidusjar] +- New translations en.json (French) [Jamie] -- Fixed the build. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Small improvements to the wizard. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Always set the wizard to be true when editing the Plex Requests settings (Since the flag is not in the UI, a bool defaults to false). [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Tiny bit of more info. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Finished #459. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- #459 is almost done. [tidusjar] +- New translations en.json (Polish) [Jamie] -- Modified the episode modal so that we are now resetting the button after a request. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- Commented out the transaction for now to debug it. [tidusjar] +- New translations en.json (Italian) [Jamie] -- Since we are multithreading, we should use a threadsafe type to store the episodes to prevent any threading or race conditions. [tidusjar] +- New translations en.json (German) [Jamie] -- Wrapped the bulk insert inside a transaction. [tidusjar] +- New translations en.json (French) [Jamie] -- Made the episode check parallel. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Log out the GUID causing the issue. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Fixed another test. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Fixed tests. [tidusjar] +- New translations en.json (German) [Jamie] -- Got mostly everything working for #254 Ready for testing. [tidusjar] +- New translations en.json (German) [Jamie] -- Fixed issue with saving to db. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Need to work out why the cacher is not working and where the datatype mismatch is. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Don't delete first. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Fix the log path issue #451. [tidusjar] +- New translations en.json (Polish) [Jamie] -- Dump an item. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- Small change with the return value in the batch insert. [tidusjar] +- New translations en.json (Italian) [Jamie] -- #254 Removed the cache, we are now storing the plex information into the database. [tidusjar] +- New translations en.json (German) [Jamie] -- Small change in the episode saver. [tidusjar] +- New translations en.json (French) [Jamie] -- Some small tweaks to improve the memory alloc. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Short circuit when Plex hasn't been setup. Added Miniprofiler. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Consolidate newtonsoft.json packages. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Some performance improvements around the new TV stuff. [tidusjar] +- Fix #2529 - Change data type to long. [Anojh] -- Reworked the cacher, fixed the memory leak. No more logging within tight loops. [tidusjar] +- Fix #2527 - Music request not triggering search and failing. [Anojh] -- Another null check. [tidusjar] -- Some more changes. [tidusjar] +## v3.0.3776 (2018-09-21) -- Some error handling. [tidusjar] +### **New Features** -- Check if the sonarr ep is monitored. [tidusjar] +- Update settingsmenu.component.html. [Jamie] -- Some logging. [tidusjar] +- Added the request limits in the ui for music. [Jamie] -- Small changes, we will actually see the episode cacher on the scheduled jobs page now. [tidusjar] +- Added the root folders and qualities per user! [Jamie] -- Work on the UI to show what episodes have been requested #254. [tidusjar] +- Updated all the MS packages. [TidusJar] -- Small fix. [tidusjar] +- Update the .net core packages to fix "CVE-2018-8409: ASP.NET Core Denial Of Service Vulnerability" [TidusJar] -- Fix the api change in #450. [tidusjar] +- Change way remainingrequests component is notified. [Kenton Royal] -- #254. [tidusjar] +- Added the music request limits. [TidusJar] -- Workaround for #440. [tidusjar] +- Added the Notification Preferences to the user. [TidusJar] -- Async async async improvements. [tidusjar] +- Added the API to add user notification preferences. [TidusJar] -- Finished #266 Added a new cacher job to cache all episodes in Plex. [tidusjar] +- Added more logging into the updater. [Jamie] -- Fixed #442. [tidusjar] +- Update CHANGELOG.md. [Jamie] -- #254. [tidusjar] +### **Fixes** -- Work around the sonarr bug #254. [tidusjar] +- Fixed #2518. [TidusJar] -- #254 having an issue with Sonarr. [tidusjar] +- Fixed #2522. [TidusJar] -- Small bit of work on #266. [tidusjar] +- Fixed #2485. [TidusJar] -- #254. [tidusjar] +- Fixed #2516. [TidusJar] -- Precheck and disable the episode boxes if we already have requested it. TODO check sonarr to see if it's already there. #254. [tidusjar] +- Fix bug in which requested TV wasn't logging for some users. [Kenton Royal] -- Fixed broken build. [tidusjar] +- Add to translations. [Kenton Royal] -- More work for #254. [tidusjar] +- Add html for displaying remaining requests on users page. [Kenton Royal] -- More work on #254. [tidusjar] +- Add quota fields to user view model. [Kenton Royal] -- Fixed the bug in #438 and added unit tests to make so we dont break it in the future. [tidusjar] +- Users can now see the music search tab #2493. [TidusJar] -- Some reason we had dupe translations. [tidusjar] +- Add href to a tags so that a pointer cursor shows on requests page. [Stephen Panzer] -- Rename SubDir to Base Url. [tidusjar] +- Allow Lidarr to specify if we should search for the album. [TidusJar] -- Fix the exception in #440. [tidusjar] +- Fixed the issue if in Radarr we only want to add and monitor, if the movie already exists we search for it. [TidusJar] -- Reworking the login page for #426. [tidusjar] +- Fix bug causing wrong time to be displayed for next request. [Kenton Royal] -- Fixed #438. [tidusjar] +- Bodge fix test to prevent compile error. [Kenton Royal] -- Finished the auth stuff. [tidusjar] +- Fix displaying year in issue dialog. [Stephen Panzer] -- Finished up the SMTP side of #429. [tidusjar] +- Add clearfix class. Closes #2486. [Stephen Panzer] -- #428 Added a message when the we cannot get a TVMaze ID. [tidusjar] +- Correct path of lidarr component import for unix systems. [Kenton Royal] -- #254 MOSTLY DONE! At last, this took a while. [tidusjar] +- Refactor code. [Kenton Royal] -- Removed the other rootpath provider. [TidusJar] +- Fix formatting error. [Kenton Royal] -- Removed the other rootpath provider. [TidusJar] +- Revert "Revert request.service.ts to version on upstream/develop" [Kenton Royal] -- Removed the other rootpath provider. [TidusJar] +- Revert request.service.ts to version on upstream/develop. [Kenton Royal] -- Should fix #429. [TidusJar] +- Fix lint errors. [Kenton Royal] -- Done #135 We are including the application version number in the directory. [tidusjar] +- Move logic for notifying when reuqest is complete. [Kenton Royal] -- #387 trim the spaces from the api key. Tidied up the setting models a bit. [tidusjar] +- Remove import. [Kenton Royal] -- Wrapped the repo to catch Sqlite corrupt messages. [tidusjar] +- Remove unused module. [Kenton Royal] -- Frontend and tv episodes api work for #254. [tidusjar] +- Refactor code. [Kenton Royal] -- #424. [tidusjar] +- Add text to translation file. [Kenton Royal] -- #359. [tidusjar] +- Fix query for fetching requested tv shows. [Kenton Royal] -- Moved the plex auth token to the plex settings where it should belong. [tidusjar] +- Add vscode to gitignore. [Kenton Royal] -- Small changes around the user management. [tidusjar] +- Fix lint errors. [Kenton Royal] -- Missed brace. [tidusjar] +- Remove unused methods from SearchController. [Kenton Royal] -- Removed. [tidusjar] +- Remove local vscode files. [Kenton Royal] -- Fixed issues from the merge. [tidusjar] +- Fix bug when submitting requests for multiple episodes accross multiple seasons. [Kenton Royal] -- Stupid &$(*£ merge. [tidusjar] +- Fix bug with TV requests in which requesting a seasion would treat request as single episode. [Kenton Royal] -- Angular. [tidusjar] +- Fix issues with remaining count updating. [Kenton Royal] -- Reworked the custom notifications... again. Need to figure out how to find the view to the model. [tidusjar] +- Trigger update of request limit on new request. [Kenton Royal] -- Fixed #417. [tidusjar] +- Add logic for movie request count. [Kenton Royal] -- Removed NinjectConventions, we hadn't started to use it anyway. [tidusjar] +- Add logic for retriving request information. [Kenton Royal] -- Fixed the way we will be using custom messages. [tidusjar] +- Move to seperate component and display for both TV and movies. [Kenton Royal] -- Test checkin. [tidusjar] +- Add dummy for request counter. [Kenton Royal] -- Better handling for #388. [tidusjar] +- Fix scss import for unix systems. [Kenton Royal] -- Fixed #412. [tidusjar] +- Add methods to interface and add model class. [Kenton Royal] -- Fixed #413. [tidusjar] +- !fixed lint. [TidusJar] -- Fixed #409. [tidusjar] +- Fixed #2481. [TidusJar] -- Trycatch around the availbility checker. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- WIP on notification resolver. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Tidy. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Plugged in MediatR. [tidusjar] +- New translations en.json (Polish) [Jamie] -- Moved over to using Ninject. [tidusjar] +- New translations en.json (Norwegian) [Jamie] +- New translations en.json (Italian) [Jamie] -## v1.8.4 (2016-06-30) +- New translations en.json (German) [Jamie] -### **Fixes** +- New translations en.json (French) [Jamie] -- Fixed the bug where we were auto approving everything. Added French language into the navigation bar. [tidusjar] +- New translations en.json (Dutch) [Jamie] +- New translations en.json (Danish) [Jamie] -## v1.8.3 (2016-06-29) +- Fixed #2475. [Jamie] -### **New Features** +- Stript out certain characters when sending a pushover message #2385. [TidusJar] -- Update README.md. [Jamie] +- Add default values for Priority and Sound. [David Pooley] -- Update appveyor.yml. [Jamie] +- Allow for the ability to set Pushover notification sound and priority from within Ombi. [David Pooley] -- Added some of the backend bits for #182. [tidusjar] +- It works now when we request an album when we do not have the artist in Lidarr. Waiting on https://github.com/lidarr/Lidarr/issues/459 to do when we have the artist. [Jamie] -- Updates for #243. [tidusjar] +- Fix non-Windows builds. Fixes #2453. [Joe Groocock] -- Added Dutch language #243. [tidusjar] -- Added languages #243. [tidusjar] +## v3.0.3587 (2018-08-19) -- Added logging #350. [tidusjar] +### **New Features** -### **Fixes** +- Added the ability to invite Plex Friends from the user management screen. [Jamie] -- Small changes. [tidusjar] +- Added rich notifications for mobile. [Jamie] -- Allow html in the notice message. [tidusjar] +- Updater fixes. [Jamie] -- Some more unit tests around the NotificationMessageResolver. [tidusjar] +- Added updater test mode. [Jamie Rees] -- Fixed a timing bug found the in build. Note, when working with time differences use TotalDays. [tidusjar] +- Added a new API method to delete issue comments. [TidusJar] -- More translations on the search page (Mainly the notification messages) #243. [tidusjar] +- Updated @ngu/carousel to beta version to remove rxjs-compat dependency. [Matt Jeanes] -- Fixed some warnings. [tidusjar] +- Update to Angular 6/Webpack 4. [Matt Jeanes] -- CodeCleanup. [tidusjar] +- Update CHANGELOG.md. [Jamie] -- Fixed a bit of a stupid bug in the resetter and added unit tests around it to make sure this never happens again. [tidusjar] +- Updated the way we create the wizard user, errors show now be fed back to the user. [Jamie] -- Fixed an issue where we didn't provide the correct response when clearing the logs. [tidusjar] +- Added Brazillian Portuguese as a language and also Polish. [Jamie] -- Made it so users that are in the whitelist do not have a request limit. [tidusjar] +- Updated swagger. [Jamie] -- Made it so the request limit doesn't apply to admin users. [tidusjar] +- Updated to 2.1.1. [Jamie] -- Fixed where a user could see the delete button on the issues page. [tidusjar] +### **Fixes** -- Fixed some small issues and improved the navbar. [tidusjar] +- Now include the release year in the issue title #2381. [TidusJar] -- Translated the Requested page #243. [tidusjar] +- Made the OAuth a Popout to work with Org. [Jamie] -- Finished #337. [tidusjar] +- Fixed #2418. [TidusJar] -- Some analytics. [tidusjar] +- #2408 Added the feature to delete comments on issues. [Jamie] -- More translations for #243 and welcome text for #293. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Small bit of work for #359. [tidusjar] +- New translations en.json (French) [Jamie] -- Finished #6. [tidusjar] +- Fixed #2440. [TidusJar] -- Analytics and fixes. [tidusjar] +- Delete cake.config. [Chris Pritchard] -- Translated the search page #243. [tidusjar] +- Initial attempt at getting anime seriestype working. [Chris Pritchard] -- Implemented the different languages and added the ability to change cultures. #243. [tidusjar] +- Add cake.config. [Chris Pritchard] -- Started #243. [tidusjar] +- Fixed the issue where we wouldn't correctly mark some shows as available when there was no provider id #2429. [Jamie] -- Fixed #364. [tidusjar] +- Fixed the 'loop' in the cacher #2429. [Jamie] -- Some more useful analytical information. [tidusjar] +- Fixed #2427. [Jamie] -- Generic try catch to fix #350. [tidusjar] +- Fixed #2424. [Jamie] -- Slight changes, moved the donate button. [tidusjar] +- Fixed #2409. [Jamie] -- Potential fix for #350. [tidusjar] +- More updater. [Jamie] -- Better way of obtaining clean enum string. [Drewster727] +- Humanize the request type enum in notifications e.g. TvShow will now appear as "Tv Show" #2416. [TidusJar] -- Fixed #362. [tidusjar] +- Made the quality override and root folder override load when we load the show (It will now appear) [Jamie] +- Fixed #2415 where power users could not set the Sonarr Quality Override or Root Folder Override. [Jamie] -## v1.8.2 (2016-06-22) +- #2371 Fixed the issue where certain actions would not setup the series correctly in Sonarr. [Jamie] -### **New Features** +- Tightened up the security from an API perspecitve. [TidusJar] -- Update readme. [tidusjar] +- Stop the root folder and profile calls from erroring. [TidusJar] -- Update appveyor.yml. [Jamie] +- New translations en.json (Polish) [Jamie] -### **Fixes** +- New translations en.json (Polish) [Jamie] -- Fixed a circular reference issue. [tidusjar] +- New translations en.json (Polish) [Jamie] -- Small changes around how we work with custom events in the analytics. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Fixed #353 #354 #355. [tidusjar] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Null provider check for movies. [Drewster727] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Show request type in notifications #346 and fix an issue from previous commit for #345. [Drewster727] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Add an option to stop sending notifications for requests that don't require approval #345. [Drewster727] +- New translations en.json (Portuguese, Brazilian) [Jamie] +- Fixed all linting. [TidusJar] -## v1.8.1 (2016-06-21) +- Comment out envparam stuff. [Matt Jeanes] -### **New Features** +- Fixed prod build issue. [Matt Jeanes] -- Update appveyor.yml. [Jamie] +- Missed a tiny bit. [Matt Jeanes] -### **Fixes** +- Fix test. [Matt Jeanes] -- Fix obj ref error when scheduler runs (ProviderId is null?) [Drewster727] +- Fix test build. [Matt Jeanes] -- Fix logic for obtaining a sonarr quality profile #340. [Drewster727] +- Linting + remove debug. [Matt Jeanes] +- Switch to Yarn and disable auto publish in release mode. [Matt Jeanes] -## v1.8.0 (2016-06-21) +- Fix for #2409. [TidusJar] -### **New Features** +- New translations en.json (Swedish) [Jamie] -- Update README.md. [Jamie] +- New translations en.json (Spanish) [Jamie] -- Update README.md. [Jamie] +- New translations en.json (Portuguese, Brazilian) [Jamie] -- Update README.md. [Jamie] +- New translations en.json (Polish) [Jamie] -- Added the new advanced search into the search page too. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- Change the way we configure the IoC container in the bootstrapper, we are registering all the concrete instances on application start rather than on each web request. This should increase the performance per HTTP request. [tidusjar] +- New translations en.json (Italian) [Jamie] -- Updated nlog and fixed #295. [tidusjar] +- New translations en.json (German) [Jamie] -### **Fixes** +- New translations en.json (French) [Jamie] -- Workaround for #334. [Drewster727] +- New translations en.json (Dutch) [Jamie] -- Create .gitattributes. [Jamie] +- New translations en.json (Danish) [Jamie] -- Fixes to the issues. [tidusjar] +- Possible fix for #2298. [D34DC3N73R] -- Set the defaults for the landing page. [tidusjar] +- Fixed the text for #2370. [Jamie] -- Revert branch to 664dae2. [tidusjar] +- Fixed where you couldn't bulk edit the limits to 0 #2318. [Jamie] -- Some unit tests for the issues. [tidusjar] +- Upgraded to .net 2.1.2 (Includes security fixes) [Jamie] -- Tidied up the bootstrapper. [tidusjar] -- Fix up landing page UI. [Drewster727] +## v3.0.3477 (2018-07-18) -- Fixed CSS issue with the top arrow in the Plex theme. [tidusjar] +### **New Features** -- Small changes. [tidusjar] +- Updated the Emby availability checker to bring it more in line with what we do with Plex. [TidusJar] -- Done #318. [tidusjar] +- Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363. [Jamie Rees] -- Fixed tests. [tidusjar] +- Added more background images and it will loop through the available ones. [Jamie Rees] -- #298 added some tests for the landing page. [tidusjar] +- Added chunk hashing to resolve #2330. [Jamie Rees] -- We are now only keeping the latest 1000 log records in the database. Delete everything else. [tidusjar] +- Added API at /api/v1/status/info to get branch and version information #2331. [Jamie Rees] -- Some analytic stuff. [tidusjar] +- Update to .net 2.1.1. [Jamie] -- Capture the TVDBID when requesting. [tidusjar] +### **Fixes** -- Attempting to improve #219. [tidusjar] +- Fix #2322 caused by continue statement inside try catch block. [Anojh] -- Just some more async changes. [tidusjar] +- Fixed #2367. [TidusJar] -- Small changes. [tidusjar] +- Fixed the issue where you could not delete a user #2365. [TidusJar] -- More work on #298. Everything wired up. [tidusjar] +- Another attempt to fix #2366. [Jamie Rees] -- Fixed the issue on the landing page #298. [tidusjar] +- Fixed the Plex OAuth warning. [Jamie] -- #298 moved the content to the left a bit. [tidusjar] +- Revert "Fixed Plex OAuth, should no longer show Insecure warning" [Jamie Rees] -- Styling for #298 done, just need to wire up the model and do the actual status check. [tidusjar] +- Fixed Plex OAuth, should no longer show Insecure warning. [Jamie Rees] -- Bumped up the version number. [tidusjar] +- Fixed the View On Emby URL since the Link changed #2368. [Jamie Rees] -- Removed some DumpJson() from the trace logs. [tidusjar] +- Fixed the issue where episodes were not being marked as available in the search #2367. [Jamie Rees] -- Small ui fix (100% width user/password fields to improve mobile experience) [Drewster727] +- Fixed #2371. [Jamie Rees] -- Landing page stuff #298. [tidusjar] +- Fixed collection issues in Emby #2366. [Jamie Rees] -- Datepicker UI fixes + small landing page UI fix. [Drewster727] +- Do not delete the Emby Information every time we run, let's keep the content now. [Jamie Rees] -- Removed a change that shoudn't have been commited. [tidusjar] +- Emby Improvements: Batch up the amount we get from the server. [Jamie Rees] -- Fixed tests. [tidusjar] +- Log errors when they are uncaught. [Jamie Rees] -- More work for #298. [tidusjar] +- Fix unclosed table tags causing overflow #2322. [Anojh] -- #273 added for only available content on the search. [tidusjar] +- This should now fix #2350. [Jamie] -- Fixed #303 Looks like there was some incorrect business logic. [tidusjar] +- Improve the validation around the Application URL. [Jamie Rees] -- Most of #273 done. [tidusjar] +- Fixed #2341. [Jamie Rees] -- Settings done for #298. [tidusjar] +- Stop spamming errors when FanArt doesn't have the image. [Jamie Rees] -- Started #298. [tidusjar] +- Fixed #2338. [Jamie Rees] -- A crap tonne of work on #273. [tidusjar] +- Removed some logging statements. [Jamie Rees] -- More work on #273. [tidusjar] +- Fixed the api key being case sensative #2350. [Jamie Rees] -- Reduced kept logs for 2 days. [tidusjar] +- Improved the Emby API #2230 Thanks Luke! [Jamie Rees] -- Fixed #300. [tidusjar] +- Revert. [Jamie Rees] -- #273. [tidusjar] +- Fixed a small error in the Mobile Notification Provider. [Jamie Rees] -- Fixed a bug with some users with the CP profiles. [tidusjar] +- Minor style tweaks. [Randall Bruder] -- #273. [tidusjar] +- Downgrade to .net core 2.0. [Jamie Rees] -- Done the same for TV. [tidusjar] +- Downgrade Microsoft.AspNetCore.All package back to 2.0.8. [Jamie Rees] -- Fixes #296. [tidusjar] +- Removed old code. [Jamie Rees] -- More for #273. [tidusjar] +- Swap out the old way of validating the API key with a real middlewear this time. [Jamie Rees] -- Small changes. [tidusjar] -- Revert "Small changes" [tidusjar] +## v3.0.3421 (2018-06-23) -- Small changes. [tidusjar] +### **New Features** -- Finished #221 and added more async #278. [tidusjar] +- Added TVRequestsLite. [Jamie] -- Spelling mistake in the html! this fixes #264. [tidusjar] +- Added a smaller and simplier way of getting TV Request info. [Jamie Rees] -- More work on #273. [tidusjar] +### **Fixes** -- Fixed #210. [tidusjar] +- Show the popular movies and tv shows by default. [Jamie] -- Started #273. [tidusjar] +- Fixed #2348. [Jamie] -## v1.7.5 (2016-05-29) +## v3.0.3407 (2018-06-18) ### **New Features** -- Update preview. [Jamie] - -- Updated dapper.contrib. Looks like there was a bug in the async methods. [tidusjar] +- Update appveyor.yml. [Jamie] -- Updater wouldn't work when running a reverse proxy #236. [tidusjar] +- Update build.cake. [Jamie] ### **Fixes** -- Bump build ver. [tidusjar] - -- Use HTTPS for the poster images, so there aren't any mixed content warnings when serving the application via an HTTPS reverse proxy. [Sean Callinan] +- Fixed the issue where when we find an episode for the recently added sync, we don't check if we should run the availbility checker. [Jamie] -- Removed static declarations. [tidusjar] +- Fixed the API not working due to a bug in .Net Core 2.1. [Jamie] -- Fixed styling on modal. [tidusjar] +- Fixed #2321. [Jamie] -- Made the search page all async goodness #278. [tidusjar] +- Maybe this will fix #2298. [Jamie] -- Made the request module async #278. [tidusjar] +- Fixed #2312. [Jamie] -- Started some dynamic scrolling. [tidusjar] +- Fixed the SickRage/Medusa Issue where it was always being set as Skipped/Ignore #2084. [Jamie] -- Stop dumping out the settings to the log. [tidusjar] +- Fixed the sorting and filtering on the Movie Requests page, it all functions correctly now. [Jamie] -- Made more async goodness. [tidusjar] +- Fixed #2288. [Jamie] -- Made some of the searching async #278. [tidusjar] +- Upgrade packages. [Jamie] -- Fixed #277. [tidusjar] +- Inital Migration. [Jamie] -- Reworked some tests. [tidusjar] +- Fixed #2317. [Jamie] -- #26q make the auth users list taller. [Drewster727] -- Fix 404 error. [Drewster727] +## v3.0.3383 (2018-06-07) -- #262 make the auth users list taller. [Drewster727] - -- #221 delete requests per category. [Drewster727] +### **New Features** -- #256 #237 UI Improvements and consolidation. [Drewster727] +- Update CHANGELOG.md. [Jamie] -- Fixed a bug in the user notification where if an admin wants to be notified they wouldn't be. [tidusjar] +### **Fixes** -- Set the admin to have all claims. [tidusjar] +- Minor improvements. [Jamie] -- Fix null exception possibility in cp/sickrage cacher classes. [Drewster727] +- Run the availability checker on finish of the recentlty added sync. [Jamie] -- Fixed #244. [tidusjar] +- Fixed the issue with the Recently Added Sync sometimes not working as expected. [Jamie] -- Fixed #240. [tidusjar] +- The UI looks at the local time to see if the JWT token has expired. Use local time to generate the token. [Jamie Rees] -- Fixed #270. [tidusjar] -- Fixed an issue where if you have only 1 plex friend it would not show in the list. [tidusjar] +## v3.0.3368 (2018-06-03) +### **New Features** -## v1.7.4 (2016-05-25) +- Added the subscribe on the sarch page. [Jamie Rees] -### **New Features** +- Added the subscribe button to the search page if we have an existing request. [Jamie Rees] -- Update README.md. [Jamie] +- Update CHANGELOG.md. [Jamie] ### **Fixes** -- Fixed #252. [tidusjar] +- Use selected episodes in submitRequest. [Calvin] -- Fixed #428. [tidusjar] +- Fixed where the test button wouldn't work on the mobile notifications page. [Jamie] -- Version bump. [tidusjar] +- Fixed the sorting and filtering on the Movie Requests page, it all functions correctly now. [Jamie] -- Fixed tests. [tidusjar] +- Fixed #2288. [Jamie] -- Fully fixed #239. [tidusjar] +- Fixed the SickRage/Medusa Issue where it was always being set as Skipped/Ignore #2084. [Jamie] -- We wan't updating the DB schema. [tidusjar] +- Fixed UI typo refrencing discord in mattermost notifications #2175. [Anojh] +- Fix #2175. [Anojh] -## v1.7.3 (2016-05-25) +- Fixed #2013. [Jamie Rees] -### **Fixes** - -- Fixed the release build issue where we could not access the settings #239. [tidusjar] +- Fixed #2147. [Jamie Rees] -## v1.7.2 (2016-05-25) +## v3.0.3346 (2018-05-26) -### **Fixes** +### **New Features** -- Fixed a small bug where an exception would get thrown. [tidusjar] +- Added a default set of root folders and qualities for Anime in Sonarr. [Jamie Rees] -- Build version bump. [tidusjar] +### **Fixes** -- Cleanup. [tidusjar] +- Made the Open on Mobile link less hidden. [Jamie Rees] -- Typo. [tidusjar] +- Fixed #2263. [Jamie Rees] -- Fixed #241. [tidusjar] +- !changelog. [Jamie Rees] -- Fixed #239. [tidusjar] +- Fixed #2243 The refresh metadata was being run everytime we launched Ombi... [Jamie] -- Fixed #238. [tidusjar] +- Fixed a issue where the Plex Content Sync wouldn't pick up new shows #2276 #2244 #2261. [Jamie] -- Small UI tweaks/improvements. [Drewster727] +- Sort TvRequests by latest request. [Joe Harvey] +- Fixed build. [Jamie Rees] -## v1.7.1 (2016-05-24) +- Fix newsletter card background overflow when only one item is available. [Anojh] -### **New Features** +- Fix #1745. [Anojh] -- Update version. [tidusjar] -### **Fixes** +## v3.0.3330 (2018-05-17) -- Fixed an issue with the auth page when running with a reverse proxy. [tidusjar] +### **New Features** +- Added the test button for mobile notifications. [Jamie Rees] -## v1.7 (2016-05-24) +- Added classes to donation html elements. [Anojh] -### **New Features** +### **Fixes** -- Update appveyor.yml. [Jamie] +- !changelog. [Jamie Rees] -- Update README.md. [Jamie] +- Fixed #2257. [Jamie Rees] -- Update README.md. [Jamie] +- Improved the way we sync the plex content and then get the metadata. #2243. [Jamie Rees] -- Added the ability to get the apikey from the api if you provide a correct username and password. Added more unit tests Added the ability to change a users password using the api refactored the Usermapper and made it unit testsable. [tidusjar] +- Fixed the issue when enabling the Hide Request Users included system users e.g. API key user #2232. [Jamie Rees] -- Update. [tidusjar] +- Removed the test button from the mobile screen since it did nada. [Jamie Rees] -- Added in an audit table. Since we are now allowing multiple users to change and modify things we need to audit this. [TidusJar] +- Finished adding subscriptions for TV Shows. [Jamie Rees] -- Added the updater to the soloution and did a bit of starting code. [TidusJar] +- Fix #2167. [Anojh] -- Updated the claims so we can support more users. Added a user management section (not yet complete) Added the api to the solution and a api key in the settings (currently only gets the requests). [TidusJar] +- Fix #2228. [Anojh] -- Updated packages. [TidusJar] +- Enhanced newsletter styling to support more mail clients. [Anojh] -- Added a retry handler into the solution. We can now retry failed api requests. [TidusJar] +- Fix #2246. [Anojh] -- Update README.md. [Jamie] +- Fix #2234. [Anojh] -- Added Released propety to RequestViewModel. Added Released filter to the Requests page. [Chris Lees] +- Fixed that sometimes there would be a hidden error on the login page. [Jamie Rees] -- Added #27 to albums. [tidusjar] -- Added the actual notification part of #27. [tidusjar] +## v3.0.3304 (2018-05-09) -- Added the missing baseurl bit on the login page for #72. [tidusjar] +### **New Features** -- Added the 'enable user notifications' to the email settings view and model. [tidusjar] +- Updated to prevent security vulnerability as noted here: https://github.com/aspnet/Announcements/issues/300. [Jamie Rees] - Update README.md. [Jamie] ### **Fixes** -- Remove pointless test, change the default theme and fix a small bug. [tidusjar] - -- Fixed api. [tidusjar] +- [LC] - Added classes to root/quality override divs. [Anojh] -- Finished #26. [tidusjar] +- Fixed an issue where sometimes the OAuth wouldn't work when loading the login page. [Jamie Rees] -- Plex theme. [tidusjar] +- Alwats enable mobile link. [Jamie] -- Implimented a theme changer, waiting for the Plex theme. [tidusjar] -- Finished #222 #205. [tidusjar] +## v3.0.3293 (2018-05-05) -- Started working on #26. [tidusjar] - -- Undid some small changes that was checked in by accident. [tidusjar] +### **New Features** -- #164 has been resolved. [tidusjar] +- Added a check for long movie descriptions and dealt with accordingly. [Anojh] -- Resolved #224 , Removed the 'SSL' option from the email notification settings. We will now use the correct secure socket options (SSL/TLS) for your email host. [tidusjar] +- Update jobs.component.html. [D34DC3N73R] -- Small changes. [tidusjar] +- Added id to emby button to distinguish for UI purposes. [Anojh] -- #27 fully finished. [tidusjar] +- Changed theme content textarea to use monospace font. [Anojh] -- Fixed #215. [tidusjar] +- Added classes and ids to issue status. [Anojh] -- Using Mailkit to fix #204. [tidusjar] +- Changed overlay picture to poster pic so we have fallback styling on older clients. [Anojh] -- Color. [tidusjar] +### **Fixes** -- Fully finished #27 just need to test it! [tidusjar] +- Fixed #2224. [Jamie] -- Fixed test. [tidusjar] +- More robust check for release date. [Anojh] -- Styling for #27. [tidusjar] +- Fixed duplicate titles in Plex Newsletter. [Anojh] -- I think the auto updater is finished! #29. [tidusjar] +- Fixed the filter on the Requests page #2219 and added the default sort to be most recent requests. [Jamie Rees] -- I think we have finished the main bulk of the auto updater #29. [tidusjar] +- Enable the mobile ntoifications inside Ombi. [Jamie Rees] -- #222 #205 more ! Started getting the settings out. [tidusjar] +- Made the episode list in the newsletter easier to read. Rather than 1,2,3,4,5,10 we will now show 1-5, 10. [Jamie Rees] -- Removed the service locator from the base classes and added in some Api tests added all the tests back in! [tidusjar] +- Moved the RecentlyAddedSync into it's own job, it still is calls the regular sync but this should make it easier to start the job from the UI (When I add that) [Jamie Rees] -- More work on the api and documentation #222 #205. [tidusjar] +- Made a massive improvement on the Smaller more frequent Plex Job. This should pick up content a lot quicker now and also get their metadata a lot quicker. [Jamie Rees] -- Started documenting the API we now have swagger under ~/apidocs #222 #205. [tidusjar] +- Trigger a metadata refresh when we finish scanning the libraries. [Jamie Rees] -- Api work for #205 Refactored how we check if the user has a valid api key Added POST request, PUT and DELTE. [tidusjar] +- Fixed a potential issue in the newsletter where it wouldn't send content due to missing metadata, but would mark it as if it was sent. [Jamie Rees] -- First pass of the updater working. #29. [tidusjar] +- Fixed settings retaining active class when elsewhere in UI. [Anojh] -- Removed SIGHUP from the termination list #220. [tidusjar] +- Separated user and subject details into spans and fixed styling. [Anojh] -- Fixed. [tidusjar] +- Fixed linting errors. [Anojh] -- Missing. [tidusjar] +- Fixed settings nav item not retaining active class when in other tabs in the settings page. [Anojh] -- Missed out a file. [TidusJar] +- Separated reported by and subject and added classes. [Anojh] -- And some more... [TidusJar] +- Fix for issue #2152. [Anojh] -- Missed some files. [TidusJar] +- Fix genres being ambigious error. [Anojh] -- A bit more work on switching to using user claims so we can support multiple users. [TidusJar] +- Made text style justified. [Anojh] -- Made the store backup clean up some of the older backups (> 7 days). [TidusJar] +- V1.0, needs TV background and needs styles for outlook. [Anojh] -- More work on the user management. [TidusJar] +- CSS done for the template. [Anojh] -- - Notifications will no longer be send to the admins if they request something. - Looks like we missed out adding the notifications to Music requests, so I added that in. [TidusJar] +- Fixing some format issues. [Anojh] -- - Improved the RetryHandler. - Made the tester buttons on the settings pages a bit more robust and added an indication when it's testing (spinner) [TidusJar] +- Newsletter template structure done. [Anojh] -- Packages. [TidusJar] -- Nm, [TidusJar] +## v3.0.3268 (2018-04-28) -- Downgraded packages. [TidusJar] +### **Fixes** -- Better handling for #202. [TidusJar] +- Potential fix for #2119. [Jamie Rees] -- Finished #208 and #202. [TidusJar] +- Use the Application URL if we have it to fix #2201. [Jamie] -- This should help #202. [TidusJar] -- Resolved #209. [TidusJar] +## v3.0.3239 (2018-04-26) -- Finished #209. [TidusJar] +### **New Features** -- Slight adjustments to #189. [tidusjar] +- Update appveyor.yml. [Jamie] -- - Added a visual indication on the UI to tell the admin there is a update available. - We are now also recording the last scheduled run in the database. [tidusjar] +- Added paging to the TV Requests page. [Jamie Rees] -- Did the login bit on #185. [tidusjar] +- Added Paging to the Movie Requests Page. [Jamie Rees] -- Finished #186. [tidusjar] +- Updated Mailkit dependancy. [Jamie Rees] -- Fixed #185. [tidusjar] +- Update Hangfire, Newtonsoft and Swagger. [Jamie Rees] -- Fixed issue in #27 with albums. [tidusjar] +- Added View on Emby Button (#2173) [Anojh Thayaparan] -- #27 added TV Search to the notification. [tidusjar] +- Added background property to tvrequests API (#2172) [Anojh Thayaparan] -- Fixed bug. [tidusjar] +### **Fixes** -- More work on #27 Added a new notify button to the search UI (Needs styling). Also fixed a bug where if the user could only see their own requests, if they search for something that has been requested, it will show as requested. [tidusjar] +- Clean up the error code when the OAuth user is not authorized. [Jamie] -- Improved the startup of the application. We now properaly parse any args passed into the console. [tidusjar] +- More improvements to the Plex OAuth, Added the ability to turn it off if needed. [Jamie] -- Additional cacher error handling + don't bother checking the requests when we don't get data back from plex. [Drewster727] +- Fixed bug #2188 #2134. [Jamie] -- Remove old migration code and added new migration code. [tidusjar] +- Fixed the bug where only showing API User #2187. [Jamie] -- Stop the Cachers from bombing out when the response from the 3rd party api returns an exception or invalid response. #171. [tidusjar] +- Detect if baseurl is already set, and reset the link. [Anojh] -- Increase the scheduler cache timeframe to avoid losing cache when the remote api endpoints go offline (due to a reboot or some other reason) -- if they're online, the cache will get refreshed every 10 minutes like normal. [Drewster727] +- Fixed #2164. [Jamie Rees] -- Fix the cacher by adding locking + extra logging in the plex checker + use a const key for scheduler caching time. [Drewster727] +- Fixed #2151. [Jamie Rees] -- Small changes. [tidusjar] +- Fixed #2170. [Jamie Rees] -- Switched out the schedulers, this seems to be a better implimentation to the previous and is easier to add new "jobs" in. [tidusjar] +- Fixed the newsletter not sending #2134. [Jamie Rees] -- Fixed #168. [tidusjar] +- Fix baseurl breaking themes. [Anojh] -- Fixed #162. [tidusjar] +- Inject base url if set before theme file url, see issue #1795. [Anojh] -- Fix saving the log level. [Drewster727] +- Sign In rather than Login/Continue. [Avi] -- Set the max json length (fixes large json response errors) [Drewster727] +- Fixed #2179. [Jamie Rees] +- Fixed #2169. [Jamie Rees] -## v1.6.1 (2016-04-16) +- Knocking out LC requirements in issue #2124 (#2125) [Anojh Thayaparan] -### **New Features** +- Inject base url if set before theme file url, see issue #1795 (#2148) [Anojh Thayaparan] -- Update README.md. [Jamie] -- Added a url base. [tidusjar] +## v3.0.3185 (2018-04-16) -- Change default logging. [tidusjar] +### **New Features** -- Added logging around SickRage. [tidusjar] +- Added a new Job. Plex Recently Added, this is a slimmed down version of the Plex Sync job, this will just scan the recently added list and not the whole library. I'd reccomend running this very regulary and the full scan not as regular. [Jamie] ### **Fixes** -- Bump up the version number ready for the release. [tidusjar] +- Add web-app-capable for IOS and Android. [Thomas] -- BaseUrl is finally finished! #72. [tidusjar] +- Fixed the bug where the newsletter CRON was not appearing on the job settings page. [Jamie] -- #72 Login page done. [tidusjar] +- Add base url as a startup argument #2153. [Jamie Rees] -- More changes for the urlbase #72. [tidusjar] +- Fixed a bug with the RefreshMetadata where we would never get TheMovieDBId's if it was missing it. [Jamie] -- Done the auth, cp, logs and sidebar for #72. [tidusjar] -- Add an extra check when determining if a tv show is already available (also check if it starts with the show name returned from the tv db) [Drewster727] +## v3.0.3173 (2018-04-12) -- Cache plex library data regardless of whether we have requests in the database or not. [Drewster727] +### **Fixes** -- By default don't use a url base. [tidusjar] +- Removed some early disposition that seemed to be causing errors in the API. [Jamie] -- Return empty array when obtaining queued IDs in sickrage cacher. [Drewster727] -- Fixed a small bug in the SR cacher. [tidusjar] +## v3.0.3164 (2018-04-10) -- Fixed when we do not have a base. [tidusjar] +### **New Features** -- More changes for #72. [tidusjar] +- Added the ability to send newsletter out to users that are not in Ombi. [Jamie] -- Fixed exception and all areas will now use the base url #72. [tidusjar] +- Added the ability to turn off TV or Movies from the newsletter. [Jamie] -- Removed the test code from #72. [tidusjar] +- Update about.component.html. [Jamie] -- Commented out the unit tests as they need to be reworked now. [tidusjar] +- Update about.component.html. [Jamie] -- Finally fixed #72. [tidusjar] +- Added random versioning prefix to the translations so the users don't have to clear the cache. [Jamie] -- Remove test code from plex api GetLibrary method. [Drewster727] +- Added more information to the about page. [Jamie] -- Finished up the caching TODO's. [tidusjar] +- Changed let to const to adhere to linting. [Anojh] -- Kick off the schedulers once the web app has started (fixes api errors on start) [Drewster727] +- Update _Layout.cshtml. [goldenpipes] -- Converted the UI back down to .NET 4.5.2. [tidusjar] +- Update _Layout.cshtml. [goldenpipes] -- Fixed #154. [tidusjar] +- Changed the TV Request API. We now only require the TvDbId and the seasons and episodes that you want to request. This should make integration regarding TV a lot easier. [Jamie] -- Revert everything (except PlexRequests.UI) back to .NET 4.5.2 -- fixes incompatibilities with the latest version of mono (4.2.3.4) -- fixes notifications not working #152 #147 #141. [Drewster727] +### **Fixes** -- #150 start caching plex media as well. refactored the availability checker. NEEDS TESTING. also, we need to make the Requests hit the plex api directly rather than hitting the cache as it does now. [Drewster727] +- Emby improvments on the way we sync/cache the data. [Jamie] -- #150 split out the cache subscriptions to make sure they subscribe properly. [Drewster727] +- Memory improvements. [Jamie] -- #150 sonarr/sickrage cache checking. sickrage has a couple small items left. [Drewster727] +- Made some improvements to the Sonarr Sync job #2127. [Jamie] -- Fixed args. [tidusjar] +- Turn off Server GC to hopefully help with #2127. [Jamie Rees] -- Fixed. [tidusjar] +- Fixed #2109. [Jamie] -- Made the base better. [tidusjar] +- Fixed #2101. [Jamie] -- Remove couchpotato api test code. [Drewster727] +- Fixed #2105. [Jamie] -- Start the initial couchpotato cache call on a separate thread to keep the startup process quick. [Drewster727] +- Fixed some styling on the issues detail page. [Jamie] -- Add csproj with file changes from previous commit. [Drewster727] +- Fixed #2116. [Jamie] -- Cache the couchpotato wanted list, update it on an interval, and use it to determine if a movie has been queued already. [Drewster727] +- Limit the amount of FileSystemWatchers being spawned. [Jamie] -- I think i've fixed an issue where SickRage reports Show not found. [tidusjar] +- Fixed the issue where Emby connect users could not log in #2115. [Jamie] -- Set the default log level to info. #141. [tidusjar] +- Had to update some base styles since currently some styling does not look right... [Anojh] -- #125 refactor async task logic to work with mono. [Drewster727] +- Adding wrappers and classes for LC and toggling active style for UI elements. [Anojh] -- Fix search spinner sticking around after clearing search text + make the "Requested" and "Available" indicators in the search page different colors. [Drewster727] +- Fixed a little bug in the newsletter. [Jamie] -- #125 start indicating in the results if an item is already requested or available. [Drewster727] +- Fixed the issue where movies were not appearing in the newsletter for users with Emby #2111. [Jamie] -- #145 firefox css dsplay issue. [Drewster727] +- The fact that this button has another style really bothers me. [Louis Laureys] -- Fixes for sonarr, we now display the error messages back to the user. [tidusjar] +- Fix discord current user count. [Avi] -- Fixed #144. [tidusjar] +- Fix broken images and new discord invite. [Avi] -## v1.6.0 (2016-04-06) +## v3.0.3111 (2018-03-27) ### **New Features** -- Changed the build number. [tidusjar] - -- Update README.md. [Drew] - -- Update README.md. [Drew] +- Added the Recently Added Newsletter! You are welcome. [tidusjar] -- Update README.md. [Drew] +- Added a new scrollbar to Ombi. [tidusjar] -- Update README.md. [Drew] +- Added the ability to automatically generate the API Key on startup if it does not exist #2070. [tidusjar] -- Changed the title to a contains but the artist still must match, [tidusjar] +- Updated npm dependancies. [Jamie] -- Added unit tests to cover the new changes to the availability checker. [tidusjar] +- Update README.md. [Jamie] -- Added the music check in the Plex Checker. [tidusjar] +- Update README.md. [Jamie] -- Changed around the startup so we cache the profiles after the DB has been created. [tidusjar] +- Update ISSUE_TEMPLATE.md. [Jamie] -- Updated where we update the request blobs schema change. [tidusjar] +- Update appveyor.yml. [Jamie] -- Update SearchModule.cs. [Jamie] +- Added recently added stuff. [Jamie] -- Update README.md. [Jamie] +- Added the recently added engine with some basic methods. [Jamie] -- Update README.md. [Jamie] +- Added the ability to refresh out backend metadata (#2078) [Jamie] -- Change the new columns type. [tidusjar] +- Update README.md. [zobe123] -- Added a DBSchema so we have an easier way to update the DB. [tidusjar] +### **Fixes** -- Added an issue template. [tidusjar] +- Specific favicons for different platforms. [louis-lau] -- Update README.md. [Jamie] +- MovieDbId was switched to string fron number so accomodated for change. [Anojh] -- Added back the username into the Session when the admin logs in. This means they do not have to log in twice. [tidusjar] +- Removing duplicate functions. [Anojh Thayaparan] -- Added happy path tests for the Checker. [tidusjar] +- Conflict resolving and adopting Jamie's new method. [Anojh] -- Added music to the search and requests page. [tidusjar] +- Wrote new calls to just get poster and bg. [Anojh] -- Added a scroll to the top thingy and a bit more work on headphones. [tidusjar] +- Fix for issue #1907, which is to add content poster and bg to issue details page. [Anojh] -- Added some tests and fixed the issue where the DB would get created in the wrong place depending on how you launched the application. [tidusjar] +- Dynamic Background Animation. [Anojh] -- Added the settings page for #32. [tidusjar] +- Improved the message for #2037. [tidusjar] -- Update README.md. [Drewster727] +- Improved the way we use the notification variables, we have now split out the Username and Alias (Requested User is depricated but not removed) [tidusjar] -- Update README.md. [Drewster727] +- Removed redundant timers. [Anojh] -- Update README.md. [Drewster727] +- More optimizations by reducing requests. [Anojh] -- Update README.md. [Drewster727] +- Improved version. [Anojh] -- Update README.md. [Drewster727] +- Dynamic Background Animation. [Anojh] -- Update appveyor.yml. [Jamie] +- Fixed #2055 and #1903. [Jamie] -### **Fixes** +- Small changes to the auto updater, let's see how this works. [Jamie] -- Some final tweaks for #32. [tidusjar] +- Fixed build. [Jamie] -- Fixed a bug where if we are the admin we didn't add the request to the db. [tidusjar] +- Fixed the update check for the master build. [Jamie] -- Fixed an issue where we would add the Sickrage series but it would fail on adding the seasons. [tidusjar] +- Fixed build. [Jamie] -- Properly account for future/past dates when humanizing with moment. [Drewster727] +- Fixed #2074 and #2079. [Jamie] -- Properly display release date on requests page. [Drewster727] -- Add missing reference for release mode. [Drewster727] +## v3.0.3030 (2018-03-14) -- #139 remove dependency and usage of humanize() - should help with cross-platform issues. start using moment.js. [Drewster727] +### **New Features** -- Fix selectors for music list on request page to get sorting working. [Drewster727] +- Updated the .Net core dependancies #2072. [Jamie] -- Fixed the error #32. [tidusjar] +### **Fixes** -- Fixed the logs page. [tidusjar] +- Delete Ombi.testdb. [Jamie] -- Another attempt at filtering #32. [tidusjar] -- A bit more error handling #32. [tidusjar] +## v3.0.3020 (2018-03-13) -- Improved the availabilty check to include music results #32. [tidusjar] +### **Fixes** -- Small changes for #32. [tidusjar] +- Small memory improvements in the Plex Sync. [Jamie] -- A bit more logging for #32. [tidusjar] +- Fixed the sort issue on the user Management page. Also added sorting to the Movie Requests page. [tidusjar] -- More headphones #32 I am starting to hate headphones... Sometimes the artists and albums just randomly fail. [tidusjar] +- Downgraded the angular2-jwt library since it has a bug in it. #2064. [tidusjar] -- #134 temporary workaround for this. [Drewster727] +- Fixed an issue when Plex decideds to reuse the Plex Key for a different media item... #2038. [tidusjar] -- Task.run for startup caching + fix admin module unit test failures. [Drewster727] +- Fixed an issue where we might show the Imdb link when we do not have a imdbid #1797. [tidusjar] -- Cache injection, error handling and logging on startup, etc. [Drewster727] +- Fixed the issue where we can no longer select Pending Approval in the filters #2057. [tidusjar] -- Tweaks for #32. [tidusjar] +- Fixed the API key not working when attempting to get requests #2058. [tidusjar] -- #132 auto-approve for admins. [Drewster727] +- Fixed #2056. [tidusjar] -- Finished the bulk work for Headphones. Needs testing #32. [tidusjar] +- Experimental, set the Webpack base root to the ombi base path if we have it. This should hopefully fix the reverse proxy issues. [Jamie] -- Made the album search 10x faster. We are now loading the images in a seperate call. #32. [tidusjar] +- Fixed #2056. [tidusjar] -- Add a reference to API Interfaces to fix the build. [tidusjar] -- #114 start caching quality profiles. Set the cache on startup and when obtaining quality profiles in settings. [Drewster727] +## v3.0.3000 (2018-03-09) -- Work for #32. [tidusjar] +### **New Features** -- #114 first pass at choosing quality profile when approving + focus search input by default and when switching tabs. [Drewster727] +- Added the ability to override root and quality options in Sonarr (#2049) [Jamie] -- #131 fix for default selected tab. [Drewster727] +- Added Pending Approval into the filters list. [tidusjar] -- Remove references to obsolete RequestedBy property + start setting the db schema to the app version, and check that in the future for migrations. [Drewster727] +- Added the ability to hide requests that have not been made by that user (#2052) [Jamie] -- Fixed async issue. [Shannon Barrett] +- Update README.md. [Jamie] -- Updating SickRage api to verify Season List is up to date. [Shannon Barrett] +- Update README.md. [Louis Laureys] -- Work on showing the requests for #32. [tidusjar] +### **Fixes** -- Got the search finished up for #32. [tidusjar] +- Fixed #2042. [Jamie] -- Remove test/temp code in UserLoginModule. [Drewster727] -- A bit more work on #32 started working on requesting it. The DB is a bit of an issue... [tidusjar] +## v3.0.0 (2018-03-04) -- Most of the UI work done for #32. [tidusjar] +### **New Features** -- Basic search working for #32. [tidusjar] +- Update build.cake. [Jamie] -- Mono datetime offset workaround. [Drewster727] +- Update README.md. [Jamie] -- #122 store utc time in the databse + obtain timezone offset of the client upon login + offset times returned to client based on session offset. [Drewster727] +- Update README.md. [Jamie] -- Method reference bug fix. [Drewster727] +- Added a similar button to the movie searches. Makes movie discoverablility easier. [tidusjar] -- Fix search focus z-index issue (hid suggestions options) [Drewster727] +- Update README.md. [Jamie] -- Minor search UI adjustments. [Drewster727] +- Update README.md. [Jamie] -- #55 first attempt at "suggestions" starting with "Comming Soon" and "In Theaters" [Drewster727] +- Update ISSUE_TEMPLATE.md. [Jamie] -- #106 rename sorting options and polish the dropdown UI a bit. [Drewster727] +- Update appveyor.yml. [Jamie] -- Started adding the api part for headphones #32. [tidusjar] +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -- Upped the time of #123. [tidusjar] +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -- First attempt at #123. [tidusjar] +- Update README.md. [Jamie] -- We now do not show the text Requested By to the user, we also show a 'success' message instead of a warning when something has already been requested. [tidusjar] +- Update README.md. [Jamie] -- Show a "no requests yet" message on the requests page (for each cateogory) [Drewster727] +- Update README.md. [Jamie] -- Ignore items that are already available when approving in bulk, and simplify the checking + compile css. [Drewster727] +- Update README.md. [PotatoQuality] -- Add a better way to merge RequestedBy and RequestedUsers to avoid code duplication and simplify checks. [Drewster727] +- Change the default templates to use {IssueUser} [Jamie] -- Don't query the session as much in the modules, rely on a variable from the base class and store the username as needed. [Drewster727] +- Changed the base url validation. [tidusjar] -- Show the requested by user from legacy request models. [Drewster727] +- Added bulk editing (#1941) [Jamie] -- Only show requested by users to admins + start maintaining a list of users with each request. [Drewster727] +- Change the poster size to w300 #1932. [Jamie] -- #96 fix up notification test feature. [Drewster727] +- Added a default user agent on all API calls. [tidusjar] -- Fix the request page sort/approve button alignment. [Drewster727] +- Update request.service.ts. [Jamie] -- When pulling requests, set each to approved that is already available (so the UI avoids showing the approve option for already available content) [Drewster727] +- Added a filter onto the movies requests page for some inital feedback. [Jamie] -- Mono doesn't seem to have Tls1.2. Let's try TLS 1 #119. [tidusjar] +- Added ordering to the User Management screen. [Jamie] -- Specify a protocol type of TLS12. Looks like CP doesn't seem to like SSL3 (it is quite old now so understandable) #119. [tidusjar] +- Update README.md. [Jamie] -- Made #85 better. [tidusjar] +- Added custom donation url (#1902) [m4tta] -- Fixed the tests. [tidusjar] +- Changed the url scheme to make it easier to parse. [Jamie] -- Made the feedback from Sonarr better when Sonarr already has the series #85. [tidusjar] +- Added Norwegian to the translation code, forgot to check this in. [Jamie] -- An attempt to fix #108. [tidusjar] +- Added Norwegian to the language dropdown. [Jamie] -- Add some "no results" feedback to the searching + minor UI improvements. [Drewster727] +- Added the stuff needed for omBlur. [tidusjar] -- Fix notification tests. [Drewster727] +- Update README.md (#1872) [xnaas] -- UI - increase icon size of nav menu (they were too small before) [Drewster727] +- Update README.md. [Jamie] -- #96 Finished adding test functionality to notifications. [Drewster727] +- Update plex.component.html. [Jamie] -- #96 add the necessary back-end code to produce a test message for all notification types (still have to add the test buttons for pushbullet/pushover) [Drewster727] +- Change plus to list in menu (#1855) [Louis Laureys] -- #96 modify notifications interface/service to accept a non-type specific settings object. [Drewster727] +- Update README.md. [Jamie] -- #96 Email notification test button (others to come) [Drewster727] +- Update README.md. [Jamie] -- Minor UI adjustments. [Drewster727] +- Added user request limits, We can now set the limit for a user. [tidusjar] -- #84 provide an option in settings to resttrict users from viewing requests other than their own. [Drewster727] +- Updated the UI JWT framework. [Jamie] -- #54 comma separated list of users who don't require approval + fix a couple request messages (include show title) [Drewster727] +- Added missing migrations #1744. [Jamie] -- Clean up the sorting option names. add a way to see which filter/sort is currently applied. [Drewster727] +- Added the SickRage API integration. [Jamie] -- Fix up the animations. seems to be related to the data-bound attribute causing the animtions not to fire on each .mix object. [Drewster727] +- Update the Emby Connect Username in the user importer. To update the emby connect email address we do it when the user logs in, since the only way to get that information is to use the users Username and Password, since we do not keep this information we cannot do it in the User Importer, but if they have successfully logged in via Emby Connect then we check if we need to update the email address on login. [Jamie] -- Move approve buttons to the tab content. [Drewster727] +- Update the connectuserid for emby users. [tidusjar] -- Allow approving all requests by category. [Drewster727] +- Added the ability to customize job scheudles. [Jamie] -- Fix up sorting on the request page. [Drewster727] +- Update README.md. [Jamie] -- Add ubuntu/debian instructions. [Drewster727] +- Added the option to import the plex admin. [tidusjar] -- #86 - display movie/show title + year in request notifications. [Drewster727] +- Added the route name as an Id on the container div #1698. [Jamie] -- Show the movie/show title when requesting. [Drewster727] +- Updated packages including uglify-es and the package-lock #1683. [Jamie] +- Updated to Angular5 with best practises. [Jamie] -## v1.5.2 (2016-03-26) +- Update README.md. [Jamie] -### **Fixes** +- Added the new backgrounds for the requests pages. [tidusjar] -- Stoped users from spamming the request button. [tidusjar] +- Added caching to the settings. [tidusjar] -- Fixed the logger no longer writing to the file. [tidusjar] +- Added some better handling when adding existing seasons to a tv show in the Plex cacher. [tidusjar] -- Fixed #97. [tidusjar] +- Added Telegram Notification support, Not tested. [Jamie] +- Added the new banner background for tv shows. [tidusjar] -## v1.5.1 (2016-03-26) +- Added a new customization option to provide a css link. [Jamie] -### **New Features** +- Update README.md. [Jamie] -- Update appveyor.yml. [Jamie] +- Update README.md. [Jamie] -- Added logs to the sidebar. I'm an idiot. [tidusjar] +- Changed to discord link. [tidusjar] -### **Fixes** +- Added more translations. [Jamie] -- Approve tv shows or movies. [Drewster727] +- Added more logging for #1643. [tidusjar] -- Fixed a bug where if you had auto approve it wouldn't notify you. [tidusjar] +- Update README.md. [Jamie] +- Update Crowdin configuration file. [Jamie] -## v1.5.0 (2016-03-25) +- Added more translations. [Jamie] -### **New Features** +- Update da.json (#1634) [Lixumos] -- Updated version number for release. [tidusjar] +- Added 32bit version of Ombi. [Jamie] -- Updated the logic for handling specific seasons in Sonarr and Sickrage. [Shannon Barrett] +- Added more translations. [Jamie] -- Updated the readme and added some icons to the navbar. [tidusjar] +- Added translations. [Jamie] -- Added the ability to sepcify a username in the email notification settings for external MTA's. We have had to add a new option called Email Sender because of this. #78. [tidusjar] +- Updated the npm packages. [Jamie.Rees] -- Update README.md. [Jamie] +- Added four new endpoints to change the availability for TV shows and Movies #1601. [Jamie.Rees] -- Update README.md. [Jamie] +- Added the ability to run a user defined update script #1460. [Jamie.Rees] -- Added a notification model to the notifiers. Added the backend work for sending a notification for an issue report #75. [tidusjar] +- Added logging around creating the wizard user #1604. [tidusjar] -- Added a subdir to CP, SickRage, Sonarr and Plex #43. [tidusjar] +- Added the option to run the content cacher from the settings page, it will no longer get triggered when we press save. [tidusjar] -### **Fixes** +- Added the ability to specify how many episodes we should cache at a time. #1598. [tidusjar] -- And again. [tidusjar] +- Added usersname and password option for the updater #1460. [Jamie.Rees] -- Made the check actually work. [tidusjar] +- Changed the way we download the .zip files in the auto updater #1460 This might make a difference to the permissions issue. but not 100% sure. [Jamie.Rees] -- Finished up #68 and #62. [tidusjar] +- Changed cake. [Jamie.Rees] -- Finished styling on the logger for now. #59. [tidusjar] +- Added feedback when we send a welcome email #1578. [Jamie.Rees] -- Fixed #69. [tidusjar] +- Update README.md. [Jamie] -- Working on getting the Sonarr component to work correctly. [Shannon Barrett] +- Added some logging into the PlexCachers and set the log level to informational. [tidusjar] -- Fixes issue #62. [Shannon Barrett] +- Added Couchpotato support and fixed #1548. [tidusjar] -- Refactored the Notification service to how it should have really been done in the first place. [tidusjar] +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -- Fixed the build. [tidusjar] +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -- Finished #49. [tidusjar] +- Update README.md. [Jamie] -- Finished #57. [tidusjar] +- Added the ability to use a cookie to login. Mainly for Org. [tidusjar] -- Small changes around the filtering. [tidusjar] +- Added support for a new TV and Movie provider. DogNZB. [tidusjar] -- Finished adding pushover support. #44. [tidusjar] +- Added a PartlyAvailable flag for tv shows. [Jamie.Rees] -- Resolved #75. [tidusjar] +- Added some feedback on the request buttons for movies #1536. [Jamie.Rees] -- Include DB changes. [tidusjar] +- Added a fix for the poster path issue #1533. [Jamie.Rees] -- Done most on #59. [tidusjar] +- Added back the log path writing #865. [Jamie.Rees] -- Lowercase logs folder, because you know, linux. #59. [tidusjar] +- Update README.md. [Jamie] -- Adding the imdb when requesting. [tidusjar] +- Added support for email login #1513. [tidusjar] -- Fixed an issue where the table didn't match the model. [tidusjar] +- Added some more logging around the plex user importer fixed the discord notification issue #1513. [tidusjar] -- Improved the status page with the suggestion from #29. [tidusjar] +- Update README.md. [PotatoQuality] -- Hooked up most of #49 Just the validation messages need to be done. [tidusjar] +- Update README.md. [PotatoQuality] -- Fixed #74 and #64. [tidusjar] +- Update README.md. [PotatoQuality] -- Resolved #70. [tidusjar] +- Update README.md. [PotatoQuality] -- Finished #71. [tidusjar] +- Update README.md. [PotatoQuality] -- Got the filter working on both movie and tv #57. [tidusjar] +- Update Readme for V3. [PotatoQuality] -- Started #57, currently there is a bug where the TV list won't filter. [tidusjar] +- Added some wiki pages and also made it so we cannot edit Plex Users email address (since they will get overwrote via the Importer) #865. [Jamie.Rees] +- Added transparency to icon files (#1520) [Auwen] -## v1.4.1 (2016-03-20) +- Added an application URL in the customization settings #1513. [Jamie.Rees] -### **New Features** +- Update ISSUE_TEMPLATE.md. [Jamie] -- Update appveyor.yml. [Jamie] +- Added the ability to enable Plex User importing. We also allow you to exclude users #1456. [tidusjar] -- Update AvailabilityUpdateService.cs. [Jamie] +- Update Startup.cs. [Jamie] +- Added an about page #865. [Jamie.Rees] -## v1.4.0 (2016-03-19) +- Changelog. [Jamie.Rees] -### **New Features** +- Changed the way we download the updates #865. [Jamie.Rees] -- Update README.md. [Jamie] +- Updated packages and more logging. [Jamie.Rees] -- Update README.md. [Jamie] +- Update versioning. [Jamie.Rees] -- Update README.md. [Jamie] +- Update nuget packages and added logging to the Updater #865. [tidusjar] -- Update README.md. [Jamie] +- Added the ForwardedHeaders middlewear for Reverse Proxy scenarios #865. [tidusjar] -- Updated the build version ready for the next release. [tidusjar] +- Update build.cake. [Jamie] -- Added the api and settings page for Sickrage. Just need to do the tester and hook it up #40. [tidusjar] +- Update DiscordApi.cs. [Jamie] -- Added the option to set a CP quality #38. [tidusjar] +- Update README.md. [PotatoQuality] -- Added the code to lookup the old requests and refresh them with new information from TVMaze. [tidusjar] +- Added a authorization filter so we can see hangfire outisde of the local requests. [TidusJar] -- Update StatusCheckerTests.cs. [Jamie] +- Added more logging for the updater. [Jamie.Rees] -- Update README.md. [Jamie] +- Added the emby episode cacher and the job to check if items are available on emby #1464 #865. [tidusjar] -- Added TVMaze to the search. #21. [tidusjar] +- Added the Emby Cacher, we now cache the Emby data! [tidusjar] -- Added migration code and cleaned up the DB. [tidusjar] +- Updated CHangelog. [Jamie.Rees] -- Updated the way we add requests. [tidusjar] +- Updated changelog. [Jamie.Rees] -- Updated the Dapper.Contrib package, it had a bug where it wasn't returning the correct Id from inserts. [tidusjar] +- Updated assembly versions. [Jamie.Rees] -### **Fixes** +- Added the logo in the email notifications to use the application image #1459. [Jamie.Rees] -- This fixes #36. [tidusjar] +- Change Os to VS2015. [Jamie.Rees] -- Should fix issue #36. [Shannon Barrett] +- Added multiple emby server support and enabled it for Plex #865. [tidusjar] -- When we do a batch update we need to reset the cache. [tidusjar] +- Update ISSUE_TEMPLATE.md. [Jamie] -- Fixed an issue where the default quality on Sickrage wouldn't work. [tidusjar] +- Update README.md. [Jamie] -- Wow, that was a lot of work. - So, I have now finished #40. - Fixed a bug where we was not choosing the correct tv series (Because of TVMaze) - Fixed a bug when checking for plex titles - Fixed a bug where the wrong issue would clean on the UI (DB was correct) - Refactored how we send tv shows - And too many small changes to count. [tidusjar] +- Added slack #1459 #865. [Jamie.Rees] -- Fixed the new dependancy with the admin class tests. [tidusjar] +- Added a checkbox to the usermanagement screen.. Does nothing yet #865 #1456. [Jamie.Rees] -- Back to what it was :( [tidusjar] +- Update build.cake. [Jamie] -- Another test for #37. [tidusjar] +- Added swagger into the .zips. [Jamie.Rees] -- This should fix #37. [Jamie Rees] +- Added Cake build #865. [Jamie.Rees] -- Catch the missing table exception when they have a new DB. [Jamie Rees] +- Added Pushbullet notifications #1459 #865. [Jamie.Rees] -- Exploratory test for #37. [Jamie Rees] +- Update README.md. [Jamie] -- Fixed #33 we now have SSL options for Sonarr and CP. [Jamie Rees] +- Update README.md. [Jamie] -- Removed all the html from the new TVMaze api (for overview). Added tests to cover the html removal. updated Readme to remove TheTVDB. [Jamie Rees] +- Update README.md. [Jamie] -- Fixed tests. [Jamie Rees] +- Added the logging endpoint at /api/v1/Logging for the UI logs #1465. [tidusjar] -- Almost fully integrated TVMaze #21 and also improved the fix for #31. [Jamie Rees] +- Change the RID. [Jamie.Rees] -- Should fix #28. [Shannon Barrett] +- Update README.md. [Jamie] -- Fixed #16 and #30. [tidusjar] +- Update README.md. [Jamie] -- Modified the adding of request to update the model with the added ID. [tidusjar] +- Updated Changelog. [Jamie.Rees] -- Switched over to the new service. [tidusjar] +- Added changelog. [Jamie.Rees] -- Fixed #25. [Jamie Rees] +- Update README.md. [Jamie] +- Updated stuff. [Jamie.Rees] -## v1.3.0 (2016-03-17) +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -### **New Features** +- Update ISSUE_TEMPLATE.md. [PotatoQuality] -- Added pushbullet to the sidebar. [Jamie Rees] +- Added the Ombi or customized logo onto the login page #865. [TidusJar] -- Updated build version for the next release. [Jamie Rees] +- Added new login page #865. [Jamie] -- Updated readme link. [tidusjar] +- Added Migrations rather than a manual DB Script #865. [Jamie.Rees] -- Added ignore to static tests. [tidusjar] +- Updated all the packages. [tidusjar] -- Added Pushbullet notifications #8. [tidusjar] +- Added a skip options #865. [tidusjar] -- Added first implimentation of the Notification Service #8 Added tests to cover the notification service. [tidusjar] +- Added the minimumAvailability #865. [tidusjar] -- Added validation to the Email settings, also increased the availability checker from 2 minutes to 5. [tidusjar] +- Updater build script. [Jamie.Rees] -### **Fixes** +- Added the testing notifications and discord notification #865. [Jamie.Rees] -- Fixed #22. [Jamie Rees] +- Added tests into the build. [Jamie.Rees] -- Started on #16, nothing is hooked up yet. [tidusjar] +- Added Swagger. [Jamie.Rees] -- Fixed tests. [tidusjar] +- Added Discord notification #865. [Jamie.Rees] +- Added the Movie Sender, Movies will be sent to Radarr now #865. [Jamie.Rees] -## v1.2.1 (2016-03-16) +- Added SQLite storage for Hangfire. [Jamie.Rees] -### **New Features** +- Added the Radarr cacher #865. [tidusjar] -- Update Program.cs. [Jamie] +- Added Radarr and Sonarr settings pages #865. [Jamie.Rees] -- Update Program.cs. [Jamie] +- Update appveyor.yml. [Jamie] -- Added back the reference. [tidusjar] +- Update appveyor.yml. [Jamie] -### **Fixes** +- Update appveyor.yml. [Jamie] -- Removed the email notification settings from the settings (for release 1.2.1) [Jamie Rees] +- Update appveyor.yml. [Jamie] -- Fixed. [Jamie Rees] +- Updated build script. [tidusjar] -- Resolved #10. [tidusjar] +- Added the Rules Engine Pattern and the Auto approve and request rules #865. [tidusjar] +- Update .gitattributes. [Jamie] -## v1.2.0 (2016-03-15) +- Added the TraktApi back. [tidusjar] -### **New Features** +- Changes. [Jamie.Rees] -- Updated. [Jamie Rees] +- Added some sonarr stuff. [Jamie.Rees] -- Updated appveyor. [Jamie Rees] +- Added Hangfire #865. [tidusjar] -- Update appveyor.yml. [Jamie] +- Update node again... [Jamie.Rees] -- Added latest version code and view. Need to finish the view #11. [tidusjar] +- Update node. [Jamie.Rees] -- Added test button to Plex. That's fixed #9. [tidusjar] +- Changes. [Jamie.Rees] -- Added test sonarr button #9. [tidusjar] +- Update StringCipher.cs. [Jamie] -- Added more tests. [tidusjar] +### **Fixes** -- Added a bunch of logging. [tidusjar] +- New translations en.json (Norwegian) (#2020) [Jamie] -- Added the application tester for CP #9. [tidusjar] +- Publish 32bit build of windows. [tidusjar] -- Added settings page for #8. [tidusjar] +- Fixing incorrect filter translation targets (#1987) [Jono Cairns] -- Added pace.js. [tidusjar] +- New Crowdin translations (#2017) [Jamie] -### **Fixes** +- Fixed #1997. [tidusjar] -- Finished the notes! Resolved #7. [Jamie Rees] +- We now show the digital release date in the search if available #1962. [tidusjar] -- #12. [Jamie Rees] +- Css fixes (#2014) [Louis Laureys] -- #12. [Jamie Rees] +- API improvements. [Jamie] -- Finished the status page #11 and some more work to #12. [Jamie Rees] +- Fix #1599 (#2008) [Louis Laureys] -- Resolved #7. [tidusjar] +- Issue button fix (#2006) [Louis Laureys] -- Small changes. [tidusjar] +- Fixed #1886 #1865. [Jamie] -- Yeah... [tidusjar] +- Fixed the outstanding issue on #1995. [Jamie] -- Fixed #5 and also added some tests to the availability checker. [tidusjar] +- Fixed an issue for #1951. [tidusjar] -- Started added tests. [Jamie Rees] +- Try and fuzzy match the title and release if we cannot get the tvdb id or imdbid (depends on the media agents in Plex) #1951. [tidusjar] -- Fixed an issue where the issues text appears larger. [Jamie Rees] +- Fixed #1989 #1719. [Jamie] +- Small changes that might fix #1985 but doubt it. [Jamie] -## v1.1 (2016-03-13) +- Should fix #1975. [tidusjar] -### **New Features** +- Fixed #1789. [tidusjar] -- Update appveyor.yml. [Jamie] +- Fixed #1968. [tidusjar] -- Updated readme. [Jamie Rees] +- Fixed #1978. [tidusjar] -- Added the support for TV Series integrating with Sonarr. [Jamie Rees] +- Fixed #1954. [tidusjar] -- Added the functionality to pass a port through an argument. [tidusjar] +- Small changes to the auto updater, let's see how this works. [Jamie] -- Added the code to get the quality profiles from Sonarr Started plugging that into the UI. [Jamie Rees] +- Fixed build. [Jamie] -- Added the spinners #3. [tidusjar] +- Fixed the update check for the master build. [Jamie] -- Added the functionality for the admin to clear the issues. [tidusjar] +- Removed accidently merged files. [Jamie] -- Added the issues to the requests page. [tidusjar] +- Create CODE_OF_CONDUCT.md. [Jamie] -- Added user logout method and unit tests to cover it. [tidusjar] +- Windows installation guide link update. [PotatoQuality] -- Added DeniedUsers to the view. [tidusjar] +- Fixed the issue comment issue #1914 also added another variable for issues {IssueUser} which is the user that reported the issue. [Jamie] -- Added the denied user check to the UserLoginModule. added a test case to cover it. [tidusjar] +- Fix #1914. [tidusjar] -- Added a missing reference. [tidusjar] +- Fixed #1914. [tidusjar] -- Added first real test. [tidusjar] +- Fixed build and added logging. [TidusJar] -- Update README.md. [Jamie] +- New Crowdin translations (#1934) [Jamie] -- Added the latest version of nuget. [tidusjar] +- Potential fix for #1942. [Jamie] -- Added travisyml. [tidusjar] +- Quick change to the Emby Availability rule to make it in line slightly with the Plex one. #1950. [Jamie] -- Added logging. [tidusjar] +- Turn off mobile notifications. [tidusjar] -- Added missing files. [tidusjar] +- FIXED PLEX!!!!! [tidusjar] -- Update README.md. [Jamie] +- Batch the PlexContentSync and increase the plex episode batch size. [tidusjar] -- Added logging (Still WIP) [tidusjar] +- Fixed the migration issue, it's too difficult to migrate the tables. [tidusjar] -- Added favicon and also structured the HTML correctly. [tidusjar] +- Fixed #1942. [tidusjar] -- Updated the packages so everything is now with the correct framework (4.5.2) [tidusjar] +- Fixed checkboxes style. [Jamie] -- Added in deletion of requests. [tidusjar] +- These are not the droids you are looking for. [Jamie] -- Added test code. [tidusjar] +- Fixed the wrong translation and see if we can VACUUM the db. [tidusjar] -- Added dashboard. [tidusjar] +- More translations and added a check on the baseurl to ensure it starts with a '/' [Jamie] -- Added couchpotato page. [Jamie Rees] +- More translations. [Jamie] -- Added readme to the project and updated it. [Jamie Rees] +- Fixed #1878 and added a Request all button when selecting episodes. [Jamie] -- Added helpers. [tidusjar] +- New translations en.json (Dutch) [Jamie] -### **Fixes** +- New translations en.json (German) [Jamie] -- Bug fix, Couchpotato settings wouldn't show in release due to a Nancy bug. [Jamie Rees] +- New translations en.json (German) [Jamie] -- Small changes. [Jamie Rees] +- New translations en.json (Danish) [Jamie] -- First release, build 1.0.0. [Jamie Rees] +- New translations en.json (Norwegian) [Jamie] -- Removed the request limit since it's not currently being used. [Jamie Rees] +- New translations en.json (Norwegian) [Jamie] -- REmoved Sickbeared for the first release. [Jamie Rees] +- New translations en.json (Norwegian) [Jamie] -- Fixed #4 We now can manually set the status of a request. [tidusjar] +- New translations en.json (Italian) [Jamie] -- Made the pass in the port a bit more robust. [tidusjar] +- New translations en.json (German) [Jamie] -- Styling, Added the functionality for the Sonarr Profiles on the Admin page #2 resolved. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Fixed a bug in the Login and added a unit test to cover that. Added a button to approve an individual request. Fixed some minor bugs in the request screen. [Jamie Rees] +- New translations en.json (Swedish) [Jamie] -- Fixed the 'responsive' issue for the search and requests pages #3. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Styling! #3. [tidusjar] +- New translations en.json (French) [Jamie] -- Navbar category now will follow you to various screens #3. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Fixed bugs with the 'other' reporting issue and also the clear issues. [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- We now are appending the users name to who wrote the comment. Rather than it being unknown. [tidusjar] +- New translations en.json (Italian) [Jamie] -- More work on submitting issues. [tidusjar] +- New translations en.json (German) [Jamie] -- More test changes. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- More tests to cover the login. [tidusjar] +- New translations en.json (Swedish) [Jamie] -- Refactoring. [tidusjar] +- New translations en.json (Spanish) [Jamie] -- Implimented the password part and authentication with Plex. [tidusjar] +- New translations en.json (French) [Jamie] -- Initial Use authentication is working. Need to do the password bit. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Some error handling and ensure we are an admin to delete requests. [tidusjar] +- New translations en.json (Danish) [Jamie] -- Fixed the issue where the Release build would not show the admin screens! [tidusjar] +- New translations en.json (Norwegian) [Jamie] -- Fixes. [tidusjar] +- New translations en.json (Italian) [Jamie] -- Removed the DI part of the service. TinyIOC doesn't want to work with FluentScheduler. [tidusjar] +- New translations en.json (German) [Jamie] -- First pass at the plex update service. [tidusjar] +- New translations en.json (Dutch) [Jamie] -- Small changes. [Jamie Rees] +- New translations en.json (Swedish) [Jamie] -- Started to impliment the Plex checker. This will check plex every x minutes to see if there is any new content and then update the avalibility of the requests. [Jamie Rees] +- New translations en.json (Spanish) [Jamie] -- Mre work. [Jamie Rees] +- New translations en.json (French) [Jamie] -- Few small changes, added plex settings. [Jamie Rees] +- New translations en.json (Danish) [Jamie] -- Making the configuration actually do something. Setting a default configuration if there is no DB. [Jamie Rees] +- New translations en.json (Swedish) [Jamie] -- Remove post build. [Jamie Rees] +- New translations en.json (Norwegian) [Jamie] -- Small changes. [Jamie Rees] +- Working on the movie matching. Stop dupes #1869. [tidusjar] -- MOre work. [Jamie Rees] +- Delete plex episodes on every run due to a bug, need to spend quite a bit of time on this. [tidusjar] -- Fixed the issue when sending movies to CouchPotato. [Jamie Rees] +- Fixed the issue where we were always adding emby episodes. Also fixed #1933. [tidusjar] -- Add appveyor. [tidusjar] +- New Crowdin translations (#1906) [Jamie] -- Build it on 4.5. [tidusjar] +- Add plain password for emby login (#1925) [dorian ALKOUM] -- Upgraded .net to 4.6. [tidusjar] +- Fixed #1924. [Jamie] -- Typo2. [tidusjar] +- Fixed the issue where I knocked out the ordering of notifications, oops. [tidusjar] -- Typo. [tidusjar] +- #1914 for the issue resolved notification. [Jamie] -- Another update. [tidusjar] +- #1916. [Jamie] -- Fixed. [tidusjar] +- Remove the placeholder. [Jamie] -- More logging to figure out why the we cannot access the admin module in a release build. [tidusjar] +- Feature arm (#1909) [Jamie] -- Firstpass integrating with CouchPotato. [tidusjar] +- New Crowdin translations (#1897) [Jamie] -- Some styling. [tidusjar] +- Fix logo cut off on login screen (#1896) [Louis Laureys] -- Fixed the plex friends. Added some unit tests, moved the plex auth into it's own page. [tidusjar] +- E-Mails: Only add poster table row if img is set (#1899) [Louis Laureys] -- Fully switched the TV shows over to use the other provider. [Jamie Rees] +- New Crowdin translations (#1884) [Jamie] -- Renamed folders. [tidusjar] +- Fix mobile layout (#1888) [Louis Laureys] -- Assembly updates. [tidusjar] +- Smal changes to the api. [tidusjar] -- Moved the rest of the projects. [tidusjar] +- OmBlur. [tidusjar] -- Moved UI. [tidusjar] +- Hide the password field if it's not needed #1815. [Jamie] -- Mass rename. [tidusjar] +- Should fix #1885. [Jamie] -- Quick changes. [tidusjar] +- Make user management table responsive (#1882) [Louis Laureys] -- Started switching the TV over to the new provider (TheTVDB). Currently TV search is partially broken. It will search but we are not mapping all of the details. [tidusjar] +- Fixed some stuff for omBlur. [Jamie] -- Implimented the new TV show Provider (needed for Sonarr TheTvDB) [tidusjar] +- Some work... No one take a look at this, it's a suprise. [Jamie] -- Started the user auth. [tidusjar] +- New Crowdin translations (#1858) [Jamie] -- Some work on the requests page. [tidusjar] +- When requesting Anime, we now mark it correctly as Anime in Sonarr. [tidusjar] -- Made the 'requested' better and made the remove look nicer. [tidusjar] +- Fixed #1879 and added the spans. [tidusjar] -- Cleaned up the program a tiny bit. [tidusjar] +- Some work on the auto updater #1460. [tidusjar] -- Removed additional namespace. [tidusjar] +- Removed the potential locking. [tidusjar] -- Fixed some db issues and added a preview. [Jamie Rees] +- Fixed #1863. [tidusjar] -- More work on the settings. [Jamie Rees] +- Moved the update check code from the External azure service into Ombi at /api/v1/update/BRANCH. [Jamie] -- Upgraded Json.Net and Nancy packages. [Jamie Rees] +- Fixed the UI erroring out, also dont show tv with no externals. [tidusjar] -- Plex friends api. [Jamie Rees] +- More memory management and improvements. [tidusjar] -- Enabled trace logs. [tidusjar] +- These are not needed, added accidentally (#1860) [Louis Laureys] -- Sql syntax issue fixed. [tidusjar] +- Some memory management improvements. [tidusjar] -- Fixed release build. [tidusjar] +- Fixed #1857. [tidusjar] -- Small updates including assembly version. [tidusjar] +- Delete old v2 ombi from v3 branch. [tidusjar] -- Work on the requests page mostly done. [tidusjar] +- New Crowdin translations (#1840) [Jamie] -- Work on the TV request. the `latest` parameter is not being passed into the requestTvshow. [tidusjar] +- Better login backgrounds! (#1852) [Louis Laureys] -- Missing file. [tidusjar] +- Fixed #1851. [tidusjar] -- Using the IoC container now. [tidusjar] +- Fixed #1826. [tidusjar] -- Some plex work. [Jamie Rees] +- Redo change #1848. [tidusjar] -- More work. [Jamie Rees] +- Fix the issue for welcome emails not sending. [tidusjar] -- Removed the setup code out of the startup, since we attemtp to connect to the DB before that. [Jamie Rees] +- Fix typo (#1845) [Kyle Lucy] -- Some more work. Need to stop the form submitting on a request. [tidusjar] +- Fix user mentions in Slack notifications (#1846) [Aljosa Asanovic] -- Moved everything up a directory. [tidusjar] +- If Radarr/Sonarr has noticed that the media is available, then mark it as available in the UI. [Jamie] -- Lots of work! [tidusjar] +- Fixed #1835. [Jamie] -- Done most of the movie search work. [Jamie Rees] +- Enable Multi MIME and add alt tags to images (#1838) [Louis Laureys] -- First pass with RequestPlex. [tidusjar] +- New Crowdin translations (#1816) [Jamie] -- Initial commit. [Jamie] +- Fixed #1832. [tidusjar] +- Switch to use a single HTTPClient rather than a new one every request !dev. [tidusjar] diff --git a/README.md b/README.md index 9b0e212e1..cfc0ad23c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ ![](http://i.imgur.com/qQsN78U.png) ____ -[![Discord](https://img.shields.io/discord/102860784329052160.svg)](https://discord.gg/KxYZ64w) +[![Discord](https://img.shields.io/discord/270828201473736705.svg)](https://discord.gg/Sa7wNWb) [![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/ombi.svg)](https://hub.docker.com/r/linuxserver/ombi/) [![Github All Releases](https://img.shields.io/github/downloads/tidusjar/Ombi/total.svg)](https://github.com/tidusjar/Ombi) -[![firsttimersonly](http://img.shields.io/badge/first--timers--only-friendly-blue.svg?style=flat-square)](http://www.firsttimersonly.com/) +[![firsttimersonly](http://img.shields.io/badge/first--timers--only-friendly-blue.svg)](http://www.firsttimersonly.com/) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/ombi/localized.svg)](https://crowdin.com/project/ombi) -[![Patreon](https://www.ombi.io/img/patreondonate.svg)](https://patreon.com/tidusjar/Ombi) -[![Paypal](https://www.ombi.io/img/paypaldonate.svg)](https://paypal.me/PlexRequestsNet) +[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi) +[![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet) + +___ +Get it on Google Play + +Get it on App Store + ___ diff --git a/appveyor.yml b/appveyor.yml index c513c650e..862993a21 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ version: 3.0.{build} configuration: Release os: Visual Studio 2017 environment: - nodejs_version: "7.8.0" + nodejs_version: "9.8.0" install: # Get the latest stable version of Node.js or io.js @@ -15,19 +15,19 @@ test: off after_build: - cmd: >- - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" diff --git a/build.cake b/build.cake index 58eadd651..d706e7b6b 100644 --- a/build.cake +++ b/build.cake @@ -1,10 +1,10 @@ #tool "nuget:?package=GitVersion.CommandLine" #addin "Cake.Gulp" -#addin "Cake.Npm" #addin "SharpZipLib" #addin nuget:?package=Cake.Compression&version=0.1.4 #addin "Cake.Incubator" +#addin "Cake.Yarn" ////////////////////////////////////////////////////////////////////// // ARGUMENTS @@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj var solutionFile = "Ombi.sln"; // Solution file if needed GitVersion versionInfo = null; -var frameworkVer = "netcoreapp2.0"; +var frameworkVer = "netcoreapp2.1"; var buildSettings = new DotNetCoreBuildSettings { @@ -122,36 +122,19 @@ Task("SetVersionInfo") Task("NPM") .Does(() => { - var settings = new NpmInstallSettings { - LogLevel = NpmLogLevel.Silent, - WorkingDirectory = webProjDir, - Production = true - }; - - NpmInstall(settings); + Yarn.FromPath(webProjDir).Install(); }); Task("Gulp Publish") .IsDependentOn("NPM") - .Does(() => { - - var runScriptSettings = new NpmRunScriptSettings { - ScriptName="publish", - WorkingDirectory = webProjDir, - }; - - NpmRunScript(runScriptSettings); + .Does(() => { + Yarn.FromPath(webProjDir).RunScript("publish"); }); Task("TSLint") .Does(() => { - var settings = new NpmRunScriptSettings { - WorkingDirectory = webProjDir, - ScriptName = "lint" - }; - - NpmRunScript(settings); + Yarn.FromPath(webProjDir).RunScript("lint"); }); Task("PrePublish") @@ -178,7 +161,7 @@ Task("Publish") .IsDependentOn("Publish-OSX") .IsDependentOn("Publish-Linux") .IsDependentOn("Publish-Linux-ARM") - //.IsDependentOn("Publish-Linux-ARM-64Bit") + .IsDependentOn("Publish-Linux-ARM-64Bit") .IsDependentOn("Package"); Task("Publish-Windows") @@ -189,6 +172,8 @@ Task("Publish-Windows") DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml"); + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); @@ -200,6 +185,9 @@ Task("Publish-Windows-32bit") DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml"); + + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x86/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); @@ -211,6 +199,8 @@ Task("Publish-OSX") DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml"); + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/osx-x64/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); @@ -222,6 +212,8 @@ Task("Publish-Linux") DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml"); + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-x64/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); @@ -235,6 +227,8 @@ Task("Publish-Linux-ARM") CopyFile( buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml"); + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); @@ -248,6 +242,8 @@ Task("Publish-Linux-ARM-64Bit") CopyFile( buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml"); + + publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm64/published/updater"); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); }); diff --git a/music-placeholder.psd b/music-placeholder.psd new file mode 100644 index 000000000..5715f850a Binary files /dev/null and b/music-placeholder.psd differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..10c7546e4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,291 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "1.0.3" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "requires": { + "path-parse": "1.0.5" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tslib": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", + "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==" + }, + "tslint": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.1", + "commander": "2.15.1", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.12.0", + "minimatch": "3.0.4", + "resolve": "1.7.1", + "semver": "5.5.0", + "tslib": "1.9.2", + "tsutils": "2.27.1" + } + }, + "tsutils": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", + "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", + "requires": { + "tslib": "1.9.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index 3af6d0dd5..fcb989094 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Internal; using Newtonsoft.Json; using Ombi.Api.Emby.Models; using Ombi.Api.Emby.Models.Media.Tv; @@ -90,27 +91,31 @@ namespace Ombi.Api.Emby request.AddContentHeader("Content-Type", "application/json"); } - public async Task> GetCollection(string mediaId, string apiKey, string userId, string baseUrl) + public async Task> GetCollection(string mediaId, string apiKey, string userId, string baseUrl) { var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get); AddHeaders(request, apiKey); - return await Api.Request>(request); + request.AddQueryString("Fields", "ProviderIds,Overview"); + + request.AddQueryString("VirtualItem", "False"); + + return await Api.Request>(request); } - public async Task> GetAllMovies(string apiKey, string userId, string baseUri) + public async Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Movie", apiKey, userId, baseUri); + return await GetAll("Movie", apiKey, userId, baseUri, true, startIndex, count); } - public async Task> GetAllEpisodes(string apiKey, string userId, string baseUri) + public async Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Episode", apiKey, userId, baseUri); + return await GetAll("Episode", apiKey, userId, baseUri, false, startIndex, count); } - public async Task> GetAllShows(string apiKey, string userId, string baseUri) + public async Task> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Series", apiKey, userId, baseUri); + return await GetAll("Series", apiKey, userId, baseUri, false, startIndex, count); } public async Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) @@ -129,20 +134,40 @@ namespace Ombi.Api.Emby private async Task GetInformation(string mediaId, string apiKey, string userId, string baseUrl) { var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get); + AddHeaders(request, apiKey); var response = await Api.RequestContent(request); return JsonConvert.DeserializeObject(response); } + private async Task> GetAll(string type, string apiKey, string userId, string baseUri, bool includeOverview = false) + { + var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); + + request.AddQueryString("Recursive", true.ToString()); + request.AddQueryString("IncludeItemTypes", type); + request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); + request.AddQueryString("VirtualItem", "False"); - private async Task> GetAll(string type, string apiKey, string userId, string baseUri) + AddHeaders(request, apiKey); + + + var obj = await Api.Request>(request); + return obj; + } + private async Task> GetAll(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count) { var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); request.AddQueryString("Recursive", true.ToString()); request.AddQueryString("IncludeItemTypes", type); + request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); + request.AddQueryString("startIndex", startIndex.ToString()); + request.AddQueryString("limit", count.ToString()); + + request.AddQueryString("VirtualItem", "False"); AddHeaders(request, apiKey); diff --git a/src/Ombi.Api.Emby/IEmbyApi.cs b/src/Ombi.Api.Emby/IEmbyApi.cs index 625ae3c13..b4641ea5f 100644 --- a/src/Ombi.Api.Emby/IEmbyApi.cs +++ b/src/Ombi.Api.Emby/IEmbyApi.cs @@ -14,12 +14,17 @@ namespace Ombi.Api.Emby Task LogIn(string username, string password, string apiKey, string baseUri); Task LoginConnectUser(string username, string password); - Task> GetAllMovies(string apiKey, string userId, string baseUri); - Task> GetAllEpisodes(string apiKey, string userId, string baseUri); - Task> GetAllShows(string apiKey, string userId, string baseUri); + Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, + string baseUri); - Task> GetCollection(string mediaId, string apiKey, string userId, - string baseUrl); + Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetAllShows(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetCollection(string mediaId, + string apiKey, string userId, string baseUrl); Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); diff --git a/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs b/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs index 34038edd8..a10ddaae6 100644 --- a/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs +++ b/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs @@ -28,5 +28,7 @@ namespace Ombi.Api.Emby.Models.Movie public string MediaType { get; set; } public bool HasSubtitles { get; set; } public int CriticRating { get; set; } + public string Overview { get; set; } + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs b/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs index d02c99e41..d76915923 100644 --- a/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs +++ b/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs @@ -39,5 +39,6 @@ namespace Ombi.Api.Emby.Models.Media.Tv public string LocationType { get; set; } public string MediaType { get; set; } public bool HasSubtitles { get; set; } + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs b/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs index 853c64d10..2aaf8d492 100644 --- a/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs +++ b/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs @@ -26,5 +26,7 @@ namespace Ombi.Api.Emby.Models.Media.Tv public string[] BackdropImageTags { get; set; } public string LocationType { get; set; } public DateTime EndDate { get; set; } + + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.FanartTv/FanartTvApi.cs b/src/Ombi.Api.FanartTv/FanartTvApi.cs index bc819311c..55caef72c 100644 --- a/src/Ombi.Api.FanartTv/FanartTvApi.cs +++ b/src/Ombi.Api.FanartTv/FanartTvApi.cs @@ -21,6 +21,7 @@ namespace Ombi.Api.FanartTv { var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get); request.AddHeader("api-key", token); + request.IgnoreErrors = true; try { return await Api.Request(request); @@ -36,6 +37,7 @@ namespace Ombi.Api.FanartTv { var request = new Request($"movies/{movieOrImdbId}", Endpoint, HttpMethod.Get); request.AddHeader("api-key", token); + request.IgnoreErrors = true; return await Api.Request(request); } diff --git a/src/Ombi.Api.Lidarr/ILidarrApi.cs b/src/Ombi.Api.Lidarr/ILidarrApi.cs new file mode 100644 index 000000000..4a23c6200 --- /dev/null +++ b/src/Ombi.Api.Lidarr/ILidarrApi.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Api.Lidarr.Models; + +namespace Ombi.Api.Lidarr +{ + public interface ILidarrApi + { + Task> AlbumLookup(string searchTerm, string apiKey, string baseUrl); + Task> ArtistLookup(string searchTerm, string apiKey, string baseUrl); + Task> GetProfiles(string apiKey, string baseUrl); + Task> GetRootFolders(string apiKey, string baseUrl); + Task GetArtist(int artistId, string apiKey, string baseUrl); + Task GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl); + Task GetAlbumsByArtist(string foreignArtistId); + Task GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl); + Task> GetArtists(string apiKey, string baseUrl); + Task> GetAllAlbums(string apiKey, string baseUrl); + Task AddArtist(ArtistAdd artist, string apiKey, string baseUrl); + Task MontiorAlbum(int albumId, string apiKey, string baseUrl); + Task> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl); + Task> GetMetadataProfile(string apiKey, string baseUrl); + Task> GetLanguageProfile(string apiKey, string baseUrl); + Task Status(string apiKey, string baseUrl); + Task AlbumSearch(int[] albumIds, string apiKey, string baseUrl); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/LidarrApi.cs b/src/Ombi.Api.Lidarr/LidarrApi.cs new file mode 100644 index 000000000..a6a283703 --- /dev/null +++ b/src/Ombi.Api.Lidarr/LidarrApi.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr.Models; + +namespace Ombi.Api.Lidarr +{ + public class LidarrApi : ILidarrApi + { + public LidarrApi(ILogger logger, IApi api) + { + Api = api; + Logger = logger; + } + + private IApi Api { get; } + private ILogger Logger { get; } + + private const string ApiVersion = "/api/v1"; + + public Task> GetProfiles(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/qualityprofile", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task> GetRootFolders(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/rootfolder", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public async Task> ArtistLookup(string searchTerm, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/Artist/lookup", baseUrl, HttpMethod.Get); + request.AddQueryString("term", searchTerm); + + AddHeaders(request, apiKey); + return await Api.Request>(request); + } + + public Task> AlbumLookup(string searchTerm, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/Album/lookup", baseUrl, HttpMethod.Get); + request.AddQueryString("term", searchTerm); + + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task GetArtist(int artistId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/artist/{artistId}", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + return Api.Request(request); + } + + public async Task GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/artist/lookup", baseUrl, HttpMethod.Get); + + request.AddQueryString("term", $"lidarr:{foreignArtistId}"); + AddHeaders(request, apiKey); + return (await Api.Request>(request)).FirstOrDefault(); + } + + public async Task GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/album/lookup", baseUrl, HttpMethod.Get); + + request.AddQueryString("term", $"lidarr:{foreignArtistId}"); + AddHeaders(request, apiKey); + var albums = await Api.Request>(request); + return albums.FirstOrDefault(); + } + + public Task GetAlbumsByArtist(string foreignArtistId) + { + var request = new Request(string.Empty, $"https://api.lidarr.audio/api/v0.3/artist/{foreignArtistId}", + HttpMethod.Get) {IgnoreBaseUrlAppend = true}; + return Api.Request(request); + } + + public Task> GetArtists(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task> GetAllAlbums(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task AddArtist(ArtistAdd artist, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post); + request.AddJsonBody(artist); + AddHeaders(request, apiKey); + return Api.Request(request); + } + + public async Task MontiorAlbum(int albumId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/album/monitor", baseUrl, HttpMethod.Put); + request.AddJsonBody(new + { + albumIds = new[] { albumId }, + monitored = true + }); + AddHeaders(request, apiKey); + return (await Api.Request>(request)).FirstOrDefault(); + } + + public Task> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get); + request.AddQueryString("artistId", artistId.ToString()); + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task> GetLanguageProfile(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/languageprofile", baseUrl, HttpMethod.Get); + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task> GetMetadataProfile(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/metadataprofile", baseUrl, HttpMethod.Get); + AddHeaders(request, apiKey); + return Api.Request>(request); + } + + public Task Status(string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/system/status", baseUrl, HttpMethod.Get); + AddHeaders(request, apiKey); + return Api.Request(request); + } + + public Task AlbumSearch(int[] albumIds, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/command/", baseUrl, HttpMethod.Post); + request.AddJsonBody(new { name = "AlbumSearch", albumIds }); + AddHeaders(request, apiKey); + return Api.Request(request); + } + + private void AddHeaders(Request request, string key) + { + request.AddHeader("X-Api-Key", key); + } + } +} diff --git a/src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs b/src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs new file mode 100644 index 000000000..62f19651f --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs @@ -0,0 +1,34 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class AlbumByArtistResponse + { + public Album[] Albums { get; set; } + public string ArtistName { get; set; } + public string Disambiguation { get; set; } + public string Id { get; set; } + public Image[] Images { get; set; } + public Link[] Links { get; set; } + public string Overview { get; set; } + public Rating Rating { get; set; } + public string SortName { get; set; } + public string Status { get; set; } + public string Type { get; set; } + } + + public class Rating + { + public int Count { get; set; } + public decimal Value { get; set; } + } + + public class Album + { + public string Disambiguation { get; set; } + public string Id { get; set; } + public string ReleaseDate { get; set; } + public string[] ReleaseStatuses { get; set; } + public string[] SecondaryTypes { get; set; } + public string Title { get; set; } + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs new file mode 100644 index 000000000..b2394eb5f --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs @@ -0,0 +1,25 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + public class AlbumLookup + { + public string title { get; set; } + public int artistId { get; set; } + public string foreignAlbumId { get; set; } + public bool monitored { get; set; } + public int profileId { get; set; } + public int duration { get; set; } + public string albumType { get; set; } + public string[] secondaryTypes { get; set; } + public int mediumCount { get; set; } + public Ratings ratings { get; set; } + public DateTime releaseDate { get; set; } + //public object[] releases { get; set; } + public object[] genres { get; set; } + //public object[] media { get; set; } + public Artist artist { get; set; } + public Image[] images { get; set; } + public string remoteCover { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/AlbumResponse.cs b/src/Ombi.Api.Lidarr/Models/AlbumResponse.cs new file mode 100644 index 000000000..f9d35c43b --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/AlbumResponse.cs @@ -0,0 +1,27 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + public class AlbumResponse + { + public string title { get; set; } + public string disambiguation { get; set; } + public int artistId { get; set; } + public string foreignAlbumId { get; set; } + public bool monitored { get; set; } + public int profileId { get; set; } + public int duration { get; set; } + public string albumType { get; set; } + public object[] secondaryTypes { get; set; } + public int mediumCount { get; set; } + public Ratings ratings { get; set; } + public DateTime releaseDate { get; set; } + public Currentrelease currentRelease { get; set; } + public Release[] releases { get; set; } + public object[] genres { get; set; } + public Medium[] media { get; set; } + public Image[] images { get; set; } + public Statistics statistics { get; set; } + public int id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/Artist.cs b/src/Ombi.Api.Lidarr/Models/Artist.cs new file mode 100644 index 000000000..bc6afc20e --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/Artist.cs @@ -0,0 +1,25 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + public class Artist + { + public string status { get; set; } + public bool ended { get; set; } + public string artistName { get; set; } + public string foreignArtistId { get; set; } + public int tadbId { get; set; } + public int discogsId { get; set; } + public object[] links { get; set; } + public object[] images { get; set; } + public int qualityProfileId { get; set; } + public int languageProfileId { get; set; } + public int metadataProfileId { get; set; } + public bool albumFolder { get; set; } + public bool monitored { get; set; } + public object[] genres { get; set; } + public object[] tags { get; set; } + public DateTime added { get; set; } + public Statistics statistics { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/ArtistAdd.cs b/src/Ombi.Api.Lidarr/Models/ArtistAdd.cs new file mode 100644 index 000000000..65aec3ac8 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/ArtistAdd.cs @@ -0,0 +1,49 @@ +using System; +using System.Net.Mime; + +namespace Ombi.Api.Lidarr.Models +{ + public class ArtistAdd + { + public string status { get; set; } + public bool ended { get; set; } + public string artistName { get; set; } + public string foreignArtistId { get; set; } + public int tadbId { get; set; } + public int discogsId { get; set; } + public string overview { get; set; } + public string disambiguation { get; set; } + public Link[] links { get; set; } + public Image[] images { get; set; } + public string remotePoster { get; set; } + public int qualityProfileId { get; set; } + public int languageProfileId { get; set; } + public int metadataProfileId { get; set; } + public bool albumFolder { get; set; } + public bool monitored { get; set; } + public string cleanName { get; set; } + public string sortName { get; set; } + public object[] tags { get; set; } + public DateTime added { get; set; } + public Ratings ratings { get; set; } + public Statistics statistics { get; set; } + public Addoptions addOptions { get; set; } + public string rootFolderPath { get; set; } + } + + public class Addoptions + { + /// + /// Future = 1 + /// Missing = 2 + /// Existing = 3 + /// First = 5 + /// Latest = 4 + /// None = 6 + /// + public int selectedOption { get; set; } + public bool monitored { get; set; } + public bool searchForMissingAlbums { get; set; } + public string[] AlbumsToMonitor { get; set; } // Uses the MusicBrainzAlbumId! + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/ArtistLookup.cs b/src/Ombi.Api.Lidarr/Models/ArtistLookup.cs new file mode 100644 index 000000000..aa454c0a0 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/ArtistLookup.cs @@ -0,0 +1,32 @@ +using System; +using System.Net.Mime; + +namespace Ombi.Api.Lidarr.Models +{ + public class ArtistLookup + { + public string status { get; set; } + public bool ended { get; set; } + public string artistName { get; set; } + public string foreignArtistId { get; set; } + public int tadbId { get; set; } + public int discogsId { get; set; } + public string overview { get; set; } + public string artistType { get; set; } + public string disambiguation { get; set; } + public Link[] links { get; set; } + public Image[] images { get; set; } + public string remotePoster { get; set; } + public int qualityProfileId { get; set; } + public int languageProfileId { get; set; } + public int metadataProfileId { get; set; } + public bool albumFolder { get; set; } + public bool monitored { get; set; } + public string cleanName { get; set; } + public string sortName { get; set; } + public object[] tags { get; set; } + public DateTime added { get; set; } + public Ratings ratings { get; set; } + public Statistics statistics { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/ArtistResult.cs b/src/Ombi.Api.Lidarr/Models/ArtistResult.cs new file mode 100644 index 000000000..32b3aaab5 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/ArtistResult.cs @@ -0,0 +1,93 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + + public class ArtistResult + { + public string status { get; set; } + public bool ended { get; set; } + public DateTime lastInfoSync { get; set; } + public string artistName { get; set; } + public string foreignArtistId { get; set; } + public int tadbId { get; set; } + public int discogsId { get; set; } + public string overview { get; set; } + public string artistType { get; set; } + public string disambiguation { get; set; } + public Link[] links { get; set; } + public Nextalbum nextAlbum { get; set; } + public Image[] images { get; set; } + public string path { get; set; } + public int qualityProfileId { get; set; } + public int languageProfileId { get; set; } + public int metadataProfileId { get; set; } + public bool albumFolder { get; set; } + public bool monitored { get; set; } + public object[] genres { get; set; } + public string cleanName { get; set; } + public string sortName { get; set; } + public object[] tags { get; set; } + public DateTime added { get; set; } + public Ratings ratings { get; set; } + public Statistics statistics { get; set; } + public int id { get; set; } + } + + public class Nextalbum + { + public string foreignAlbumId { get; set; } + public int artistId { get; set; } + public string title { get; set; } + public string disambiguation { get; set; } + public string cleanTitle { get; set; } + public DateTime releaseDate { get; set; } + public int profileId { get; set; } + public int duration { get; set; } + public bool monitored { get; set; } + public object[] images { get; set; } + public object[] genres { get; set; } + public Medium[] media { get; set; } + public DateTime lastInfoSync { get; set; } + public DateTime added { get; set; } + public string albumType { get; set; } + public object[] secondaryTypes { get; set; } + public Ratings ratings { get; set; } + public Release[] releases { get; set; } + public Currentrelease currentRelease { get; set; } + public int id { get; set; } + } + + public class Currentrelease + { + public string id { get; set; } + public string title { get; set; } + public DateTime releaseDate { get; set; } + public int trackCount { get; set; } + public int mediaCount { get; set; } + public string disambiguation { get; set; } + public string[] country { get; set; } + public string format { get; set; } + public string[] label { get; set; } + } + + public class Medium + { + public int number { get; set; } + public string name { get; set; } + public string format { get; set; } + } + + public class Release + { + public string id { get; set; } + public string title { get; set; } + public DateTime releaseDate { get; set; } + public int trackCount { get; set; } + public int mediaCount { get; set; } + public string disambiguation { get; set; } + public string[] country { get; set; } + public string format { get; set; } + public string[] label { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/CommandResult.cs b/src/Ombi.Api.Lidarr/Models/CommandResult.cs new file mode 100644 index 000000000..5271de91f --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/CommandResult.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + + public class CommandResult + { + public string name { get; set; } + public DateTime queued { get; set; } + public DateTime stateChangeTime { get; set; } + public bool sendUpdatesToClient { get; set; } + public string status { get; set; } + public int id { get; set; } + } +} diff --git a/src/Ombi.Api.Lidarr/Models/Image.cs b/src/Ombi.Api.Lidarr/Models/Image.cs new file mode 100644 index 000000000..172a13fe9 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/Image.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class Image + { + public string coverType { get; set; } + public string url { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs b/src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs new file mode 100644 index 000000000..f503fe33f --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class LanguageProfiles + { + public string name { get; set; } + public int id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/LidarrProfile.cs b/src/Ombi.Api.Lidarr/Models/LidarrProfile.cs new file mode 100644 index 000000000..19ebda5a6 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrProfile.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Ombi.Api.Lidarr.Models +{ + public class Quality + { + public int id { get; set; } + public string name { get; set; } + } + + public class Item + { + public Quality quality { get; set; } + public bool allowed { get; set; } + } + + public class LidarrProfile +{ + public string name { get; set; } + public List items { get; set; } + public int id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs b/src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs new file mode 100644 index 000000000..a3a252f04 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs @@ -0,0 +1,11 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class LidarrRootFolder + { + public string path { get; set; } + public long freeSpace { get; set; } + public object[] unmappedFolders { get; set; } + public int id { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/LidarrStatus.cs b/src/Ombi.Api.Lidarr/Models/LidarrStatus.cs new file mode 100644 index 000000000..27f6c1820 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrStatus.cs @@ -0,0 +1,31 @@ +using System; + +namespace Ombi.Api.Lidarr.Models +{ + public class LidarrStatus + { + public string version { get; set; } + public DateTime buildTime { get; set; } + public bool isDebug { get; set; } + public bool isProduction { get; set; } + public bool isAdmin { get; set; } + public bool isUserInteractive { get; set; } + public string startupPath { get; set; } + public string appData { get; set; } + public string osName { get; set; } + public string osVersion { get; set; } + public bool isMonoRuntime { get; set; } + public bool isMono { get; set; } + public bool isLinux { get; set; } + public bool isOsx { get; set; } + public bool isWindows { get; set; } + public string mode { get; set; } + public string branch { get; set; } + public string authentication { get; set; } + public string sqliteVersion { get; set; } + public int migrationVersion { get; set; } + public string urlBase { get; set; } + public string runtimeVersion { get; set; } + public string runtimeName { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/Link.cs b/src/Ombi.Api.Lidarr/Models/Link.cs new file mode 100644 index 000000000..492ac0426 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/Link.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class Link + { + public string url { get; set; } + public string name { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/MetadataProfile.cs b/src/Ombi.Api.Lidarr/Models/MetadataProfile.cs new file mode 100644 index 000000000..bda3333f1 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/MetadataProfile.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class MetadataProfile + { + public string name { get; set; } + public int id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/Ratings.cs b/src/Ombi.Api.Lidarr/Models/Ratings.cs new file mode 100644 index 000000000..f2aac4203 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/Ratings.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class Ratings + { + public int votes { get; set; } + public decimal value { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Models/Statistics.cs b/src/Ombi.Api.Lidarr/Models/Statistics.cs new file mode 100644 index 000000000..77c6b5217 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/Statistics.cs @@ -0,0 +1,12 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class Statistics + { + public int albumCount { get; set; } + public int trackFileCount { get; set; } + public int trackCount { get; set; } + public int totalTrackCount { get; set; } + public long sizeOnDisk { get; set; } + public decimal percentOfEpisodes { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj new file mode 100644 index 000000000..a3651df3c --- /dev/null +++ b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Ombi.Api.Mattermost/Models/MattermostClient.cs b/src/Ombi.Api.Mattermost/Models/MattermostClient.cs index 96d3e33f4..ad04947b1 100644 --- a/src/Ombi.Api.Mattermost/Models/MattermostClient.cs +++ b/src/Ombi.Api.Mattermost/Models/MattermostClient.cs @@ -120,8 +120,10 @@ namespace Ombi.Api.Mattermost.Models var attIndex = outMessages[msgCount].Attachments.Count - 1; //Get the text lines - lines = att.Text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); - + if (!String.IsNullOrEmpty(att.Text)) + { + lines = att.Text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + } foreach (var line in lines) { //Get the total length of all attachments on the current outgoing message @@ -153,8 +155,9 @@ namespace Ombi.Api.Mattermost.Models foreach (var msg in outMessages) { - var request = new Request("", _webhookUrl.ToString(), HttpMethod.Post); + var request = new Request(_webhookUrl.ToString(), "", HttpMethod.Post); request.AddJsonBody(msg); + request.AddHeader("Host", _webhookUrl.Host); await api.Request(request); } } diff --git a/src/Ombi.Api.Mattermost/Models/MattermostMessage.cs b/src/Ombi.Api.Mattermost/Models/MattermostMessage.cs index d8fe52b09..83171627a 100644 --- a/src/Ombi.Api.Mattermost/Models/MattermostMessage.cs +++ b/src/Ombi.Api.Mattermost/Models/MattermostMessage.cs @@ -33,7 +33,7 @@ namespace Ombi.Api.Mattermost.Models /// Bot/User Icon /// [JsonProperty(PropertyName = "icon_url")] - public Uri IconUrl { get; set; } + public string IconUrl { get; set; } /// /// Message body. Supports Markdown @@ -142,7 +142,7 @@ namespace Ombi.Api.Mattermost.Models /// Large images are resized to a maximum width of 400px or a maximum height of 300px, while still maintaining the original aspect ratio. /// [JsonProperty(PropertyName = "image_url")] - public Uri ImageUrl { get; set; } + public string ImageUrl { get; set; } /// /// An optional URL to an image file(GIF, JPEG, PNG, or BMP) that is displayed as a 75x75 pixel thumbnail on the right side of an attachment. diff --git a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj index ca15bae05..83318be7b 100644 --- a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj +++ b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Notifications/IOneSignalApi.cs b/src/Ombi.Api.Notifications/IOneSignalApi.cs index 2e3ef106e..6de64d11e 100644 --- a/src/Ombi.Api.Notifications/IOneSignalApi.cs +++ b/src/Ombi.Api.Notifications/IOneSignalApi.cs @@ -6,6 +6,6 @@ namespace Ombi.Api.Notifications { public interface IOneSignalApi { - Task PushNotification(List playerIds, string message); + Task PushNotification(List playerIds, string message, bool isAdminNotification, int requestId, int requestType); } } \ No newline at end of file diff --git a/src/Ombi.Api.Notifications/Models/OneSignalNotificationBody.cs b/src/Ombi.Api.Notifications/Models/OneSignalNotificationBody.cs index 6c024fa67..e65222bd5 100644 --- a/src/Ombi.Api.Notifications/Models/OneSignalNotificationBody.cs +++ b/src/Ombi.Api.Notifications/Models/OneSignalNotificationBody.cs @@ -4,18 +4,22 @@ { public string app_id { get; set; } public string[] include_player_ids { get; set; } - public Data data { get; set; } + public object data { get; set; } + public Button[] buttons { get; set; } public Contents contents { get; set; } } - public class Data - { - public string foo { get; set; } - } public class Contents { public string en { get; set; } } + public class Button + { + public string id { get; set; } + public string text { get; set; } + //public string icon { get; set; } + } + } \ No newline at end of file diff --git a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj index a3651df3c..7b890e2dd 100644 --- a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj +++ b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj @@ -4,6 +4,10 @@ netstandard2.0 + + + + diff --git a/src/Ombi.Api.Notifications/OneSignalApi.cs b/src/Ombi.Api.Notifications/OneSignalApi.cs index d4760bb09..8d5fc04a9 100644 --- a/src/Ombi.Api.Notifications/OneSignalApi.cs +++ b/src/Ombi.Api.Notifications/OneSignalApi.cs @@ -20,7 +20,7 @@ namespace Ombi.Api.Notifications private readonly IApplicationConfigRepository _appConfig; private const string ApiUrl = "https://onesignal.com/api/v1/notifications"; - public async Task PushNotification(List playerIds, string message) + public async Task PushNotification(List playerIds, string message, bool isAdminNotification, int requestId, int requestType) { if (!playerIds.Any()) { @@ -39,6 +39,17 @@ namespace Ombi.Api.Notifications include_player_ids = playerIds.ToArray() }; + if (isAdminNotification) + { + // Add the action buttons + body.data = new { requestid = requestId, requestType = requestType}; + body.buttons = new[] + { + new Button {id = "approve", text = "Approve Request"}, + new Button {id = "deny", text = "Deny Request"}, + }; + } + request.AddJsonBody(body); var result = await _api.Request(request); diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs index e11d6d914..343eaa2d7 100644 --- a/src/Ombi.Api.Plex/IPlexApi.cs +++ b/src/Ombi.Api.Plex/IPlexApi.cs @@ -1,6 +1,8 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models.Friends; +using Ombi.Api.Plex.Models.OAuth; using Ombi.Api.Plex.Models.Server; using Ombi.Api.Plex.Models.Status; @@ -9,6 +11,7 @@ namespace Ombi.Api.Plex public interface IPlexApi { Task GetStatus(string authToken, string uri); + Task GetLibrariesForMachineId(string authToken, string machineId); Task SignIn(UserRequest user); Task GetServer(string authToken); Task GetLibrarySections(string authToken, string plexFullHost); @@ -19,5 +22,9 @@ namespace Ombi.Api.Plex Task GetAllEpisodes(string authToken, string host, string section, int start, int retCount); Task GetUsers(string authToken); Task GetAccount(string authToken); + Task GetRecentlyAdded(string authToken, string uri, string sectionId); + Task GetPin(int pinId); + Task GetOAuthUrl(int pinId, string code, string applicationUrl); + Task AddUser(string emailAddress, string serverId, string authToken, int[] libs); } } \ No newline at end of file diff --git a/src/Ombi.Api.Plex/Models/Metadata.cs b/src/Ombi.Api.Plex/Models/Metadata.cs index 28bdf002d..cda6ce68b 100644 --- a/src/Ombi.Api.Plex/Models/Metadata.cs +++ b/src/Ombi.Api.Plex/Models/Metadata.cs @@ -11,20 +11,20 @@ namespace Ombi.Api.Plex.Models public string summary { get; set; } public int index { get; set; } public float rating { get; set; } - public int viewCount { get; set; } - public int lastViewedAt { get; set; } + //public int viewCount { get; set; } + //public int lastViewedAt { get; set; } public int year { get; set; } public string thumb { get; set; } public string art { get; set; } public string banner { get; set; } public string theme { get; set; } - public string duration { get; set; } - public string originallyAvailableAt { get; set; } + //public string duration { get; set; } + //public string originallyAvailableAt { get; set; } public int leafCount { get; set; } public int viewedLeafCount { get; set; } public int childCount { get; set; } - public long addedAt { get; set; } - public int updatedAt { get; set; } + //public long addedAt { get; set; } + //public int updatedAt { get; set; } public Genre[] Genre { get; set; } //public Role[] Role { get; set; } public string primaryExtraKey { get; set; } diff --git a/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs b/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs new file mode 100644 index 000000000..e65cd91d4 --- /dev/null +++ b/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs @@ -0,0 +1,27 @@ +using System; + +namespace Ombi.Api.Plex.Models.OAuth +{ + public class OAuthPin + { + public int id { get; set; } + public string code { get; set; } + public bool trusted { get; set; } + public string clientIdentifier { get; set; } + public Location location { get; set; } + public int expiresIn { get; set; } + public DateTime createdAt { get; set; } + public DateTime expiresAt { get; set; } + public string authToken { get; set; } + } + + public class Location + { + public string code { get; set; } + public string country { get; set; } + public string city { get; set; } + public string subdivisions { get; set; } + public string coordinates { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.Api.Plex/Models/PlexAdd.cs b/src/Ombi.Api.Plex/Models/PlexAdd.cs new file mode 100644 index 000000000..fb0a550d0 --- /dev/null +++ b/src/Ombi.Api.Plex/Models/PlexAdd.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Ombi.Api.Plex.Models +{ + [XmlRoot(ElementName = "Section")] + public class Section + { + [XmlAttribute(AttributeName = "id")] + public string Id { get; set; } + [XmlAttribute(AttributeName = "key")] + public string Key { get; set; } + [XmlAttribute(AttributeName = "title")] + public string Title { get; set; } + [XmlAttribute(AttributeName = "type")] + public string Type { get; set; } + [XmlAttribute(AttributeName = "shared")] + public string Shared { get; set; } + } + + [XmlRoot(ElementName = "SharedServer")] + public class SharedServer + { + [XmlElement(ElementName = "Section")] + public List
Section { get; set; } + [XmlAttribute(AttributeName = "id")] + public string Id { get; set; } + [XmlAttribute(AttributeName = "username")] + public string Username { get; set; } + [XmlAttribute(AttributeName = "email")] + public string Email { get; set; } + [XmlAttribute(AttributeName = "userID")] + public string UserID { get; set; } + [XmlAttribute(AttributeName = "accessToken")] + public string AccessToken { get; set; } + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } + [XmlAttribute(AttributeName = "acceptedAt")] + public string AcceptedAt { get; set; } + [XmlAttribute(AttributeName = "invitedAt")] + public string InvitedAt { get; set; } + [XmlAttribute(AttributeName = "allowSync")] + public string AllowSync { get; set; } + [XmlAttribute(AttributeName = "allowCameraUpload")] + public string AllowCameraUpload { get; set; } + [XmlAttribute(AttributeName = "allowChannels")] + public string AllowChannels { get; set; } + [XmlAttribute(AttributeName = "allowTuners")] + public string AllowTuners { get; set; } + [XmlAttribute(AttributeName = "owned")] + public string Owned { get; set; } + } + + [XmlRoot(ElementName = "MediaContainer")] + public class PlexAdd + { + [XmlElement(ElementName = "SharedServer")] + public SharedServer SharedServer { get; set; } + [XmlAttribute(AttributeName = "friendlyName")] + public string FriendlyName { get; set; } + [XmlAttribute(AttributeName = "identifier")] + public string Identifier { get; set; } + [XmlAttribute(AttributeName = "machineIdentifier")] + public string MachineIdentifier { get; set; } + [XmlAttribute(AttributeName = "size")] + public string Size { get; set; } + } + + [XmlRoot(ElementName = "Response")] + public class AddUserError + { + [XmlAttribute(AttributeName = "code")] + public string Code { get; set; } + [XmlAttribute(AttributeName = "status")] + public string Status { get; set; } + } + + public class PlexAddWrapper + { + public PlexAdd Add { get; set; } + public AddUserError Error { get; set; } + public bool HasError => Error != null; + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Plex/Models/PlexLibrariesForMachineId.cs b/src/Ombi.Api.Plex/Models/PlexLibrariesForMachineId.cs new file mode 100644 index 000000000..17ac59b81 --- /dev/null +++ b/src/Ombi.Api.Plex/Models/PlexLibrariesForMachineId.cs @@ -0,0 +1,66 @@ +namespace Ombi.Api.Plex.Models +{ + + using System; + using System.Xml.Serialization; + using System.Collections.Generic; + + [XmlRoot(ElementName = "Section")] + public class SectionLite + { + [XmlAttribute(AttributeName = "id")] + public string Id { get; set; } + [XmlAttribute(AttributeName = "key")] + public string Key { get; set; } + [XmlAttribute(AttributeName = "type")] + public string Type { get; set; } + [XmlAttribute(AttributeName = "title")] + public string Title { get; set; } + } + + [XmlRoot(ElementName = "Server")] + public class ServerLib + { + [XmlElement(ElementName = "Section")] + public List Section { get; set; } + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } + [XmlAttribute(AttributeName = "address")] + public string Address { get; set; } + [XmlAttribute(AttributeName = "port")] + public string Port { get; set; } + [XmlAttribute(AttributeName = "version")] + public string Version { get; set; } + [XmlAttribute(AttributeName = "scheme")] + public string Scheme { get; set; } + [XmlAttribute(AttributeName = "host")] + public string Host { get; set; } + [XmlAttribute(AttributeName = "localAddresses")] + public string LocalAddresses { get; set; } + [XmlAttribute(AttributeName = "machineIdentifier")] + public string MachineIdentifier { get; set; } + [XmlAttribute(AttributeName = "createdAt")] + public string CreatedAt { get; set; } + [XmlAttribute(AttributeName = "updatedAt")] + public string UpdatedAt { get; set; } + [XmlAttribute(AttributeName = "owned")] + public string Owned { get; set; } + [XmlAttribute(AttributeName = "synced")] + public string Synced { get; set; } + } + + [XmlRoot(ElementName = "MediaContainer")] + public class PlexLibrariesForMachineId + { + [XmlElement(ElementName = "Server")] + public ServerLib Server { get; set; } + [XmlAttribute(AttributeName = "friendlyName")] + public string FriendlyName { get; set; } + [XmlAttribute(AttributeName = "identifier")] + public string Identifier { get; set; } + [XmlAttribute(AttributeName = "machineIdentifier")] + public string MachineIdentifier { get; set; } + [XmlAttribute(AttributeName = "size")] + public string Size { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index b5c6b958d..f0808622f 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -1,20 +1,66 @@ -using System.Net.Http; +using System; +using System.Net.Http; +using System.Reflection; using System.Threading.Tasks; using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models.Friends; +using Ombi.Api.Plex.Models.OAuth; using Ombi.Api.Plex.Models.Server; using Ombi.Api.Plex.Models.Status; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; namespace Ombi.Api.Plex { public class PlexApi : IPlexApi { - public PlexApi(IApi api) + public PlexApi(IApi api, ISettingsService settings, ISettingsService p) { Api = api; + _custom = settings; + _plexSettings = p; } private IApi Api { get; } + private readonly ISettingsService _custom; + private readonly ISettingsService _plexSettings; + + private string _app; + private string ApplicationName + { + get + { + if (string.IsNullOrEmpty(_app)) + { + var settings = _custom.GetSettings(); + if (settings.ApplicationName.IsNullOrEmpty()) + { + _app = "Ombi"; + } + else + { + // Check for non-ascii characters (New .Net Core HTTPLib does not allow this) + var chars = settings.ApplicationName.ToCharArray(); + var hasNonAscii = false; + foreach (var c in chars) + { + if (c > 128) + { + hasNonAscii = true; + } + } + + _app = hasNonAscii ? "Ombi" : settings.ApplicationName; + } + + return _app; + } + + return _app; + } + } private const string SignInUri = "https://plex.tv/users/sign_in.json"; private const string FriendsUri = "https://plex.tv/pms/friends/all"; @@ -36,7 +82,7 @@ namespace Ombi.Api.Plex }; var request = new Request(SignInUri, string.Empty, HttpMethod.Post); - AddHeaders(request); + await AddHeaders(request); request.AddJsonBody(userModel); var obj = await Api.Request(request); @@ -47,14 +93,14 @@ namespace Ombi.Api.Plex public async Task GetStatus(string authToken, string uri) { var request = new Request(uri, string.Empty, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetAccount(string authToken) { var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -62,7 +108,7 @@ namespace Ombi.Api.Plex { var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -70,17 +116,24 @@ namespace Ombi.Api.Plex public async Task GetLibrarySections(string authToken, string plexFullHost) { var request = new Request("library/sections", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetLibrary(string authToken, string plexFullHost, string libraryId) { var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } + public async Task GetLibrariesForMachineId(string authToken, string machineId) + { + var request = new Request("", $"https://plex.tv/api/servers/{machineId}", HttpMethod.Get, ContentType.Xml); + await AddHeaders(request, authToken); + return await Api.Request(request); + } + /// // 192.168.1.69:32400/library/metadata/3662/allLeaves // The metadata ratingkey should be in the Cache @@ -95,21 +148,21 @@ namespace Ombi.Api.Plex public async Task GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey) { var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetMetadata(string authToken, string plexFullHost, int itemId) { var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetSeasons(string authToken, string plexFullHost, int ratingKey) { var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -128,9 +181,9 @@ namespace Ombi.Api.Plex request.AddQueryString("type", "4"); AddLimitHeaders(request, start, retCount); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); - return await Api.Request(request); + return await Api.Request(request); } /// @@ -141,32 +194,110 @@ namespace Ombi.Api.Plex /// public async Task GetUsers(string authToken) { - var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml); - AddHeaders(request, authToken); + var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml); + await AddHeaders(request, authToken); return await Api.Request(request); } + public async Task GetRecentlyAdded(string authToken, string uri, string sectionId) + { + var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get); + await AddHeaders(request, authToken); + AddLimitHeaders(request, 0, 50); + + return await Api.Request(request); + } + + public async Task GetPin(int pinId) + { + var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get); + await AddHeaders(request); + + return await Api.Request(request); + } + + public async Task GetOAuthUrl(int pinId, string code, string applicationUrl) + { + var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); + await AddHeaders(request); + + request.AddQueryString("pinID", pinId.ToString()); + request.AddQueryString("code", code); + request.AddQueryString("context[device][product]", ApplicationName); + request.AddQueryString("context[device][environment]", "bundled"); + request.AddQueryString("context[device][layout]", "desktop"); + request.AddQueryString("context[device][platform]", "Web"); + request.AddQueryString("context[device][device]", "Ombi (Web)"); + + var s = await GetSettings(); + await CheckInstallId(s); + request.AddQueryString("clientID", s.InstallId.ToString("N")); + + if (request.FullUri.Fragment.Equals("#")) + { + var uri = request.FullUri.ToString(); + var withoutEnd = uri.Remove(uri.Length - 1, 1); + var startOfQueryLocation = withoutEnd.IndexOf('?'); + var better = withoutEnd.Insert(startOfQueryLocation, "#"); + request.FullUri = new Uri(better); + } + + return request.FullUri; + } + + public async Task AddUser(string emailAddress, string serverId, string authToken, int[] libs) + { + var request = new Request(string.Empty, $"https://plex.tv/api/servers/{serverId}/shared_servers", HttpMethod.Post, ContentType.Xml); + await AddHeaders(request, authToken); + request.AddJsonBody(new + { + server_id = serverId, + shared_server = new + { + library_section_ids = libs.Length > 0 ? libs : new int[]{}, + invited_email = emailAddress + }, + sharing_settings = new { } + }); + var result = await Api.RequestContent(request); + try + { + var add = Api.DeserializeXml(result); + return new PlexAddWrapper{Add = add}; + } + catch (InvalidOperationException) + { + var error = Api.DeserializeXml(result); + return new PlexAddWrapper{Error = error}; + } + } + + /// /// Adds the required headers and also the authorization header /// /// /// - private void AddHeaders(Request request, string authToken) + private async Task AddHeaders(Request request, string authToken) { request.AddHeader("X-Plex-Token", authToken); - AddHeaders(request); + await AddHeaders(request); } /// /// Adds the main required headers to the Plex Request /// /// - private void AddHeaders(Request request) + private async Task AddHeaders(Request request) { - request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); - request.AddHeader("X-Plex-Product", "Ombi"); + var s = await GetSettings(); + await CheckInstallId(s); + request.AddHeader("X-Plex-Client-Identifier", s.InstallId.ToString("N")); + request.AddHeader("X-Plex-Product", ApplicationName); request.AddHeader("X-Plex-Version", "3"); + request.AddHeader("X-Plex-Device", "Ombi (Web)"); + request.AddHeader("X-Plex-Platform", "Web"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); request.AddHeader("Accept", "application/json"); } @@ -176,5 +307,19 @@ namespace Ombi.Api.Plex request.AddHeader("X-Plex-Container-Start", from.ToString()); request.AddHeader("X-Plex-Container-Size", to.ToString()); } + private async Task CheckInstallId(PlexSettings s) + { + if (s.InstallId == null || s.InstallId == Guid.Empty) + { + s.InstallId = Guid.NewGuid(); + await _plexSettings.SaveSettingsAsync(s); + } + } + + private PlexSettings _settings; + private async Task GetSettings() + { + return _settings ?? (_settings = await _plexSettings.GetSettingsAsync()); + } } } diff --git a/src/Ombi.Api.Pushover/IPushoverApi.cs b/src/Ombi.Api.Pushover/IPushoverApi.cs index 42e8e9060..554a15b60 100644 --- a/src/Ombi.Api.Pushover/IPushoverApi.cs +++ b/src/Ombi.Api.Pushover/IPushoverApi.cs @@ -5,6 +5,6 @@ namespace Ombi.Api.Pushover { public interface IPushoverApi { - Task PushAsync(string accessToken, string message, string userToken); + Task PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound); } } \ No newline at end of file diff --git a/src/Ombi.Api.Pushover/PushoverApi.cs b/src/Ombi.Api.Pushover/PushoverApi.cs index 96f4d2e95..9f91bc7ca 100644 --- a/src/Ombi.Api.Pushover/PushoverApi.cs +++ b/src/Ombi.Api.Pushover/PushoverApi.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Ombi.Api.Pushover.Models; @@ -15,9 +16,13 @@ namespace Ombi.Api.Pushover private readonly IApi _api; private const string PushoverEndpoint = "https://api.pushover.net/1"; - public async Task PushAsync(string accessToken, string message, string userToken) + public async Task PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound) { - var request = new Request($"messages.json?token={accessToken}&user={userToken}&message={message}", PushoverEndpoint, HttpMethod.Post); + if (message.Contains("'")) + { + message = message.Replace("'", "'"); + } + var request = new Request($"messages.json?token={accessToken}&user={userToken}&priority={priority}&sound={sound}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post); var result = await _api.Request(request); return result; diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index 1031ffd4a..0c615f301 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Radarr/RadarrApi.cs b/src/Ombi.Api.Radarr/RadarrApi.cs index 1f897b60b..fd4deb140 100644 --- a/src/Ombi.Api.Radarr/RadarrApi.cs +++ b/src/Ombi.Api.Radarr/RadarrApi.cs @@ -79,7 +79,7 @@ namespace Ombi.Api.Radarr tmdbId = tmdbId, qualityProfileId = qualityId, rootFolderPath = rootPath, - titleSlug = title, + titleSlug = title + year, monitored = true, year = year, minimumAvailability = minimumAvailability diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 8e62012d8..8cbddd874 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api.Sonarr/Models/Episode.cs b/src/Ombi.Api.Sonarr/Models/Episode.cs index c17f5486c..b01e6fd8c 100644 --- a/src/Ombi.Api.Sonarr/Models/Episode.cs +++ b/src/Ombi.Api.Sonarr/Models/Episode.cs @@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models { public class Episode { + public Episode() + { + + } + + public Episode(Episode ep) + { + seriesId = ep.seriesId; + episodeFileId = ep.episodeFileId; + seasonNumber = ep.seasonNumber; + episodeNumber = ep.episodeNumber; + title = ep.title; + airDate = ep.airDate; + airDateUtc = ep.airDateUtc; + overview = ep.overview; + hasFile = ep.hasFile; + monitored = ep.monitored; + unverifiedSceneNumbering = ep.unverifiedSceneNumbering; + id = ep.id; + absoluteEpisodeNumber = ep.absoluteEpisodeNumber; + sceneAbsoluteEpisodeNumber = ep.sceneAbsoluteEpisodeNumber; + sceneEpisodeNumber = ep.sceneEpisodeNumber; + sceneSeasonNumber = ep.sceneSeasonNumber; + } public int seriesId { get; set; } public int episodeFileId { get; set; } public int seasonNumber { get; set; } @@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models public class Episodefile { + public Episodefile() + { + + } + + public Episodefile(Episodefile e) + { + seriesId = e.seriesId; + seasonNumber = e.seasonNumber; + relativePath = e.relativePath; + path = e.path; + size = e.size; + dateAdded = e.dateAdded; + sceneName = e.sceneName; + quality = new EpisodeQuality(e.quality); + qualityCutoffNotMet = e.qualityCutoffNotMet; + id = e.id; + } public int seriesId { get; set; } public int seasonNumber { get; set; } public string relativePath { get; set; } @@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models public class EpisodeQuality { + public EpisodeQuality() + { + + } + + public EpisodeQuality(EpisodeQuality e) + { + quality = new Quality(e.quality); + revision = new Revision(e.revision); + } public Quality quality { get; set; } public Revision revision { get; set; } } public class Revision { + public Revision() + { + + } + + public Revision(Revision r) + { + version = r.version; + real = r.real; + } public int version { get; set; } public int real { get; set; } } diff --git a/src/Ombi.Api.Sonarr/Models/NewSeries.cs b/src/Ombi.Api.Sonarr/Models/NewSeries.cs index 4d2c17308..ef18baddb 100644 --- a/src/Ombi.Api.Sonarr/Models/NewSeries.cs +++ b/src/Ombi.Api.Sonarr/Models/NewSeries.cs @@ -23,6 +23,7 @@ namespace Ombi.Api.Sonarr.Models public string cleanTitle { get; set; } public string imdbId { get; set; } public string titleSlug { get; set; } + public string seriesType { get; set; } public int id { get; set; } public List images { get; set; } diff --git a/src/Ombi.Api.Sonarr/Models/Quality.cs b/src/Ombi.Api.Sonarr/Models/Quality.cs index 76a1c92d8..9989a9c3e 100644 --- a/src/Ombi.Api.Sonarr/Models/Quality.cs +++ b/src/Ombi.Api.Sonarr/Models/Quality.cs @@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models { public class Quality { + public Quality() + { + + } + + public Quality(Quality q) + { + id = q.id; + name = q.name; + } public int id { get; set; } public string name { get; set; } } diff --git a/src/Ombi.Api/Api.cs b/src/Ombi.Api/Api.cs index 98fff5e0c..19dab7530 100644 --- a/src/Ombi.Api/Api.cs +++ b/src/Ombi.Api/Api.cs @@ -39,7 +39,11 @@ namespace Ombi.Api if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } + if (request.Retry) { @@ -76,15 +80,20 @@ namespace Ombi.Api else { // XML - XmlSerializer serializer = new XmlSerializer(typeof(T)); - StringReader reader = new StringReader(receivedString); - var value = (T)serializer.Deserialize(reader); - return value; + return DeserializeXml(receivedString); } } } + public T DeserializeXml(string receivedString) + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + StringReader reader = new StringReader(receivedString); + var value = (T) serializer.Deserialize(reader); + return value; + } + public async Task RequestContent(Request request) { using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri)) @@ -94,7 +103,10 @@ namespace Ombi.Api var httpResponseMessage = await _client.SendAsync(httpRequestMessage); if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } } // do something with the response var data = httpResponseMessage.Content; @@ -112,7 +124,10 @@ namespace Ombi.Api var httpResponseMessage = await _client.SendAsync(httpRequestMessage); if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } } } } diff --git a/src/Ombi.Api/IApi.cs b/src/Ombi.Api/IApi.cs index 2b7f71bb8..e573d2d07 100644 --- a/src/Ombi.Api/IApi.cs +++ b/src/Ombi.Api/IApi.cs @@ -7,5 +7,6 @@ namespace Ombi.Api Task Request(Request request); Task Request(Request request); Task RequestContent(Request request); + T DeserializeXml(string receivedString); } } \ No newline at end of file diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 325f316b8..a37c128fb 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Api/Request.cs b/src/Ombi.Api/Request.cs index e4120ed9c..fd888d0d2 100644 --- a/src/Ombi.Api/Request.cs +++ b/src/Ombi.Api/Request.cs @@ -10,7 +10,7 @@ namespace Ombi.Api { public Request() { - + } public Request(string endpoint, string baseUrl, HttpMethod http, ContentType contentType = ContentType.Json) @@ -25,9 +25,10 @@ namespace Ombi.Api public string Endpoint { get; } public string BaseUrl { get; } public HttpMethod HttpMethod { get; } - + public bool IgnoreErrors { get; set; } public bool Retry { get; set; } public List StatusCodeToRetry { get; set; } = new List(); + public bool IgnoreBaseUrlAppend { get; set; } public Action OnBeforeDeserialization { get; set; } @@ -38,7 +39,7 @@ namespace Ombi.Api var sb = new StringBuilder(); if (!string.IsNullOrEmpty(BaseUrl)) { - sb.Append(!BaseUrl.EndsWith("/") ? string.Format("{0}/", BaseUrl) : BaseUrl); + sb.Append(!BaseUrl.EndsWith("/") && !IgnoreBaseUrlAppend ? string.Format("{0}/", BaseUrl) : BaseUrl); } sb.Append(Endpoint.StartsWith("/") ? Endpoint.Remove(0, 1) : Endpoint); return sb.ToString(); @@ -105,10 +106,10 @@ namespace Ombi.Api hasQuery = true; startingTag = builder.Query.Contains("?") ? "&" : "?"; } - builder.Query = hasQuery ? $"{builder.Query}{startingTag}{key}={value}" : $"{startingTag}{key}={value}"; + _modified = builder.Uri; } diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index 2aa71c1e2..30de4b6f0 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,15 +1,15 @@  - netcoreapp2.0 + netcoreapp2.1 - - - - - + + + + + diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs index a7f20ac40..99ff5b6bd 100644 --- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs @@ -25,11 +25,14 @@ namespace Ombi.Core.Tests.Rule.Search [Test] public async Task Movie_ShouldBe_Available_WhenFoundInEmby() { - ContextMock.Setup(x => x.Get(It.IsAny())).ReturnsAsync(new EmbyContent + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new EmbyContent { ProviderId = "123" }); - var search = new SearchMovieViewModel(); + var search = new SearchMovieViewModel() + { + TheMovieDbId = "123", + }; var result = await Rule.Execute(search); Assert.True(result.Success); @@ -39,7 +42,7 @@ namespace Ombi.Core.Tests.Rule.Search [Test] public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInEmby() { - ContextMock.Setup(x => x.Get(It.IsAny())).Returns(Task.FromResult(default(EmbyContent))); + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).Returns(Task.FromResult(default(EmbyContent))); var search = new SearchMovieViewModel(); var result = await Rule.Execute(search); diff --git a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs index a706472dd..e32c8e996 100644 --- a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs @@ -19,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search MovieMock = new Mock(); TvMock = new Mock(); - Rule = new ExistingRule(MovieMock.Object, TvMock.Object); + MusicMock = new Mock(); + Rule = new ExistingRule(MovieMock.Object, TvMock.Object, MusicMock.Object); } private ExistingRule Rule { get; set; } private Mock MovieMock { get; set; } private Mock TvMock { get; set; } + private Mock MusicMock { get; set; } [Test] diff --git a/src/Ombi.Core.Tests/StringHelperTests.cs b/src/Ombi.Core.Tests/StringHelperTests.cs new file mode 100644 index 000000000..c1b95fcd7 --- /dev/null +++ b/src/Ombi.Core.Tests/StringHelperTests.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +using NUnit.Framework; +using Ombi.Helpers; + +namespace Ombi.Core.Tests +{ + [TestFixture] + public class StringHelperTests + { + [TestCaseSource(nameof(StripCharsData))] + public string StripCharacters(string str, char[] chars) + { + return str.StripCharacters(chars); + } + + private static IEnumerable StripCharsData + { + get + { + yield return new TestCaseData("this!is^a*string",new []{'!','^','*'}).Returns("thisisastring").SetName("Basic Strip Multipe Chars"); + yield return new TestCaseData("What is this madness'",new []{'\'','^','*'}).Returns("What is this madness").SetName("Basic Strip Multipe Chars"); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs new file mode 100644 index 000000000..426037bb7 --- /dev/null +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading.Tasks; +using Ombi.Api.Plex; +using Ombi.Api.Plex.Models; +using Ombi.Api.Plex.Models.OAuth; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; + +namespace Ombi.Core.Authentication +{ + public class PlexOAuthManager : IPlexOAuthManager + { + public PlexOAuthManager(IPlexApi api, ISettingsService settings) + { + _api = api; + _customizationSettingsService = settings; + } + + private readonly IPlexApi _api; + private readonly ISettingsService _customizationSettingsService; + + public async Task GetAccessTokenFromPin(int pinId) + { + var pin = await _api.GetPin(pinId); + if (pin.expiresAt < DateTime.UtcNow) + { + return string.Empty; + } + + return pin.authToken; + } + + public async Task GetAccount(string accessToken) + { + return await _api.GetAccount(accessToken); + } + + public async Task GetOAuthUrl(int pinId, string code, string websiteAddress = null) + { + var settings = await _customizationSettingsService.GetSettingsAsync(); + var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl); + + return url; + } + + public async Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress) + { + var url = await _api.GetOAuthUrl(pinId, code, websiteAddress); + return url; + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index 552b2ac38..cb0047b96 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -9,13 +9,12 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Entities.Requests; -using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; -using Ombi.Store.Entities; -using Microsoft.AspNetCore.Identity; using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { @@ -26,18 +25,21 @@ namespace Ombi.Core.Engine private Dictionary _dbTv; protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService, - IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService ombiSettings) : base(identity, um, rules) + IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService ombiSettings, IRepository sub) : base(identity, um, rules) { RequestService = requestService; Cache = cache; OmbiSettings = ombiSettings; + _subscriptionRepository = sub; } protected IRequestServiceMain RequestService { get; } protected IMovieRequestRepository MovieRepository => RequestService.MovieRequestService; protected ITvRequestRepository TvRepository => RequestService.TvRequestService; + protected IMusicRequestRepository MusicRepository => RequestService.MusicRequestRepository; protected readonly ICacheService Cache; protected readonly ISettingsService OmbiSettings; + protected readonly IRepository _subscriptionRepository; protected async Task> GetMovieRequests() { @@ -78,7 +80,7 @@ namespace Ombi.Core.Engine var pendingTv = 0; var approvedTv = 0; - var availableTv = 0; + var availableTv = 0; foreach (var tv in tvQuery) { foreach (var child in tv.ChildRequests) @@ -108,21 +110,51 @@ namespace Ombi.Core.Engine protected async Task HideFromOtherUsers() { - if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser)) + var user = await GetUser(); + if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser) || user.IsSystemUser) { - return new HideResult(); + return new HideResult + { + UserId = user.Id + }; } var settings = await Cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await OmbiSettings.GetSettingsAsync()); var result = new HideResult { - Hide = settings.HideRequestsUsers + Hide = settings.HideRequestsUsers, + UserId = user.Id + }; + return result; + } + + public async Task SubscribeToRequest(int requestId, RequestType type) + { + var user = await GetUser(); + var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x => + x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type); + if (existingSub != null) + { + return; + } + var sub = new RequestSubscription + { + UserId = user.Id, + RequestId = requestId, + RequestType = type }; - if (settings.HideRequestsUsers) + + await _subscriptionRepository.Add(sub); + } + + public async Task UnSubscribeRequest(int requestId, RequestType type) + { + var user = await GetUser(); + var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x => + x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type); + if (existingSub != null) { - var user = await GetUser(); - result.UserId = user.Id; + await _subscriptionRepository.Delete(existingSub); } - return result; } public class HideResult diff --git a/src/Ombi.Core/Engine/IMusicRequestEngine.cs b/src/Ombi.Core/Engine/IMusicRequestEngine.cs new file mode 100644 index 000000000..5caba8a34 --- /dev/null +++ b/src/Ombi.Core/Engine/IMusicRequestEngine.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Core.Models; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.UI; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Core.Engine +{ + public interface IMusicRequestEngine + { + TaskApproveAlbum(AlbumRequest request); + Task ApproveAlbumById(int requestId); + Task DenyAlbumById(int modelId); + Task> GetRequests(); + Task> GetRequests(int count, int position, OrderFilterModel orderFilter); + Task GetTotal(); + Task MarkAvailable(int modelId); + Task MarkUnavailable(int modelId); + Task RemoveAlbumRequest(int requestId); + Task RequestAlbum(MusicAlbumRequestViewModel model); + Task> SearchAlbumRequest(string search); + Task UserHasRequest(string userId); + Task GetRemainingRequests(OmbiUser user = null); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs index e608ffebb..26bc5969c 100644 --- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -32,14 +32,7 @@ namespace Ombi.Core.Engine.Interfaces private OmbiUser _user; protected async Task GetUser() { - if (IsApiUser) - { - return new OmbiUser - { - UserName = Username, - }; - } - return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username)); + return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName.Equals(Username, StringComparison.CurrentCultureIgnoreCase))); } protected async Task UserAlias() @@ -49,10 +42,6 @@ namespace Ombi.Core.Engine.Interfaces protected async Task IsInRole(string roleName) { - if (IsApiUser && roleName != OmbiRoles.Disabled) - { - return true; - } return await UserManager.IsInRoleAsync(await GetUser(), roleName); } @@ -72,7 +61,5 @@ namespace Ombi.Core.Engine.Interfaces var ruleResults = await Rules.StartSpecificRules(model, rule); return ruleResults; } - - private bool IsApiUser => Username.Equals("Api", StringComparison.CurrentCultureIgnoreCase); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index bfeb4fbe0..152a1d923 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -17,6 +17,5 @@ namespace Ombi.Core.Engine.Interfaces Task ApproveMovie(MovieRequests request); Task ApproveMovieById(int requestId); Task DenyMovieById(int modelId); - Task> Filter(FilterViewModel vm); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs new file mode 100644 index 000000000..03294982a --- /dev/null +++ b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Api.Lidarr.Models; +using Ombi.Core.Models.Search; + +namespace Ombi.Core.Engine +{ + public interface IMusicSearchEngine + { + Task GetAlbumArtist(string foreignArtistId); + Task GetArtist(int artistId); + Task> GetArtistAlbums(string foreignArtistId); + Task> SearchAlbum(string search); + Task> SearchArtist(string search); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/IRecentlyAddedEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRecentlyAddedEngine.cs similarity index 100% rename from src/Ombi.Core/Engine/IRecentlyAddedEngine.cs rename to src/Ombi.Core/Engine/Interfaces/IRecentlyAddedEngine.cs diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index bfb8be4e8..c8b7746f0 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Ombi.Core.Models; using Ombi.Core.Models.Requests; +using Ombi.Core.Models.UI; +using Ombi.Store.Entities; namespace Ombi.Core.Engine.Interfaces { @@ -11,11 +14,15 @@ namespace Ombi.Core.Engine.Interfaces //Task> GetNewRequests(); //Task> GetAvailableRequests(); RequestCountModel RequestCount(); - Task> GetRequests(int count, int position); + Task> GetRequests(int count, int position, OrderFilterModel model); Task> GetRequests(); Task UserHasRequest(string userId); Task MarkUnavailable(int modelId); Task MarkAvailable(int modelId); + Task GetTotal(); + Task UnSubscribeRequest(int requestId, RequestType type); + Task SubscribeToRequest(int requestId, RequestType type); + Task GetRemainingRequests(OmbiUser user = null); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index 28eb066d4..36ae7da61 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Core.Models.Requests; -using Ombi.Core.Models.Search; +using Ombi.Core.Models.UI; using Ombi.Store.Entities.Requests; namespace Ombi.Core.Engine.Interfaces @@ -10,15 +10,18 @@ namespace Ombi.Core.Engine.Interfaces { Task RemoveTvRequest(int requestId); + Task GetTvRequest(int requestId); Task RequestTvShow(TvRequestViewModel tv); Task DenyChildRequest(int requestId); + Task> GetRequestsLite(int count, int position, OrderFilterModel type); Task> SearchTvRequest(string search); - Task>>> SearchTvRequestTree(string search); Task UpdateTvRequest(TvRequests request); - Task>>> GetRequestsTreeNode(int count, int position); Task> GetAllChldren(int tvId); Task UpdateChildRequest(ChildRequests request); Task RemoveTvChild(int requestId); Task ApproveChildRequest(int id); + Task> GetRequestsLite(); + Task UpdateQualityProfile(int requestId, int profileId); + Task UpdateRootPath(int requestId, int rootPath); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs index 53721f792..0926a7f9a 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs @@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces public interface ITvSearchEngine { Task> Search(string searchTerm); - Task>> SearchTreeNode(string searchTerm); - Task> GetShowInformationTreeNode(int tvdbid); Task GetShowInformation(int tvdbid); - Task>> PopularTree(); Task> Popular(); - Task>> AnticipatedTree(); Task> Anticipated(); - Task>> MostWatchesTree(); Task> MostWatches(); - Task>> TrendingTree(); Task> Trending(); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/IUserStatsEngine.cs b/src/Ombi.Core/Engine/Interfaces/IUserStatsEngine.cs new file mode 100644 index 000000000..3b8474749 --- /dev/null +++ b/src/Ombi.Core/Engine/Interfaces/IUserStatsEngine.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Core.Engine +{ + public interface IUserStatsEngine + { + Task GetSummary(SummaryRequest request); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index f4b0ee48c..9fd6033bf 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -13,11 +13,13 @@ using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; +using Ombi.Core.Models; namespace Ombi.Core.Engine { @@ -25,7 +27,9 @@ namespace Ombi.Core.Engine { public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, - OmbiUserManager manager, IRepository rl, ICacheService cache, ISettingsService ombiSettings) : base(user, requestService, r, manager, cache, ombiSettings) + OmbiUserManager manager, IRepository rl, ICacheService cache, + ISettingsService ombiSettings, IRepository sub) + : base(user, requestService, r, manager, cache, ombiSettings, sub) { MovieApi = movieApi; NotificationHelper = helper; @@ -57,6 +61,7 @@ namespace Ombi.Core.Engine ErrorMessage = $"Please try again later" }; } + var fullMovieName = $"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}"; @@ -81,7 +86,8 @@ namespace Ombi.Core.Engine }; var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); - requestModel.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; + requestModel.DigitalReleaseDate = usDates?.ReleaseDate + ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; var ruleResults = (await RunRequestRules(requestModel)).ToList(); if (ruleResults.Any(x => !x.Success)) @@ -124,24 +130,106 @@ namespace Ombi.Core.Engine /// /// The count. /// The position. + /// The order/filter type. /// - public async Task> GetRequests(int count, int position) + public async Task> GetRequests(int count, int position, + OrderFilterModel orderFilter) { var shouldHide = await HideFromOtherUsers(); - List allRequests; + IQueryable allRequests; if (shouldHide.Hide) { - allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).Skip(position).Take(count).ToListAsync(); + allRequests = + MovieRepository.GetWithUser(shouldHide + .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } else { - allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).ToListAsync(); + allRequests = + MovieRepository + .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } - allRequests.ForEach(x => + + switch (orderFilter.AvailabilityFilter) + { + case FilterType.None: + break; + case FilterType.Available: + allRequests = allRequests.Where(x => x.Available); + break; + case FilterType.NotAvailable: + allRequests = allRequests.Where(x => !x.Available); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (orderFilter.StatusFilter) + { + case FilterType.None: + break; + case FilterType.Approved: + allRequests = allRequests.Where(x => x.Approved); + break; + case FilterType.Processing: + allRequests = allRequests.Where(x => x.Approved && !x.Available); + break; + case FilterType.PendingApproval: + allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var total = allRequests.Count(); + + var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count) + .ToListAsync(); + + requests.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); }); - return allRequests; + return new RequestsViewModel + { + Collection = requests, + Total = total + }; + } + + private IQueryable OrderMovies(IQueryable allRequests, OrderType type) + { + switch (type) + { + case OrderType.RequestedDateAsc: + return allRequests.OrderBy(x => x.RequestedDate); + case OrderType.RequestedDateDesc: + return allRequests.OrderByDescending(x => x.RequestedDate); + case OrderType.TitleAsc: + return allRequests.OrderBy(x => x.Title); + case OrderType.TitleDesc: + return allRequests.OrderByDescending(x => x.Title); + case OrderType.StatusAsc: + return allRequests.OrderBy(x => x.Status); + case OrderType.StatusDesc: + return allRequests.OrderByDescending(x => x.Status); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await MovieRepository.GetWithUser(shouldHide.UserId).CountAsync(); + } + else + { + return await MovieRepository.GetWithUser().CountAsync(); + } } /// @@ -160,9 +248,30 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().ToListAsync(); } + + allRequests.ForEach(async x => + { + x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); + }); return allRequests; } + private async Task CheckForSubscription(HideResult shouldHide, MovieRequests x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Movie); + x.Subscribed = sub != null; + } + } + /// /// Searches the movie request. /// @@ -180,10 +289,12 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().ToListAsync(); } + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); - results.ForEach(x => + results.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); }); return results; } @@ -204,6 +315,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Denied = true; // We are denying a request NotificationHelper.Notify(request, NotificationType.RequestDeclined); @@ -224,6 +336,8 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + + request.MarkedAsApproved = DateTime.Now; request.Approved = true; request.Denied = false; await MovieRepository.Update(request); @@ -244,6 +358,7 @@ namespace Ombi.Core.Engine Result = true }; } + if (!result.Success) { Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); @@ -254,6 +369,7 @@ namespace Ombi.Core.Engine Result = false }; } + // If there are no providers then it's successful but movie has not been sent } @@ -315,6 +431,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = false; await MovieRepository.Update(request); @@ -335,7 +452,9 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = true; + request.MarkedAsAvailable = DateTime.Now; NotificationHelper.Notify(request, NotificationType.RequestAvailable); await MovieRepository.Update(request); @@ -364,45 +483,51 @@ namespace Ombi.Core.Engine RequestType = RequestType.Movie, }); - return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" }; + return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"}; } - public async Task> Filter(FilterViewModel vm) + public async Task GetRemainingRequests(OmbiUser user) { - var shouldHide = await HideFromOtherUsers(); - var requests = shouldHide.Hide ? MovieRepository.GetWithUser(shouldHide.UserId) : MovieRepository.GetWithUser(); - switch (vm.AvailabilityFilter) + if (user == null) { - case FilterType.None: - break; - case FilterType.Available: - requests = requests.Where(x => x.Available); - break; - case FilterType.NotAvailable: - requests = requests.Where(x => !x.Available); - break; - default: - throw new ArgumentOutOfRangeException(); + user = await GetUser(); + + // If user is still null after attempting to get the logged in user, return null. + if (user == null) + { + return null; + } } - switch (vm.StatusFilter) + int limit = user.MovieRequestLimit ?? 0; + + if (limit <= 0) { - case FilterType.None: - break; - case FilterType.Approved: - requests = requests.Where(x => x.Approved); - break; - case FilterType.Processing: - requests = requests.Where(x => x.Approved && !x.Available); - break; - case FilterType.PendingApproval: - requests = requests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); - break; - default: - throw new ArgumentOutOfRangeException(); + return new RequestQuotaCountModel() + { + HasLimit = false, + Limit = 0, + Remaining = 0, + NextRequest = DateTime.Now, + }; } - return requests; + IQueryable log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Movie); + + int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + + DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)) + .OrderBy(x => x.RequestDate) + .Select(x => x.RequestDate) + .FirstOrDefaultAsync(); + + return new RequestQuotaCountModel() + { + HasLimit = true, + Limit = limit, + Remaining = count, + NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc), + }; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index 448ff9235..09b4cea6b 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -9,20 +9,23 @@ using System.Collections.Generic; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Ombi.Core.Rule.Interfaces; using Microsoft.Extensions.Caching.Memory; using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { public class MovieSearchEngine : BaseMediaEngine, IMovieEngine { public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper, - ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s) - : base(identity, service, r, um, mem, s) + ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s, IRepository sub) + : base(identity, service, r, um, mem, s, sub) { MovieApi = movApi; Mapper = mapper; @@ -57,7 +60,6 @@ namespace Ombi.Core.Engine if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -88,7 +90,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -103,7 +104,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -133,7 +133,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -164,10 +163,31 @@ namespace Ombi.Core.Engine viewMovie.TheMovieDbId = viewMovie.Id.ToString(); await RunSearchRules(viewMovie); - + + // This requires the rules to be run first to populate the RequestId property + await CheckForSubscription(viewMovie); + return viewMovie; } + private async Task CheckForSubscription(SearchMovieViewModel viewModel) + { + // Check if this user requested it + var user = await GetUser(); + var request = await RequestService.MovieRequestService.GetAll() + .AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id); + if (request) + { + viewModel.ShowSubscribe = false; + } + else + { + viewModel.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => s.UserId == user.Id + && s.RequestId == viewModel.RequestId && s.RequestType == RequestType.Movie); + viewModel.Subscribed = sub != null; + } + } private async Task ProcessSingleMovie(MovieSearchResult movie) { diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs new file mode 100644 index 000000000..89d1a221e --- /dev/null +++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs @@ -0,0 +1,503 @@ +using Ombi.Api.TheMovieDb; +using Ombi.Core.Models.Requests; +using Ombi.Helpers; +using Ombi.Store.Entities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr; +using Ombi.Core.Authentication; +using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models; +using Ombi.Core.Models.UI; +using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Senders; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Core.Engine +{ + public class MusicRequestEngine : BaseMediaEngine, IMusicRequestEngine + { + public MusicRequestEngine(IRequestServiceMain requestService, IPrincipal user, + INotificationHelper helper, IRuleEvaluator r, ILogger log, + OmbiUserManager manager, IRepository rl, ICacheService cache, + ISettingsService ombiSettings, IRepository sub, ILidarrApi lidarr, + ISettingsService lidarrSettings, IMusicSender sender) + : base(user, requestService, r, manager, cache, ombiSettings, sub) + { + NotificationHelper = helper; + _musicSender = sender; + Logger = log; + _requestLog = rl; + _lidarrApi = lidarr; + _lidarrSettings = lidarrSettings; + } + + private INotificationHelper NotificationHelper { get; } + //private IMovieSender Sender { get; } + private ILogger Logger { get; } + private readonly IRepository _requestLog; + private readonly ISettingsService _lidarrSettings; + private readonly ILidarrApi _lidarrApi; + private readonly IMusicSender _musicSender; + + /// + /// Requests the Album. + /// + /// The model. + /// + public async Task RequestAlbum(MusicAlbumRequestViewModel model) + { + var s = await _lidarrSettings.GetSettingsAsync(); + var album = await _lidarrApi.GetAlbumByForeignId(model.ForeignAlbumId, s.ApiKey, s.FullUri); + if (album == null) + { + return new RequestEngineResult + { + Result = false, + Message = "There was an issue adding this album!", + ErrorMessage = "Please try again later" + }; + } + + var userDetails = await GetUser(); + + var requestModel = new AlbumRequest + { + ForeignAlbumId = model.ForeignAlbumId, + ArtistName = album.artist?.artistName, + ReleaseDate = album.releaseDate, + RequestedDate = DateTime.Now, + RequestType = RequestType.Album, + Rating = album.ratings?.value ?? 0m, + RequestedUserId = userDetails.Id, + Title = album.title, + Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url, + Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url, + ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty + }; + if (requestModel.Cover.IsNullOrEmpty()) + { + requestModel.Cover = album.remoteCover; + } + + var ruleResults = (await RunRequestRules(requestModel)).ToList(); + if (ruleResults.Any(x => !x.Success)) + { + return new RequestEngineResult + { + ErrorMessage = ruleResults.FirstOrDefault(x => x.Message.HasValue()).Message + }; + } + + if (requestModel.Approved) // The rules have auto approved this + { + var requestEngineResult = await AddAlbumRequest(requestModel); + if (requestEngineResult.Result) + { + var result = await ApproveAlbum(requestModel); + if (result.IsError) + { + Logger.LogWarning("Tried auto sending Album but failed. Message: {0}", result.Message); + return new RequestEngineResult + { + Message = result.Message, + ErrorMessage = result.Message, + Result = false + }; + } + + return requestEngineResult; + } + + // If there are no providers then it's successful but album has not been sent + } + + return await AddAlbumRequest(requestModel); + } + + + /// + /// Gets the requests. + /// + /// The count. + /// The position. + /// The order/filter type. + /// + public async Task> GetRequests(int count, int position, + OrderFilterModel orderFilter) + { + var shouldHide = await HideFromOtherUsers(); + IQueryable allRequests; + if (shouldHide.Hide) + { + allRequests = + MusicRepository.GetWithUser(shouldHide + .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + } + else + { + allRequests = + MusicRepository + .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + } + + switch (orderFilter.AvailabilityFilter) + { + case FilterType.None: + break; + case FilterType.Available: + allRequests = allRequests.Where(x => x.Available); + break; + case FilterType.NotAvailable: + allRequests = allRequests.Where(x => !x.Available); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (orderFilter.StatusFilter) + { + case FilterType.None: + break; + case FilterType.Approved: + allRequests = allRequests.Where(x => x.Approved); + break; + case FilterType.Processing: + allRequests = allRequests.Where(x => x.Approved && !x.Available); + break; + case FilterType.PendingApproval: + allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var total = allRequests.Count(); + + var requests = await (OrderAlbums(allRequests, orderFilter.OrderType)).Skip(position).Take(count) + .ToListAsync(); + + requests.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return new RequestsViewModel + { + Collection = requests, + Total = total + }; + } + + private IQueryable OrderAlbums(IQueryable allRequests, OrderType type) + { + switch (type) + { + case OrderType.RequestedDateAsc: + return allRequests.OrderBy(x => x.RequestedDate); + case OrderType.RequestedDateDesc: + return allRequests.OrderByDescending(x => x.RequestedDate); + case OrderType.TitleAsc: + return allRequests.OrderBy(x => x.Title); + case OrderType.TitleDesc: + return allRequests.OrderByDescending(x => x.Title); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await MusicRepository.GetWithUser(shouldHide.UserId).CountAsync(); + } + else + { + return await MusicRepository.GetWithUser().CountAsync(); + } + } + + /// + /// Gets the requests. + /// + /// + public async Task> GetRequests() + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync(); + } + else + { + allRequests = await MusicRepository.GetWithUser().ToListAsync(); + } + + allRequests.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return allRequests; + } + + private async Task CheckForSubscription(HideResult shouldHide, AlbumRequest x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Album); + x.Subscribed = sub != null; + } + } + + /// + /// Searches the album request. + /// + /// The search. + /// + public async Task> SearchAlbumRequest(string search) + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync(); + } + else + { + allRequests = await MusicRepository.GetWithUser().ToListAsync(); + } + + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); + results.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return results; + } + + public async Task ApproveAlbumById(int requestId) + { + var request = await MusicRepository.Find(requestId); + return await ApproveAlbum(request); + } + + public async Task DenyAlbumById(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Denied = true; + // We are denying a request + NotificationHelper.Notify(request, NotificationType.RequestDeclined); + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request successfully deleted", + }; + } + + public async Task ApproveAlbum(AlbumRequest request) + { + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.MarkedAsApproved = DateTime.Now; + request.Approved = true; + request.Denied = false; + await MusicRepository.Update(request); + + + var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification); + if (canNotify.Success) + { + NotificationHelper.Notify(request, NotificationType.RequestApproved); + } + + if (request.Approved) + { + var result = await _musicSender.Send(request); + if (result.Success && result.Sent) + { + return new RequestEngineResult + { + Result = true + }; + } + + if (!result.Success) + { + Logger.LogWarning("Tried auto sending album but failed. Message: {0}", result.Message); + return new RequestEngineResult + { + Message = result.Message, + ErrorMessage = result.Message, + Result = false + }; + } + + // If there are no providers then it's successful but movie has not been sent + } + + return new RequestEngineResult + { + Result = true + }; + } + + /// + /// Removes the Album request. + /// + /// The request identifier. + /// + public async Task RemoveAlbumRequest(int requestId) + { + var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); + await MusicRepository.Delete(request); + } + + public async Task UserHasRequest(string userId) + { + return await MusicRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); + } + + public async Task MarkUnavailable(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Available = false; + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request is now unavailable", + Result = true + }; + } + public async Task GetRemainingRequests(OmbiUser user) + { + if (user == null) + { + user = await GetUser(); + + // If user is still null after attempting to get the logged in user, return null. + if (user == null) + { + return null; + } + } + + int limit = user.MusicRequestLimit ?? 0; + + if (limit <= 0) + { + return new RequestQuotaCountModel() + { + HasLimit = false, + Limit = 0, + Remaining = 0, + NextRequest = DateTime.Now, + }; + } + + IQueryable log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Album); + + int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + + DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)) + .OrderBy(x => x.RequestDate) + .Select(x => x.RequestDate) + .FirstOrDefaultAsync(); + + return new RequestQuotaCountModel() + { + HasLimit = true, + Limit = limit, + Remaining = count, + NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc), + }; + } + + public async Task MarkAvailable(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Available = true; + request.MarkedAsAvailable = DateTime.Now; + NotificationHelper.Notify(request, NotificationType.RequestAvailable); + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request is now available", + Result = true + }; + } + + private async Task AddAlbumRequest(AlbumRequest model) + { + await MusicRepository.Add(model); + + var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); + if (result.Success) + { + NotificationHelper.NewRequest(model); + } + + await _requestLog.Add(new RequestLog + { + UserId = (await GetUser()).Id, + RequestDate = DateTime.UtcNow, + RequestId = model.Id, + RequestType = RequestType.Album, + }); + + return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!" }; + } + + + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MusicSearchEngine.cs b/src/Ombi.Core/Engine/MusicSearchEngine.cs new file mode 100644 index 000000000..d0e577801 --- /dev/null +++ b/src/Ombi.Core/Engine/MusicSearchEngine.cs @@ -0,0 +1,219 @@ +using System; +using AutoMapper; +using Microsoft.Extensions.Logging; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.Search; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Rule.Interfaces; +using Microsoft.Extensions.Caching.Memory; +using Ombi.Api.Lidarr; +using Ombi.Api.Lidarr.Models; +using Ombi.Core.Authentication; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities; +using Ombi.Store.Repository; + +namespace Ombi.Core.Engine +{ + public class MusicSearchEngine : BaseMediaEngine, IMusicSearchEngine + { + public MusicSearchEngine(IPrincipal identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper, + ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s, IRepository sub, + ISettingsService lidarrSettings) + : base(identity, service, r, um, mem, s, sub) + { + _lidarrApi = lidarrApi; + _lidarrSettings = lidarrSettings; + Mapper = mapper; + Logger = logger; + } + + private readonly ILidarrApi _lidarrApi; + private IMapper Mapper { get; } + private ILogger Logger { get; } + private readonly ISettingsService _lidarrSettings; + + /// + /// Searches the specified album. + /// + /// The search. + /// + public async Task> SearchAlbum(string search) + { + var settings = await GetSettings(); + var result = await _lidarrApi.AlbumLookup(search, settings.ApiKey, settings.FullUri); + var vm = new List(); + foreach (var r in result) + { + vm.Add(await MapIntoAlbumVm(r, settings)); + } + + return vm; + } + + /// + /// Searches the specified artist + /// + /// The search. + /// + public async Task> SearchArtist(string search) + { + var settings = await GetSettings(); + var result = await _lidarrApi.ArtistLookup(search, settings.ApiKey, settings.FullUri); + + var vm = new List(); + foreach (var r in result) + { + vm.Add(await MapIntoArtistVm(r)); + } + + return vm; + } + + /// + /// Returns all albums by the specified artist + /// + /// + /// + public async Task> GetArtistAlbums(string foreignArtistId) + { + var settings = await GetSettings(); + var result = await _lidarrApi.GetAlbumsByArtist(foreignArtistId); + // We do not want any Singles (This will include EP's) + var albumsOnly = + result.Albums.Where(x => !x.Type.Equals("Single", StringComparison.InvariantCultureIgnoreCase)); + var vm = new List(); + foreach (var album in albumsOnly) + { + vm.Add(await MapIntoAlbumVm(album, result.Id, result.ArtistName, settings)); + } + return vm; + } + + /// + /// Returns the artist that produced the album + /// + /// + /// + public async Task GetAlbumArtist(string foreignArtistId) + { + var settings = await GetSettings(); + return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri); + } + + public async Task GetArtist(int artistId) + { + var settings = await GetSettings(); + return await _lidarrApi.GetArtist(artistId, settings.ApiKey, settings.FullUri); + } + + private async Task MapIntoArtistVm(ArtistLookup a) + { + var vm = new SearchArtistViewModel + { + ArtistName = a.artistName, + ArtistType = a.artistType, + Banner = a.images?.FirstOrDefault(x => x.coverType.Equals("banner"))?.url, + Logo = a.images?.FirstOrDefault(x => x.coverType.Equals("logo"))?.url, + CleanName = a.cleanName, + Disambiguation = a.disambiguation, + ForignArtistId = a.foreignArtistId, + Links = a.links, + Overview = a.overview, + }; + + var poster = a.images?.FirstOrDefault(x => x.coverType.Equals("poaster")); + if (poster == null) + { + vm.Poster = a.remotePoster; + } + + + await Rules.StartSpecificRules(vm, SpecificRules.LidarrArtist); + + return vm; + } + + private async Task MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings) + { + var vm = new SearchAlbumViewModel + { + ForeignAlbumId = a.foreignAlbumId, + Monitored = a.monitored, + Rating = a.ratings?.value ?? 0m, + ReleaseDate = a.releaseDate, + Title = a.title, + Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url + }; + if (a.artistId > 0) + { + //TODO THEY HAVE FIXED THIS IN DEV + // The JSON is different for some stupid reason + // Need to lookup the artist now and all the images -.-" + var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri); + vm.ArtistName = artist.artistName; + vm.ForeignArtistId = artist.foreignArtistId; + } + else + { + vm.ForeignArtistId = a.artist?.foreignArtistId; + vm.ArtistName = a.artist?.artistName; + } + + vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url; + if (vm.Cover.IsNullOrEmpty()) + { + vm.Cover = a.remoteCover; + } + + await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum); + + await RunSearchRules(vm); + + return vm; + } + + private async Task MapIntoAlbumVm(Album a, string artistId, string artistName, LidarrSettings settings) + { + var fullAlbum = await _lidarrApi.GetAlbumByForeignId(a.Id, settings.ApiKey, settings.FullUri); + var vm = new SearchAlbumViewModel + { + ForeignAlbumId = a.Id, + Monitored = fullAlbum.monitored, + Rating = fullAlbum.ratings?.value ?? 0m, + ReleaseDate = fullAlbum.releaseDate, + Title = a.Title, + Disk = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url, + ForeignArtistId = artistId, + ArtistName = artistName, + Cover = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url + }; + + if (vm.Cover.IsNullOrEmpty()) + { + vm.Cover = fullAlbum.remoteCover; + } + + await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum); + + await RunSearchRules(vm); + + return vm; + } + + private LidarrSettings _settings; + private async Task GetSettings() + { + return _settings ?? (_settings = await _lidarrSettings.GetSettingsAsync()); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/RecentlyAddedEngine.cs b/src/Ombi.Core/Engine/RecentlyAddedEngine.cs index 59be359f8..114d32a24 100644 --- a/src/Ombi.Core/Engine/RecentlyAddedEngine.cs +++ b/src/Ombi.Core/Engine/RecentlyAddedEngine.cs @@ -63,13 +63,17 @@ namespace Ombi.Core.Engine var recentlyAddedLog = new HashSet(); foreach (var p in plexContent) { + if (!p.HasTheMovieDb) + { + continue; + } if (p.Type == PlexMediaTypeEntity.Movie) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, - ContentId = p.Id, + ContentId = int.Parse(p.TheMovieDbId), ContentType = ContentType.Parent }); } @@ -78,12 +82,18 @@ namespace Ombi.Core.Engine // Add the episodes foreach (var ep in p.Episodes) { + if (!ep.Series.HasTvDb) + { + continue; + } recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, - ContentId = ep.Id, - ContentType = ContentType.Episode + ContentId = int.Parse(ep.Series.TvDbId), + ContentType = ContentType.Episode, + EpisodeNumber = ep.EpisodeNumber, + SeasonNumber = ep.SeasonNumber }); } } @@ -91,13 +101,17 @@ namespace Ombi.Core.Engine foreach (var e in embyContent) { + if (e.TheMovieDbId.IsNullOrEmpty()) + { + continue; + } if (e.Type == EmbyMediaType.Movie) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentId = e.Id, + ContentId = int.Parse(e.TheMovieDbId), ContentType = ContentType.Parent }); } @@ -106,12 +120,18 @@ namespace Ombi.Core.Engine // Add the episodes foreach (var ep in e.Episodes) { + if (ep.Series.TvDbId.IsNullOrEmpty()) + { + continue; + } recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentId = ep.Id, - ContentType = ContentType.Episode + ContentId = int.Parse(ep.Series.TvDbId), + ContentType = ContentType.Episode, + EpisodeNumber = ep.EpisodeNumber, + SeasonNumber = ep.SeasonNumber }); } } @@ -152,7 +172,9 @@ namespace Ombi.Core.Engine model.Add(new RecentlyAddedMovieModel { Id = emby.Id, - ImdbId = emby.ProviderId, + ImdbId = emby.ImdbId, + TheMovieDbId = emby.TheMovieDbId, + TvDbId = emby.TvDbId, AddedAt = emby.AddedAt, Title = emby.Title, }); @@ -211,7 +233,9 @@ namespace Ombi.Core.Engine model.Add(new RecentlyAddedTvModel { Id = emby.Id, - ImdbId = emby.ProviderId, + ImdbId = emby.ImdbId, + TvDbId = emby.TvDbId, + TheMovieDbId = emby.TheMovieDbId, AddedAt = emby.AddedAt, Title = emby.Title, EpisodeNumber = episode.EpisodeNumber, diff --git a/src/Ombi.Core/Engine/TreeNode.cs b/src/Ombi.Core/Engine/TreeNode.cs deleted file mode 100644 index 14f2f4cb0..000000000 --- a/src/Ombi.Core/Engine/TreeNode.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; - -namespace Ombi.Core.Engine -{ - - public class TreeNode - { - public string Label { get; set; } - public T Data { get; set; } - public List> Children { get; set; } - public bool Leaf { get; set; } - public bool Expanded { get; set; } - } - - public class TreeNode - { - public string Label { get; set; } - public T Data { get; set; } - public List> Children { get; set; } - public bool Leaf { get; set; } - public bool Expanded { get; set; } - } -} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index aaa2d353d..7994b23aa 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -1,6 +1,7 @@ using System; using AutoMapper; using Ombi.Api.TvMaze; +using Ombi.Api.TheMovieDb; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; @@ -14,6 +15,7 @@ using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; +using Ombi.Core.Models.UI; using Ombi.Core.Rule; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Senders; @@ -21,16 +23,19 @@ using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; +using Ombi.Core.Models; namespace Ombi.Core.Engine { public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine { - public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, + public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, - ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings) + ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache, + IRepository sub) : base(user, requestService, rule, manager, cache, settings, sub) { TvApi = tvApi; + MovieDbApi = movApi; NotificationHelper = helper; TvSender = sender; Audit = audit; @@ -39,6 +44,7 @@ namespace Ombi.Core.Engine private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } + private IMovieDbApi MovieDbApi { get; } private ITvSender TvSender { get; } private IAuditRepository Audit { get; } private readonly IRepository _requestLog; @@ -47,7 +53,7 @@ namespace Ombi.Core.Engine { var user = await GetUser(); - var tvBuilder = new TvShowRequestBuilder(TvApi); + var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi); (await tvBuilder .GetShowInfo(tv.TvDbId)) .CreateTvList(tv) @@ -127,8 +133,8 @@ namespace Ombi.Core.Engine var newRequest = tvBuilder.CreateNewRequest(tv); return await AddRequest(newRequest.NewRequest); } - - public async Task> GetRequests(int count, int position) + + public async Task> GetRequests(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); List allRequests; @@ -138,6 +144,7 @@ namespace Ombi.Core.Engine .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) + .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()) .Skip(position).Take(count).ToListAsync(); // Filter out children @@ -150,72 +157,143 @@ namespace Ombi.Core.Engine .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) + .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()) .Skip(position).Take(count).ToListAsync(); + } - return allRequests; + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + + return new RequestsViewModel + { + Collection = allRequests + }; } - public async Task>>> GetRequestsTreeNode(int count, int position) + public async Task> GetRequestsLite(int count, int position, OrderFilterModel type) + { + var shouldHide = await HideFromOtherUsers(); + List allRequests = null; + if (shouldHide.Hide) + { + var tv = TvRepository.GetLite(shouldHide.UserId); + if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) + { + allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); + } + + // Filter out children + FilterChildren(allRequests, shouldHide); + } + else + { + var tv = TvRepository.GetLite(); + if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) + { + allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); + } + } + if (allRequests == null) + { + return new RequestsViewModel(); + } + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + + return new RequestsViewModel + { + Collection = allRequests + }; + } + public async Task> GetRequests() { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { - allRequests = await TvRepository.Get(shouldHide.UserId) - .Include(x => x.ChildRequests) - .ThenInclude(x => x.SeasonRequests) - .ThenInclude(x => x.Episodes) - .Skip(position).Take(count).ToListAsync(); + allRequests = await TvRepository.Get(shouldHide.UserId).ToListAsync(); FilterChildren(allRequests, shouldHide); } else { - allRequests = await TvRepository.Get() - .Include(x => x.ChildRequests) - .ThenInclude(x => x.SeasonRequests) - .ThenInclude(x => x.Episodes) - .Skip(position).Take(count).ToListAsync(); + allRequests = await TvRepository.Get().ToListAsync(); } - return ParseIntoTreeNode(allRequests); + + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; } - public async Task> GetRequests() + + public async Task> GetRequestsLite() { var shouldHide = await HideFromOtherUsers(); - IQueryable allRequests; + List allRequests; if (shouldHide.Hide) { - allRequests = TvRepository.Get(shouldHide.UserId); + allRequests = await TvRepository.GetLite(shouldHide.UserId).ToListAsync(); FilterChildren(allRequests, shouldHide); } else { - allRequests = TvRepository.Get(); + allRequests = await TvRepository.GetLite().ToListAsync(); } - return await allRequests.ToListAsync(); + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; + } + + public async Task GetTvRequest(int requestId) + { + var shouldHide = await HideFromOtherUsers(); + TvRequests request; + if (shouldHide.Hide) + { + request = await TvRepository.Get(shouldHide.UserId).Where(x => x.Id == requestId).FirstOrDefaultAsync(); + + FilterChildren(request, shouldHide); + } + else + { + request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync(); + } + + await CheckForSubscription(shouldHide, request); + return request; } private static void FilterChildren(IEnumerable allRequests, HideResult shouldHide) { + if (allRequests == null) + { + return; + } // Filter out children foreach (var t in allRequests) { for (var j = 0; j < t.ChildRequests.Count; j++) { - var child = t.ChildRequests[j]; - if (child.RequestedUserId != shouldHide.UserId) - { - t.ChildRequests.RemoveAt(j); - j--; - } + FilterChildren(t, shouldHide); } } } + private static void FilterChildren(TvRequests t, HideResult shouldHide) + { + // Filter out children + + for (var j = 0; j < t.ChildRequests.Count; j++) + { + var child = t.ChildRequests[j]; + if (child.RequestedUserId != shouldHide.UserId) + { + t.ChildRequests.RemoveAt(j); + j--; + } + } + + } + public async Task> GetAllChldren(int tvId) { var shouldHide = await HideFromOtherUsers(); @@ -229,6 +307,8 @@ namespace Ombi.Core.Engine allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync(); } + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; } @@ -245,23 +325,27 @@ namespace Ombi.Core.Engine allRequests = TvRepository.Get(); } var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); + + results.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); return results; } - public async Task>>> SearchTvRequestTree(string search) + public async Task UpdateRootPath(int requestId, int rootPath) { - var shouldHide = await HideFromOtherUsers(); - IQueryable allRequests; - if (shouldHide.Hide) - { - allRequests = TvRepository.Get(shouldHide.UserId); - } - else - { - allRequests = TvRepository.Get(); - } - var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); - return ParseIntoTreeNode(results); + var allRequests = TvRepository.Get(); + var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId); + results.RootFolder = rootPath; + + await TvRepository.Update(results); + } + + public async Task UpdateQualityProfile(int requestId, int profileId) + { + var allRequests = TvRepository.Get(); + var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId); + results.QualityOverride = profileId; + + await TvRepository.Update(results); } public async Task UpdateTvRequest(TvRequests request) @@ -274,9 +358,10 @@ namespace Ombi.Core.Engine results.ImdbId = request.ImdbId; results.Overview = request.Overview; results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath); + results.Background = PosterPathHelper.FixBackgroundPath(request.Background); results.QualityOverride = request.QualityOverride; results.RootFolder = request.RootFolder; - + await TvRepository.Update(results); return results; } @@ -412,6 +497,7 @@ namespace Ombi.Core.Engine }; } request.Available = true; + request.MarkedAsAvailable = DateTime.Now; foreach (var season in request.SeasonRequests) { foreach (var e in season.Episodes) @@ -428,6 +514,42 @@ namespace Ombi.Core.Engine }; } + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await TvRepository.Get(shouldHide.UserId).CountAsync(); + } + else + { + return await TvRepository.Get().CountAsync(); + } + } + + private async Task CheckForSubscription(HideResult shouldHide, TvRequests x) + { + foreach (var tv in x.ChildRequests) + { + await CheckForSubscription(shouldHide, tv); + } + } + + private async Task CheckForSubscription(HideResult shouldHide, ChildRequests x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.TvShow); + x.Subscribed = sub != null; + } + } + private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest) { // Add the child @@ -445,29 +567,7 @@ namespace Ombi.Core.Engine return await AfterRequest(model.ChildRequests.FirstOrDefault()); } - private static List>> ParseIntoTreeNode(IEnumerable result) - { - var node = new List>>(); - - foreach (var value in result) - { - node.Add(new TreeNode> - { - Data = value, - Children = new List>> - { - new TreeNode> - { - Data = SortEpisodes(value.ChildRequests), - Leaf = true - } - } - }); - } - return node; - } - - private static List SortEpisodes(List items) + private static List SortEpisodes(List items) { foreach (var value in items) { @@ -488,6 +588,15 @@ namespace Ombi.Core.Engine NotificationHelper.NewRequest(model); } + await _requestLog.Add(new RequestLog + { + UserId = (await GetUser()).Id, + RequestDate = DateTime.UtcNow, + RequestId = model.Id, + RequestType = RequestType.TvShow, + EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(), + }); + if (model.Approved) { // Autosend @@ -503,15 +612,58 @@ namespace Ombi.Core.Engine }; } - await _requestLog.Add(new RequestLog + return new RequestEngineResult { Result = true }; + } + + public async Task GetRemainingRequests(OmbiUser user) + { + if (user == null) { - UserId = (await GetUser()).Id, - RequestDate = DateTime.UtcNow, - RequestId = model.Id, - RequestType = RequestType.TvShow, - }); + user = await GetUser(); - return new RequestEngineResult { Result = true }; + // If user is still null after attempting to get the logged in user, return null. + if (user == null) + { + return null; + } + } + + int limit = user.EpisodeRequestLimit ?? 0; + + if (limit <= 0) + { + return new RequestQuotaCountModel() + { + HasLimit = false, + Limit = 0, + Remaining = 0, + NextRequest = DateTime.Now, + }; + } + + IQueryable log = _requestLog.GetAll() + .Where(x => x.UserId == user.Id + && x.RequestType == RequestType.TvShow + && x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + + // Needed, due to a bug which would cause all episode counts to be 0 + int zeroEpisodeCount = await log.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync(); + + int episodeCount = await log.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync(); + + int count = limit - (zeroEpisodeCount + episodeCount); + + DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate) + .Select(x => x.RequestDate) + .FirstOrDefaultAsync(); + + return new RequestQuotaCountModel() + { + HasLimit = true, + Limit = limit, + Remaining = count, + NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc), + }; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index 1664ecfa7..bb674a35d 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Caching.Memory; using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; namespace Ombi.Core.Engine { @@ -27,8 +28,8 @@ namespace Ombi.Core.Engine { public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService plexSettings, ISettingsService embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, - ICacheService memCache, ISettingsService s) - : base(identity, service, r, um, memCache, s) + ICacheService memCache, ISettingsService s, IRepository sub) + : base(identity, service, r, um, memCache, s, sub) { TvMazeApi = tvMaze; Mapper = mapper; @@ -53,16 +54,20 @@ namespace Ombi.Core.Engine if (searchResult != null) { - return await ProcessResults(searchResult); + var retVal = new List(); + foreach (var tvMazeSearch in searchResult) + { + if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false)) + { + continue; + } + retVal.Add(await ProcessResult(tvMazeSearch)); + } + return retVal; } return null; } - public async Task>> SearchTreeNode(string searchTerm) - { - var result = await Search(searchTerm); - return result.Select(ParseIntoTreeNode).ToList(); - } public async Task GetShowInformation(int tvdbid) { var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid); @@ -115,19 +120,6 @@ namespace Ombi.Core.Engine return await ProcessResult(mapped); } - public async Task> GetShowInformationTreeNode(int tvdbid) - { - var result = await GetShowInformation(tvdbid); - return ParseIntoTreeNode(result); - } - - public async Task>> PopularTree() - { - var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12)); - var processed = await ProcessResults(result); - return processed.Select(ParseIntoTreeNode).ToList(); - } - public async Task> Popular() { var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12)); @@ -135,12 +127,6 @@ namespace Ombi.Core.Engine return processed; } - public async Task>> AnticipatedTree() - { - var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12)); - var processed = await ProcessResults(result); - return processed.Select(ParseIntoTreeNode).ToList(); - } public async Task> Anticipated() { @@ -149,12 +135,6 @@ namespace Ombi.Core.Engine return processed; } - public async Task>> MostWatchesTree() - { - var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12)); - var processed = await ProcessResults(result); - return processed.Select(ParseIntoTreeNode).ToList(); - } public async Task> MostWatches() { var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12)); @@ -162,13 +142,6 @@ namespace Ombi.Core.Engine return processed; } - public async Task>> TrendingTree() - { - var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12)); - var processed = await ProcessResults(result); - return processed.Select(ParseIntoTreeNode).ToList(); - } - public async Task> Trending() { var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12)); @@ -176,33 +149,21 @@ namespace Ombi.Core.Engine return processed; } - private static TreeNode ParseIntoTreeNode(SearchTvShowViewModel result) - { - return new TreeNode - { - Data = result, - Children = new List> - { - new TreeNode - { - Data = result, Leaf = true - } - }, - Leaf = false - }; - } - private async Task> ProcessResults(IEnumerable items) { var retVal = new List(); foreach (var tvMazeSearch in items) { - var viewT = Mapper.Map(tvMazeSearch); - retVal.Add(await ProcessResult(viewT)); + retVal.Add(await ProcessResult(tvMazeSearch)); } return retVal; } + private async Task ProcessResult(T tvMazeSearch) + { + return Mapper.Map(tvMazeSearch); + } + private async Task ProcessResult(SearchTvShowViewModel item) { item.TheTvDbId = item.Id.ToString(); diff --git a/src/Ombi.Core/Engine/UserStatsEngine.cs b/src/Ombi.Core/Engine/UserStatsEngine.cs new file mode 100644 index 000000000..06ab65f92 --- /dev/null +++ b/src/Ombi.Core/Engine/UserStatsEngine.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Store.Entities; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Core.Engine +{ + public class UserStatsEngine : IUserStatsEngine + { + public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest) + { + _userManager = um; + _movieRequest = movieRequest; + _tvRequest = tvRequest; + } + + private readonly OmbiUserManager _userManager; + private readonly IMovieRequestRepository _movieRequest; + private readonly ITvRequestRepository _tvRequest; + + public async Task GetSummary(SummaryRequest request) + { + // get all movie requests + var movies = _movieRequest.GetWithUser(); + var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To); + var tv = _tvRequest.GetLite(); + var children = tv.SelectMany(x => + x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To)); + + var moviesCount = filteredMovies.CountAsync(); + var childrenCount = children.CountAsync(); + var availableMovies = + movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync(); + var availableChildren = tv.SelectMany(x => + x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync(); + + var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync(); + var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync(); + + + return new UserStatsSummary + { + TotalMovieRequests = await moviesCount, + TotalTvRequests = await childrenCount, + CompletedRequestsTv = await availableChildren, + CompletedRequestsMovies = await availableMovies, + MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser, + MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser, + }; + } + } + + public class SummaryRequest + { + public DateTime From { get; set; } + public DateTime To { get; set; } + } + + public class UserStatsSummary + { + public int TotalRequests => TotalTvRequests + TotalMovieRequests; + public int TotalMovieRequests { get; set; } + public int TotalTvRequests { get; set; } + public int TotalIssues { get; set; } + public int CompletedRequestsMovies { get; set; } + public int CompletedRequestsTv { get; set; } + public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv; + public OmbiUser MostRequestedUserMovie { get; set; } + public OmbiUser MostRequestedUserTv { get; set; } + + } +} diff --git a/src/Ombi.Core/Helpers/NotificationHelper.cs b/src/Ombi.Core/Helpers/NotificationHelper.cs index 9bd890c21..1615b24f7 100644 --- a/src/Ombi.Core/Helpers/NotificationHelper.cs +++ b/src/Ombi.Core/Helpers/NotificationHelper.cs @@ -40,6 +40,18 @@ namespace Ombi.Core BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); } + public void NewRequest(AlbumRequest model) + { + var notificationModel = new NotificationOptions + { + RequestId = model.Id, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = model.RequestType + }; + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); + } + public void Notify(MovieRequests model, NotificationType type) { @@ -66,5 +78,19 @@ namespace Ombi.Core }; BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); } + + public void Notify(AlbumRequest model, NotificationType type) + { + var notificationModel = new NotificationOptions + { + RequestId = model.Id, + DateTime = DateTime.Now, + NotificationType = type, + RequestType = model.RequestType, + Recipient = model.RequestedUser?.Email ?? string.Empty + }; + + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); + } } } \ No newline at end of file diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index 1f92536b8..e5277e236 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Ombi.Api.TvMaze; +using Ombi.Api.TheMovieDb; using Ombi.Api.TvMaze.Models; +using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; @@ -16,23 +18,37 @@ namespace Ombi.Core.Helpers public class TvShowRequestBuilder { - public TvShowRequestBuilder(ITvMazeApi tvApi) + public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi) { TvApi = tvApi; + MovieDbApi = movApi; } private ITvMazeApi TvApi { get; } + private IMovieDbApi MovieDbApi { get; } public ChildRequests ChildRequest { get; set; } public List TvRequests { get; protected set; } public string PosterPath { get; protected set; } + public string BackdropPath { get; protected set; } public DateTime FirstAir { get; protected set; } public TvRequests NewRequest { get; protected set; } protected TvMazeShow ShowInfo { get; set; } + protected List Results { get; set; } public async Task GetShowInfo(int id) { ShowInfo = await TvApi.ShowLookupByTheTvDbId(id); + Results = await MovieDbApi.SearchTv(ShowInfo.name); + foreach (TvSearchResult result in Results) { + if (result.Name == ShowInfo.name) + { + var showIds = await MovieDbApi.GetTvExternals(result.Id); + ShowInfo.externals.imdb = showIds.imdb_id; + BackdropPath = result.BackdropPath; + break; + } + } DateTime.TryParse(ShowInfo.premiered, out var dt); @@ -55,7 +71,7 @@ namespace Ombi.Core.Helpers RequestedUserId = userId, SeasonRequests = new List(), Title = ShowInfo.name, - SeriesType = ShowInfo.type.Equals("Animation", StringComparison.CurrentCultureIgnoreCase) ? SeriesType.Anime : SeriesType.Standard + SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.OrdinalIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard }; return this; @@ -226,7 +242,8 @@ namespace Ombi.Core.Helpers ImdbId = ShowInfo.externals?.imdb ?? string.Empty, TvDbId = tv.TvDbId, ChildRequests = new List(), - TotalSeasons = tv.Seasons.Count() + TotalSeasons = tv.Seasons.Count(), + Background = BackdropPath }; NewRequest.ChildRequests.Add(ChildRequest); diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs new file mode 100644 index 000000000..a5c0c44ff --- /dev/null +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; +using Ombi.Api.Plex.Models; + +namespace Ombi.Core.Authentication +{ + public interface IPlexOAuthManager + { + Task GetAccessTokenFromPin(int pinId); + Task GetOAuthUrl(int pinId, string code, string websiteAddress = null); + Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress); + Task GetAccount(string accessToken); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/RequestQuotaCountModel.cs b/src/Ombi.Core/Models/RequestQuotaCountModel.cs new file mode 100644 index 000000000..1af9ad819 --- /dev/null +++ b/src/Ombi.Core/Models/RequestQuotaCountModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ombi.Core.Models +{ + public class RequestQuotaCountModel + { + public bool HasLimit { get; set; } + + public int Limit { get; set; } + + public int Remaining { get; set; } + + public DateTime NextRequest { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/FilterResult.cs b/src/Ombi.Core/Models/Requests/FilterResult.cs new file mode 100644 index 000000000..467b9731a --- /dev/null +++ b/src/Ombi.Core/Models/Requests/FilterResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ombi.Core.Models.Requests +{ + public class FilterResult + { + public int Total { get; set; } + public IEnumerable Collection { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/FilterViewModel.cs b/src/Ombi.Core/Models/Requests/FilterViewModel.cs index 27ccc39d6..25a9d5ef1 100644 --- a/src/Ombi.Core/Models/Requests/FilterViewModel.cs +++ b/src/Ombi.Core/Models/Requests/FilterViewModel.cs @@ -4,6 +4,8 @@ { public FilterType AvailabilityFilter { get; set; } public FilterType StatusFilter { get; set; } + public int Position { get; set; } + public int Count { get; set; } } public enum FilterType diff --git a/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs b/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs index 8a269054f..0e68a38e9 100644 --- a/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs +++ b/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs @@ -7,5 +7,6 @@ namespace Ombi.Core.Models.Requests { IMovieRequestRepository MovieRequestService { get; } ITvRequestRepository TvRequestService { get; } + IMusicRequestRepository MusicRequestRepository { get; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs new file mode 100644 index 000000000..4c3426c9e --- /dev/null +++ b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs @@ -0,0 +1,7 @@ +namespace Ombi.Core.Models.Requests +{ + public class MusicAlbumRequestViewModel + { + public string ForeignAlbumId { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/RequestService.cs b/src/Ombi.Core/Models/Requests/RequestService.cs index 049666440..6f7431baa 100644 --- a/src/Ombi.Core/Models/Requests/RequestService.cs +++ b/src/Ombi.Core/Models/Requests/RequestService.cs @@ -5,13 +5,15 @@ namespace Ombi.Core.Models.Requests { public class RequestService : IRequestServiceMain { - public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie) + public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie, IMusicRequestRepository music) { TvRequestService = tv; MovieRequestService = movie; + MusicRequestRepository = music; } public ITvRequestRepository TvRequestService { get; } + public IMusicRequestRepository MusicRequestRepository { get; } public IMovieRequestRepository MovieRequestService { get; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs new file mode 100644 index 000000000..a494a3cb5 --- /dev/null +++ b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs @@ -0,0 +1,23 @@ +using System; +using Ombi.Store.Entities; + +namespace Ombi.Core.Models.Search +{ + public class SearchAlbumViewModel : SearchViewModel + { + public string Title { get; set; } + public string ForeignAlbumId { get; set; } + public bool Monitored { get; set; } + public string AlbumType { get; set; } + public decimal Rating { get; set; } + public DateTime ReleaseDate { get; set; } + public string ArtistName { get; set; } + public string ForeignArtistId { get; set; } + public string Cover { get; set; } + public string Disk { get; set; } + public decimal PercentOfTracks { get; set; } + public override RequestType Type => RequestType.Album; + public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0; + public bool FullyAvailable => PercentOfTracks == 100; + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchArtistViewModel.cs b/src/Ombi.Core/Models/Search/SearchArtistViewModel.cs new file mode 100644 index 000000000..b736df529 --- /dev/null +++ b/src/Ombi.Core/Models/Search/SearchArtistViewModel.cs @@ -0,0 +1,19 @@ +using Ombi.Api.Lidarr.Models; + +namespace Ombi.Core.Models.Search +{ + public class SearchArtistViewModel + { + public string ArtistName { get; set; } + public string ForignArtistId { get; set; } + public string Overview { get; set; } + public string Disambiguation { get; set; } + public string Banner { get; set; } + public string Poster { get; set; } + public string Logo { get; set; } + public bool Monitored { get; set; } + public string ArtistType { get; set; } + public string CleanName { get; set; } + public Link[] Links { get; set; } // Couldn't be bothered to map it + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs index a91d75d65..c421af45f 100644 --- a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs @@ -56,7 +56,6 @@ namespace Ombi.Core.Models.Search public bool FullyAvailable { get; set; } // We only have some episodes public bool PartlyAvailable { get; set; } - public override RequestType Type => RequestType.TvShow; } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchViewModel.cs b/src/Ombi.Core/Models/Search/SearchViewModel.cs index 8c8d49aff..a388ccfff 100644 --- a/src/Ombi.Core/Models/Search/SearchViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchViewModel.cs @@ -8,12 +8,13 @@ namespace Ombi.Core.Models.Search public int Id { get; set; } public bool Approved { get; set; } public bool Requested { get; set; } + public int RequestId { get; set; } public bool Available { get; set; } public string PlexUrl { get; set; } + public string EmbyUrl { get; set; } public string Quality { get; set; } public abstract RequestType Type { get; } - /// /// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule /// @@ -26,5 +27,11 @@ namespace Ombi.Core.Models.Search public string TheTvDbId { get; set; } [NotMapped] public string TheMovieDbId { get; set; } + + + [NotMapped] + public bool Subscribed { get; set; } + [NotMapped] + public bool ShowSubscribe { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/OrderFilterModel.cs b/src/Ombi.Core/Models/UI/OrderFilterModel.cs new file mode 100644 index 000000000..3e3192829 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderFilterModel.cs @@ -0,0 +1,11 @@ +using Ombi.Core.Models.Requests; + +namespace Ombi.Core.Models.UI +{ + public class OrderFilterModel + { + public FilterType AvailabilityFilter { get; set; } + public FilterType StatusFilter { get; set; } + public OrderType OrderType { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/OrderType.cs b/src/Ombi.Core/Models/UI/OrderType.cs new file mode 100644 index 000000000..67d20b127 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderType.cs @@ -0,0 +1,12 @@ +namespace Ombi.Core.Models.UI +{ + public enum OrderType + { + RequestedDateAsc =1, + RequestedDateDesc =2, + TitleAsc =3, + TitleDesc=4, + StatusAsc=5, + StatusDesc=6 + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/RequestsViewModel.cs b/src/Ombi.Core/Models/UI/RequestsViewModel.cs new file mode 100644 index 000000000..9d772bc53 --- /dev/null +++ b/src/Ombi.Core/Models/UI/RequestsViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ombi.Core.Models.UI +{ + public class RequestsViewModel + { + public IEnumerable Collection { get; set; } + public int Total { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/UserViewModel.cs b/src/Ombi.Core/Models/UI/UserViewModel.cs index 1c1e6162b..ca40c2ec5 100644 --- a/src/Ombi.Core/Models/UI/UserViewModel.cs +++ b/src/Ombi.Core/Models/UI/UserViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Ombi.Store.Entities; namespace Ombi.Core.Models.UI { @@ -16,6 +17,11 @@ namespace Ombi.Core.Models.UI public UserType UserType { get; set; } public int MovieRequestLimit { get; set; } public int EpisodeRequestLimit { get; set; } + public RequestQuotaCountModel EpisodeRequestQuota { get; set; } + public RequestQuotaCountModel MovieRequestQuota { get; set; } + public RequestQuotaCountModel MusicRequestQuota { get; set; } + public int MusicRequestLimit { get; set; } + public UserQualityProfiles UserQualityProfiles { get; set; } } public class ClaimCheckboxes diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index c2af094bb..10e07822a 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -10,18 +10,18 @@ - - - - - + + + + - + + diff --git a/src/Ombi.Core/Rule/Interfaces/SpecificRules.cs b/src/Ombi.Core/Rule/Interfaces/SpecificRules.cs index 522ba8a95..d432f87be 100644 --- a/src/Ombi.Core/Rule/Interfaces/SpecificRules.cs +++ b/src/Ombi.Core/Rule/Interfaces/SpecificRules.cs @@ -3,5 +3,7 @@ public enum SpecificRules { CanSendNotification, + LidarrArtist, + LidarrAlbum, } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs index 7eecd62f2..a55868db8 100644 --- a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs @@ -29,6 +29,8 @@ namespace Ombi.Core.Rule.Rules.Request obj.Approved = true; if (obj.RequestType == RequestType.TvShow && User.IsInRole(OmbiRoles.AutoApproveTv)) obj.Approved = true; + if (obj.RequestType == RequestType.Album && User.IsInRole(OmbiRoles.AutoApproveMusic)) + obj.Approved = true; return Task.FromResult(Success()); // We don't really care, we just don't set the obj to approve } } diff --git a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs index e9729ec35..1cdf03955 100644 --- a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs @@ -23,13 +23,23 @@ namespace Ombi.Core.Rule.Rules if (obj.RequestType == RequestType.Movie) { - if (User.IsInRole(OmbiRoles.RequestMovie)) + if (User.IsInRole(OmbiRoles.RequestMovie) || User.IsInRole(OmbiRoles.AutoApproveMovie)) return Task.FromResult(Success()); return Task.FromResult(Fail("You do not have permissions to Request a Movie")); } - if (User.IsInRole(OmbiRoles.RequestTv)) - return Task.FromResult(Success()); + if (obj.RequestType == RequestType.TvShow) + { + if (User.IsInRole(OmbiRoles.RequestTv) || User.IsInRole(OmbiRoles.AutoApproveTv)) + return Task.FromResult(Success()); + } + + if (obj.RequestType == RequestType.Album) + { + if (User.IsInRole(OmbiRoles.RequestMusic) || User.IsInRole(OmbiRoles.AutoApproveMusic)) + return Task.FromResult(Success()); + } + return Task.FromResult(Fail("You do not have permissions to Request a TV Show")); } } diff --git a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs index a19ac1df8..2afd0700b 100644 --- a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs @@ -54,6 +54,7 @@ namespace Ombi.Core.Rule.Rules.Request var movieLimit = user.MovieRequestLimit; var episodeLimit = user.EpisodeRequestLimit; + var musicLimit = user.MusicRequestLimit; var requestLog = _requestLog.GetAll().Where(x => x.UserId == obj.RequestedUserId); if (obj.RequestType == RequestType.Movie) @@ -71,7 +72,7 @@ namespace Ombi.Core.Rule.Rules.Request return Fail("You have exceeded your Movie request quota!"); } } - else + else if (obj.RequestType == RequestType.TvShow) { if (episodeLimit <= 0) return Success(); @@ -81,21 +82,40 @@ namespace Ombi.Core.Rule.Rules.Request // Get the count of requests to be made foreach (var s in child.SeasonRequests) { - requestCount = s.Episodes.Count; + requestCount += s.Episodes.Count; } var tvLogs = requestLog.Where(x => x.RequestType == RequestType.TvShow); // Count how many requests in the past 7 days var tv = tvLogs.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); - var count = await tv.Select(x => x.EpisodeCount).CountAsync(); - count += requestCount; // Add the amount of requests in + + // Needed, due to a bug which would cause all episode counts to be 0 + var zeroEpisodeCount = await tv.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync(); + + var episodeCount = await tv.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync(); + + var count = requestCount + episodeCount + zeroEpisodeCount; // Add the amount of requests in if (count > episodeLimit) { return Fail("You have exceeded your Episode request quota!"); } + } else if (obj.RequestType == RequestType.Album) + { + if (musicLimit <= 0) + return Success(); + + var albumLogs = requestLog.Where(x => x.RequestType == RequestType.Album); + + // Count how many requests in the past 7 days + var count = await albumLogs.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + count += 1; // Since we are including this request + if (count > musicLimit) + { + return Fail("You have exceeded your Album request quota!"); + } } - return Success(); + return Success(); } } } diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 74b537352..486de9ea8 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; @@ -23,20 +25,20 @@ namespace Ombi.Core.Rule.Rules.Search EmbyContent item = null; if (obj.ImdbId.HasValue()) { - item = await EmbyContentRepository.Get(obj.ImdbId); + item = await EmbyContentRepository.GetByImdbId(obj.ImdbId); } if (item == null) { if (obj.TheMovieDbId.HasValue()) { - item = await EmbyContentRepository.Get(obj.TheMovieDbId); + item = await EmbyContentRepository.GetByTheMovieDbId(obj.TheMovieDbId); } if (item == null) { if (obj.TheTvDbId.HasValue()) { - item = await EmbyContentRepository.Get(obj.TheTvDbId); + item = await EmbyContentRepository.GetByTvDbId(obj.TheTvDbId); } } } @@ -44,6 +46,7 @@ namespace Ombi.Core.Rule.Rules.Search if (item != null) { obj.Available = true; + obj.EmbyUrl = item.Url; if (obj.Type == RequestType.TvShow) { @@ -58,10 +61,19 @@ namespace Ombi.Core.Rule.Rules.Search { EmbyEpisode epExists = null; - epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && - x.Series.ProviderId == item.ProviderId.ToString()); - + if (item.HasImdb) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.ImdbId == item.ImdbId); + } if (item.HasTvDb && epExists == null) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.Series.TvDbId == item.TvDbId); + } if (item.HasTheMovieDb && epExists == null) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.TheMovieDbId == item.TheMovieDbId); + } if (epExists != null) { diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index 2118e2a96..965fcdfaf 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -11,32 +11,35 @@ namespace Ombi.Core.Rule.Rules.Search { public class ExistingRule : BaseSearchRule, IRules { - public ExistingRule(IMovieRequestRepository movie, ITvRequestRepository tv) + public ExistingRule(IMovieRequestRepository movie, ITvRequestRepository tv, IMusicRequestRepository music) { Movie = movie; Tv = tv; + Music = music; } private IMovieRequestRepository Movie { get; } + private IMusicRequestRepository Music { get; } private ITvRequestRepository Tv { get; } - public Task Execute(SearchViewModel obj) + public async Task Execute(SearchViewModel obj) { if (obj.Type == RequestType.Movie) { - var movieRequests = Movie.GetRequest(obj.Id); + var movieRequests = await Movie.GetRequestAsync(obj.Id); if (movieRequests != null) // Do we already have a request for this? { obj.Requested = true; + obj.RequestId = movieRequests.Id; obj.Approved = movieRequests.Approved; obj.Available = movieRequests.Available; - return Task.FromResult(Success()); + return Success(); } - return Task.FromResult(Success()); + return Success(); } - else + if (obj.Type == RequestType.TvShow) { //var tvRequests = Tv.GetRequest(obj.Id); //if (tvRequests != null) // Do we already have a request for this? @@ -49,7 +52,7 @@ namespace Ombi.Core.Rule.Rules.Search // return Task.FromResult(Success()); //} - var request = (SearchTvShowViewModel) obj; + var request = (SearchTvShowViewModel)obj; var tvRequests = Tv.GetRequest(obj.Id); if (tvRequests != null) // Do we already have a request for this? { @@ -67,6 +70,7 @@ namespace Ombi.Core.Rule.Rules.Search existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber); if (existingSeason == null) continue; + foreach (var ep in existingSeason.Episodes) { // Find the episode from what we are searching @@ -92,9 +96,24 @@ namespace Ombi.Core.Rule.Rules.Search request.PartlyAvailable = true; } + return Success(); + } + if (obj.Type == RequestType.Album) + { + var album = (SearchAlbumViewModel) obj; + var albumRequest = await Music.GetRequestAsync(album.ForeignAlbumId); + if (albumRequest != null) // Do we already have a request for this? + { + obj.Requested = true; + obj.RequestId = albumRequest.Id; + obj.Approved = albumRequest.Approved; + obj.Available = albumRequest.Available; - return Task.FromResult(Success()); + return Success(); + } + return Success(); } + return Success(); } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs new file mode 100644 index 000000000..97a27d47f --- /dev/null +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Core.Models.Search; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; +using Ombi.Store.Repository; + +namespace Ombi.Core.Rule.Rules.Search +{ + public class LidarrAlbumCacheRule : SpecificRule, ISpecificRule + { + public LidarrAlbumCacheRule(IRepository db) + { + _db = db; + } + + private readonly IRepository _db; + + public Task Execute(object objec) + { + var obj = (SearchAlbumViewModel) objec; + // Check if it's in Lidarr + var result = _db.GetAll().FirstOrDefault(x => x.ForeignAlbumId.Equals(obj.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase)); + if (result != null) + { + obj.PercentOfTracks = result.PercentOfTracks; + obj.Monitored = true; // It's in Lidarr so it's monitored + } + + return Task.FromResult(Success()); + } + + public override SpecificRules Rule => SpecificRules.LidarrAlbum; + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs new file mode 100644 index 000000000..db472a951 --- /dev/null +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Core.Models.Search; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; +using Ombi.Store.Repository; + +namespace Ombi.Core.Rule.Rules.Search +{ + public class LidarrArtistCacheRule : SpecificRule, ISpecificRule + { + public LidarrArtistCacheRule(IRepository db) + { + _db = db; + } + + private readonly IRepository _db; + + public Task Execute(object objec) + { + var obj = (SearchArtistViewModel) objec; + // Check if it's in Lidarr + var result = _db.GetAll().FirstOrDefault(x => x.ForeignArtistId.Equals(obj.ForignArtistId, StringComparison.InvariantCultureIgnoreCase)); + if (result != null) + { + obj.Monitored = true; // It's in Lidarr so it's monitored + } + + return Task.FromResult(Success()); + } + + public override SpecificRules Rule => SpecificRules.LidarrArtist; + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs index 50ac607cb..3f9e2f159 100644 --- a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs +++ b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs @@ -42,6 +42,13 @@ namespace Ombi.Core.Rule.Rules.Specific sendNotification = !await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.AutoApproveTv); } } + else if (req.RequestType == RequestType.Album) + { + if (settings.DoNotSendNotificationsForAutoApprove) + { + sendNotification = !await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.AutoApproveMusic); + } + } if (await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.Admin)) { diff --git a/src/Ombi.Core/Senders/IMusicSender.cs b/src/Ombi.Core/Senders/IMusicSender.cs new file mode 100644 index 000000000..abeec5c29 --- /dev/null +++ b/src/Ombi.Core/Senders/IMusicSender.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Core.Senders +{ + public interface IMusicSender + { + Task Send(AlbumRequest model); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Senders/INotificationHelper.cs b/src/Ombi.Core/Senders/INotificationHelper.cs index efc45020c..4ba47d761 100644 --- a/src/Ombi.Core/Senders/INotificationHelper.cs +++ b/src/Ombi.Core/Senders/INotificationHelper.cs @@ -8,7 +8,9 @@ namespace Ombi.Core { void NewRequest(FullBaseRequest model); void NewRequest(ChildRequests model); + void NewRequest(AlbumRequest model); void Notify(MovieRequests model, NotificationType type); void Notify(ChildRequests model, NotificationType type); + void Notify(AlbumRequest model, NotificationType type); } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index e57a5bf2a..8b89ef7bb 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.CouchPotato; using Ombi.Api.DogNzb.Models; @@ -9,6 +10,8 @@ using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Entities.Requests; using Ombi.Api.DogNzb; +using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.Senders { @@ -16,7 +19,7 @@ namespace Ombi.Core.Senders { public MovieSender(ISettingsService radarrSettings, IRadarrApi api, ILogger log, ISettingsService dogSettings, IDogNzbApi dogApi, ISettingsService cpSettings, - ICouchPotatoApi cpApi) + ICouchPotatoApi cpApi, IRepository userProfiles) { RadarrSettings = radarrSettings; RadarrApi = api; @@ -25,6 +28,7 @@ namespace Ombi.Core.Senders DogNzbApi = dogApi; CouchPotatoSettings = cpSettings; CouchPotatoApi = cpApi; + _userProfiles = userProfiles; } private ISettingsService RadarrSettings { get; } @@ -34,6 +38,7 @@ namespace Ombi.Core.Senders private ISettingsService DogNzbSettings { get; } private ISettingsService CouchPotatoSettings { get; } private ICouchPotatoApi CouchPotatoApi { get; } + private readonly IRepository _userProfiles; public async Task Send(MovieRequests model) { @@ -88,13 +93,33 @@ namespace Ombi.Core.Senders private async Task SendToRadarr(MovieRequests model, RadarrSettings settings) { + var qualityToUse = int.Parse(settings.DefaultQualityProfile); + + var rootFolderPath = settings.DefaultRootPath; + + var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); + if (profiles != null) + { + if (profiles.SonarrRootPathAnime > 0) + { + rootFolderPath = await RadarrRootPath(profiles.SonarrRootPathAnime, settings); + } + if (profiles.SonarrQualityProfileAnime > 0) + { + qualityToUse = profiles.SonarrQualityProfileAnime; + } + } + + // Overrides on the request take priority if (model.QualityOverride > 0) { qualityToUse = model.QualityOverride; } - - var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings); + if (model.RootPathOverride > 0) + { + rootFolderPath = await RadarrRootPath(model.RootPathOverride, settings); + } // Check if the movie already exists? Since it could be unmonitored var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri); @@ -123,7 +148,10 @@ namespace Ombi.Core.Senders existingMovie.monitored = true; await RadarrApi.UpdateMovie(existingMovie, settings.ApiKey, settings.FullUri); // Search for it - await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri); + if (!settings.AddOnly) + { + await RadarrApi.MovieSearch(new[] {existingMovie.id}, settings.ApiKey, settings.FullUri); + } return new SenderResult { Success = true, Sent = true }; } diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs new file mode 100644 index 000000000..9e2dab1bd --- /dev/null +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Api.Lidarr; +using Ombi.Api.Lidarr.Models; +using Ombi.Api.Radarr; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities.Requests; +using Serilog; + +namespace Ombi.Core.Senders +{ + public class MusicSender : IMusicSender + { + public MusicSender(ISettingsService lidarr, ILidarrApi lidarrApi) + { + _lidarrSettings = lidarr; + _lidarrApi = lidarrApi; + } + + private readonly ISettingsService _lidarrSettings; + private readonly ILidarrApi _lidarrApi; + + public async Task Send(AlbumRequest model) + { + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await SendToLidarr(model, settings); + } + + return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" }; + } + + private async Task SendToLidarr(AlbumRequest model, LidarrSettings settings) + { + var qualityToUse = int.Parse(settings.DefaultQualityProfile); + //if (model.QualityOverride > 0) + //{ + // qualityToUse = model.QualityOverride; + //} + + var rootFolderPath = /*model.RootPathOverride <= 0 ?*/ settings.DefaultRootPath /*: await RadarrRootPath(model.RootPathOverride, settings)*/; + + // Need to get the artist + var artist = await _lidarrApi.GetArtistByForeignId(model.ForeignArtistId, settings.ApiKey, settings.FullUri); + + if (artist == null || artist.id <= 0) + { + // Create artist + var newArtist = new ArtistAdd + { + foreignArtistId = model.ForeignArtistId, + addOptions = new Addoptions + { + monitored = true, + searchForMissingAlbums = false, + selectedOption = 6, // None + AlbumsToMonitor = new[] {model.ForeignAlbumId} + }, + added = DateTime.Now, + monitored = true, + albumFolder = settings.AlbumFolder, + artistName = model.ArtistName, + cleanName = model.ArtistName.ToLowerInvariant().RemoveSpaces(), + images = new Image[] { }, + languageProfileId = settings.LanguageProfileId, + links = new Link[] {}, + metadataProfileId = settings.MetadataProfileId, + qualityProfileId = qualityToUse, + rootFolderPath = rootFolderPath, + }; + + var result = await _lidarrApi.AddArtist(newArtist, settings.ApiKey, settings.FullUri); + if (result != null && result.id > 0) + { + // Search for it + if (!settings.AddOnly) + { + await _lidarrApi.AlbumSearch(new[] { result.id }, settings.ApiKey, settings.FullUri); + } + return new SenderResult { Message = "Album has been requested!", Sent = true, Success = true }; + } + } + else + { + SenderResult result = await SetupAlbum(model, artist, settings); + return result; + } + + return new SenderResult { Success = false, Sent = false, Message = "Album is already monitored" }; + } + + private async Task SetupAlbum(AlbumRequest model, ArtistResult artist, LidarrSettings settings) + { + // Get the album id + var albums = await _lidarrApi.GetAllAlbumsByArtistId(artist.id, settings.ApiKey, settings.FullUri); + var album = albums.FirstOrDefault(x => + x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase)); + var maxRetryCount = 10; // 5 seconds + var currentRetry = 0; + while (!albums.Any() || album == null) + { + if (currentRetry >= maxRetryCount) + { + break; + } + currentRetry++; + await Task.Delay(500); + albums = await _lidarrApi.GetAllAlbumsByArtistId(artist.id, settings.ApiKey, settings.FullUri); + album = albums.FirstOrDefault(x => + x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase)); + } + // Get the album we want. + + if (album == null) + { + return new SenderResult { Message = "Could not find album in Lidarr", Sent = false, Success = false }; + } + + var result = await _lidarrApi.MontiorAlbum(album.id, settings.ApiKey, settings.FullUri); + if (!settings.AddOnly) + { + await _lidarrApi.AlbumSearch(new[] {result.id}, settings.ApiKey, settings.FullUri); + } + if (result.monitored) + { + return new SenderResult { Message = "Album has been requested!", Sent = true, Success = true}; + } + return new SenderResult { Message = "Could not set album to monitored", Sent = false, Success = false }; + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index cdf834fa5..e48e54c1a 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.DogNzb; using Ombi.Api.DogNzb.Models; @@ -12,7 +13,9 @@ using Ombi.Api.Sonarr.Models; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; namespace Ombi.Core.Senders { @@ -20,7 +23,7 @@ namespace Ombi.Core.Senders { public TvSender(ISonarrApi sonarrApi, ILogger log, ISettingsService sonarrSettings, ISettingsService dog, IDogNzbApi dogApi, ISettingsService srSettings, - ISickRageApi srApi) + ISickRageApi srApi, IRepository userProfiles) { SonarrApi = sonarrApi; Logger = log; @@ -29,6 +32,7 @@ namespace Ombi.Core.Senders DogNzbApi = dogApi; SickRageSettings = srSettings; SickRageApi = srApi; + UserQualityProfiles = userProfiles; } private ISonarrApi SonarrApi { get; } @@ -38,6 +42,7 @@ namespace Ombi.Core.Senders private ISettingsService SonarrSettings { get; } private ISettingsService DogNzbSettings { get; } private ISettingsService SickRageSettings { get; } + private IRepository UserQualityProfiles { get; } public async Task Send(ChildRequests model) { @@ -118,17 +123,58 @@ namespace Ombi.Core.Senders return null; } - int.TryParse(s.QualityProfile, out var qualityToUse); + int qualityToUse; + string rootFolderPath; + string seriesType; + var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); + + if (model.SeriesType == SeriesType.Anime) + { + // Get the root path from the rootfolder selected. + // For some reason, if we haven't got one use the first root folder in Sonarr + rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPathAnime), s); + int.TryParse(s.QualityProfileAnime, out qualityToUse); + if (profiles != null) + { + if (profiles.SonarrRootPathAnime > 0) + { + rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s); + } + if (profiles.SonarrQualityProfileAnime > 0) + { + qualityToUse = profiles.SonarrQualityProfileAnime; + } + } + seriesType = "anime"; + + } + else + { + int.TryParse(s.QualityProfile, out qualityToUse); + // Get the root path from the rootfolder selected. + // For some reason, if we haven't got one use the first root folder in Sonarr + rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s); + if (profiles != null) + { + if (profiles.SonarrRootPath > 0) + { + rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s); + } + if (profiles.SonarrQualityProfile > 0) + { + qualityToUse = profiles.SonarrQualityProfile; + } + } + seriesType = "standard"; + } + + // Overrides on the request take priority if (model.ParentRequest.QualityOverride.HasValue) { qualityToUse = model.ParentRequest.QualityOverride.Value; } - - // Get the root path from the rootfolder selected. - // For some reason, if we haven't got one use the first root folder in Sonarr - // TODO make this overrideable via the UI - var rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s); + try { // Does the series actually exist? @@ -149,27 +195,18 @@ namespace Ombi.Core.Senders rootFolderPath = rootFolderPath, qualityProfileId = qualityToUse, titleSlug = model.ParentRequest.Title, + seriesType = seriesType, addOptions = new AddOptions { - ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season - ignoreEpisodesWithoutFiles = true, // We want all missing + ignoreEpisodesWithFiles = false, // There shouldn't be any episodes with files, this is a new season + ignoreEpisodesWithoutFiles = false, // We want all missing searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly. } }; // Montitor the correct seasons, // If we have that season in the model then it's monitored! - var seasonsToAdd = new List(); - for (var i = 1; i < model.ParentRequest.TotalSeasons + 1; i++) - { - var index = i; - var season = new Season - { - seasonNumber = i, - monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index) - }; - seasonsToAdd.Add(season); - } + var seasonsToAdd = GetSeasonsToCreate(model); newSeries.seasons = seasonsToAdd; var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri); existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri); @@ -223,7 +260,7 @@ namespace Ombi.Core.Senders { var sonarrEp = sonarrEpList.FirstOrDefault(x => x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber); - if (sonarrEp != null) + if (sonarrEp != null && !sonarrEp.monitored) { sonarrEp.monitored = true; episodesToUpdate.Add(sonarrEp); @@ -231,22 +268,64 @@ namespace Ombi.Core.Senders } } var seriesChanges = false; + foreach (var season in model.SeasonRequests) { var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); var sonarrEpCount = sonarrSeason.Count(); var ourRequestCount = season.Episodes.Count; + var existingSeason = + result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); + if (existingSeason == null) + { + Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title); + continue; + } + + if (sonarrEpCount == ourRequestCount) { // We have the same amount of requests as all of the episodes in the season. - var existingSeason = - result.seasons.First(x => x.seasonNumber == season.SeasonNumber); - existingSeason.monitored = true; - seriesChanges = true; + + if (!existingSeason.monitored) + { + existingSeason.monitored = true; + seriesChanges = true; + } } else { + // Make sure this season is set to monitored + if (!existingSeason.monitored) + { + // We need to monitor it, problem being is all episodes will now be monitored + // So we need to monior the series but unmonitor every episode + // Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand) + existingSeason.monitored = true; + var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber); + sea.monitored = true; + //var previouslyMonitoredEpisodes = sonarrEpList.Where(x => + // x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this + result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri); + var epToUnmonitor = new List(); + var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member + foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList()) + { + //if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber)) + //{ + // // This was previously monitored. + // continue; + //} + ep.monitored = false; + epToUnmonitor.Add(ep); + } + + foreach (var epToUpdate in epToUnmonitor) + { + await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); + } + } // Now update the episodes that need updating foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber)) { @@ -266,6 +345,24 @@ namespace Ombi.Core.Senders } } + private static List GetSeasonsToCreate(ChildRequests model) + { + // Let's get a list of seasons just incase we need to change it + var seasonsToUpdate = new List(); + for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++) + { + var index = i; + var sea = new Season + { + seasonNumber = i, + monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0) + }; + seasonsToUpdate.Add(sea); + } + + return seasonsToUpdate; + } + private async Task SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null) { var tvdbid = model.ParentRequest.TvDbId; @@ -300,9 +397,17 @@ namespace Ombi.Core.Senders foreach (var seasonRequests in model.SeasonRequests) { var srEpisodes = await SickRageApi.GetEpisodesForSeason(tvdbid, seasonRequests.SeasonNumber, settings.ApiKey, settings.FullUri); - while (srEpisodes.message.Equals("Show not found", StringComparison.CurrentCultureIgnoreCase) && srEpisodes.data.Count <= 0) + int retryTimes = 10; + var currentRetry = 0; + while (srEpisodes.message.Equals("Show not found", StringComparison.CurrentCultureIgnoreCase) || srEpisodes.message.Equals("Season not found", StringComparison.CurrentCultureIgnoreCase) && srEpisodes.data.Count <= 0) { + if (currentRetry > retryTimes) + { + Logger.LogWarning("Couldnt find the SR Season or Show, message: {0}", srEpisodes.message); + break; + } await Task.Delay(TimeSpan.FromSeconds(1)); + currentRetry++; srEpisodes = await SickRageApi.GetEpisodesForSeason(tvdbid, seasonRequests.SeasonNumber, settings.ApiKey, settings.FullUri); } diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 92ecf8282..3af06b476 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -32,6 +32,7 @@ using Ombi.Api.CouchPotato; using Ombi.Api.DogNzb; using Ombi.Api.FanartTv; using Ombi.Api.Github; +using Ombi.Api.Lidarr; using Ombi.Api.Mattermost; using Ombi.Api.Notifications; using Ombi.Api.Pushbullet; @@ -51,10 +52,13 @@ using Ombi.Store.Repository.Requests; using Ombi.Updater; using PlexContentCacher = Ombi.Schedule.Jobs.Plex; using Ombi.Api.Telegram; +using Ombi.Core.Authentication; using Ombi.Core.Processor; +using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Processor; +using Ombi.Store.Entities; namespace Ombi.DependencyInjection { @@ -78,10 +82,15 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterHttp(this IServiceCollection services) { @@ -114,6 +123,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterStore(this IServiceCollection services) { @@ -128,6 +138,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -150,6 +161,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); } @@ -175,6 +187,11 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); } } } diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 75f4385bb..028c37b43 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + @@ -21,6 +21,7 @@ + diff --git a/src/Ombi.Helpers/CacheKeys.cs b/src/Ombi.Helpers/CacheKeys.cs index e6c482f7b..f7a40d321 100644 --- a/src/Ombi.Helpers/CacheKeys.cs +++ b/src/Ombi.Helpers/CacheKeys.cs @@ -18,6 +18,8 @@ namespace Ombi.Helpers public const string NowPlayingMovies = nameof(NowPlayingMovies); public const string RadarrRootProfiles = nameof(RadarrRootProfiles); public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles); + public const string LidarrRootFolders = nameof(LidarrRootFolders); + public const string LidarrQualityProfiles = nameof(LidarrQualityProfiles); public const string FanartTv = nameof(FanartTv); } } diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs new file mode 100644 index 000000000..567bcfe7e --- /dev/null +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -0,0 +1,17 @@ +using System; +using System.Globalization; +using System.Collections.Generic; +using System.Text; + +namespace Ombi.Helpers +{ + public class EmbyHelper + { + public static string GetEmbyMediaUrl(string mediaId) + { + var url = + $"http://app.emby.media/#!/itemdetails.html?id={mediaId}"; + return url; + } + } +} diff --git a/src/Ombi.Helpers/LinqHelpers.cs b/src/Ombi.Helpers/LinqHelpers.cs index 279d161b7..67fdb5c53 100644 --- a/src/Ombi.Helpers/LinqHelpers.cs +++ b/src/Ombi.Helpers/LinqHelpers.cs @@ -14,5 +14,12 @@ namespace Ombi.Helpers yield return source1; } } + + public static HashSet ToHashSet( + this IEnumerable source, + IEqualityComparer comparer = null) + { + return new HashSet(source, comparer); + } } } \ No newline at end of file diff --git a/src/Ombi.Helpers/LoggingEvents.cs b/src/Ombi.Helpers/LoggingEvents.cs index 39c309102..40ec3fd2b 100644 --- a/src/Ombi.Helpers/LoggingEvents.cs +++ b/src/Ombi.Helpers/LoggingEvents.cs @@ -20,6 +20,7 @@ namespace Ombi.Helpers public static EventId CouchPotatoCacher => new EventId(2007); public static EventId PlexContentCacher => new EventId(2008); public static EventId SickRageCacher => new EventId(2009); + public static EventId LidarrArtistCache => new EventId(2010); public static EventId MovieSender => new EventId(3000); diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 00cd8d5e9..2aaaa076f 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/OmbiRoles.cs b/src/Ombi.Helpers/OmbiRoles.cs index e7527279d..1d584d57f 100644 --- a/src/Ombi.Helpers/OmbiRoles.cs +++ b/src/Ombi.Helpers/OmbiRoles.cs @@ -2,13 +2,17 @@ { public static class OmbiRoles { + // DONT FORGET TO ADD TO IDENTITYCONTROLLER.CREATEROLES AND THE UI! + public const string Admin = nameof(Admin); public const string AutoApproveMovie = nameof(AutoApproveMovie); public const string AutoApproveTv = nameof(AutoApproveTv); + public const string AutoApproveMusic = nameof(AutoApproveMusic); public const string PowerUser = nameof(PowerUser); public const string RequestTv = nameof(RequestTv); public const string RequestMovie = nameof(RequestMovie); + public const string RequestMusic = nameof(RequestMusic); public const string Disabled = nameof(Disabled); - public const string RecievesNewsletter = nameof(RecievesNewsletter); + public const string ReceivesNewsletter = nameof(ReceivesNewsletter); } } \ No newline at end of file diff --git a/src/Ombi.Helpers/PosterPathHelper.cs b/src/Ombi.Helpers/PosterPathHelper.cs index 7de767e44..ed1dee5c0 100644 --- a/src/Ombi.Helpers/PosterPathHelper.cs +++ b/src/Ombi.Helpers/PosterPathHelper.cs @@ -18,5 +18,19 @@ namespace Ombi.Helpers return poster; } + + public static string FixBackgroundPath(string background) + { + // https://image.tmdb.org/t/p/w1280/fJAvGOitU8y53ByeHnM4avtKFaG.jpg + + if (background.Contains("image.tmdb.org", CompareOptions.IgnoreCase)) + { + // Somehow we have a full path here for the poster, we only want the last segment + var backgroundSegments = background.Split('/'); + return backgroundSegments.Last(); + } + + return background; + } } } \ No newline at end of file diff --git a/src/Ombi.Helpers/StringHelper.cs b/src/Ombi.Helpers/StringHelper.cs index daa8cabe7..c198301fc 100644 --- a/src/Ombi.Helpers/StringHelper.cs +++ b/src/Ombi.Helpers/StringHelper.cs @@ -65,5 +65,24 @@ namespace Ombi.Helpers securePassword.MakeReadOnly(); return securePassword; } + + public static int IntParseLinq(string stringIn) + { + if (int.TryParse(stringIn, out var result)) + { + return result; + } + + return -1; + } + + public static string RemoveSpaces(this string str) + { + return str.Replace(" ", ""); + } + public static string StripCharacters(this string str, params char[] chars) + { + return string.Concat(str.Where(c => !chars.Contains(c))); + } } } \ No newline at end of file diff --git a/src/Ombi.Helpers/UriHelper.cs b/src/Ombi.Helpers/UriHelper.cs index 83cd27e9d..6ec8047ae 100644 --- a/src/Ombi.Helpers/UriHelper.cs +++ b/src/Ombi.Helpers/UriHelper.cs @@ -112,6 +112,7 @@ namespace Ombi.Helpers return uriBuilder.Uri; } + } public class ApplicationSettingsException : Exception diff --git a/src/Ombi.Mapping/Ombi.Mapping.csproj b/src/Ombi.Mapping/Ombi.Mapping.csproj index 78f39bcd5..b6f602886 100644 --- a/src/Ombi.Mapping/Ombi.Mapping.csproj +++ b/src/Ombi.Mapping/Ombi.Mapping.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Mapping/Profiles/MovieProfile.cs b/src/Ombi.Mapping/Profiles/MovieProfile.cs index d7e43fc50..39961b785 100644 --- a/src/Ombi.Mapping/Profiles/MovieProfile.cs +++ b/src/Ombi.Mapping/Profiles/MovieProfile.cs @@ -24,6 +24,19 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average)) .ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count)); + CreateMap() + .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path)) + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.OriginalLanguage, opts => opts.MapFrom(src => src.original_language)) + .ForMember(dest => dest.OriginalName, opts => opts.MapFrom(src => src.original_name)) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.overview)) + .ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity)) + .ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path)) + .ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.first_air_date)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average)) + .ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count)); + CreateMap() .ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult)) .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path)) @@ -55,7 +68,7 @@ namespace Ombi.Mapping.Profiles .ForMember(x => x.ReleaseDate, o => o.MapFrom(s => s.release_date)) .ForMember(x => x.Type, o => o.MapFrom(s => s.Type)); - CreateMap(); + CreateMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); diff --git a/src/Ombi.Notifications.Templates/TemplateBase.cs b/src/Ombi.Notifications.Templates/TemplateBase.cs index 9b9207c9a..ccabfa65c 100644 --- a/src/Ombi.Notifications.Templates/TemplateBase.cs +++ b/src/Ombi.Notifications.Templates/TemplateBase.cs @@ -3,6 +3,6 @@ public abstract class TemplateBase { public abstract string TemplateLocation { get; } - public virtual string OmbiLogo => "http://i.imgur.com/qQsN78U.png"; + public virtual string OmbiLogo => "http://i.imgur.com/7pqVq7W.png"; } } \ No newline at end of file diff --git a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html index 464e3463b..450e7df2a 100644 --- a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html +++ b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html @@ -4,118 +4,140 @@ Ombi - - - + +
- - "); } protected bool ValidateConfiguration(EmailNotificationSettings settings) diff --git a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs index da3b3305c..72f0ef6f5 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs @@ -72,7 +72,7 @@ namespace Ombi.Schedule.Jobs.Ombi Logger.LogDebug(LoggingEvents.Updater, "Starting Update job"); var settings = await Settings.GetSettingsAsync(); - if (!settings.AutoUpdateEnabled) + if (!settings.AutoUpdateEnabled && !settings.TestMode) { Logger.LogDebug(LoggingEvents.Updater, "Auto update is not enabled"); return; @@ -83,7 +83,7 @@ namespace Ombi.Schedule.Jobs.Ombi var productVersion = AssemblyHelper.GetRuntimeVersion(); Logger.LogDebug(LoggingEvents.Updater, "Product Version {0}", productVersion); - + var serverVersion = string.Empty; try { var productArray = GetVersion(); @@ -96,13 +96,17 @@ namespace Ombi.Schedule.Jobs.Ombi Logger.LogDebug(LoggingEvents.Updater, "Branch {0}", branch); Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now"); + //TODO this fails because the branch = featureupdater when it should be feature/updater var updates = await Processor.Process(branch); Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates); - var serverVersion = updates.UpdateVersionString; + + + serverVersion = updates.UpdateVersionString; Logger.LogDebug(LoggingEvents.Updater, "Service Version {0}", updates.UpdateVersionString); - if (!serverVersion.Equals(version, StringComparison.CurrentCultureIgnoreCase)) + + if (!serverVersion.Equals(version, StringComparison.CurrentCultureIgnoreCase) || settings.TestMode) { // Let's download the correct zip var desc = RuntimeInformation.OSDescription; @@ -135,7 +139,8 @@ namespace Ombi.Schedule.Jobs.Ombi if (process == Architecture.Arm) { download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm.", CompareOptions.IgnoreCase)); - } else if (process == Architecture.Arm64) + } + else if (process == Architecture.Arm64) { download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm64.", CompareOptions.IgnoreCase)); } @@ -206,32 +211,34 @@ namespace Ombi.Schedule.Jobs.Ombi updaterExtension = ".exe"; } var updaterFile = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), - "TempUpdate", $"Ombi.Updater{updaterExtension}"); + "TempUpdate", "updater", $"Ombi.Updater{updaterExtension}"); // Make sure the file is an executable - ExecLinuxCommand($"chmod +x {updaterFile}"); + //ExecLinuxCommand($"chmod +x {updaterFile}"); + // There must be an update var start = new ProcessStartInfo { - UseShellExecute = true, - CreateNoWindow = false, // Ignored if UseShellExecute is set to true + UseShellExecute = false, + CreateNoWindow = true, // Ignored if UseShellExecute is set to true FileName = updaterFile, Arguments = GetArgs(settings), WorkingDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "TempUpdate"), }; - if (settings.Username.HasValue()) + //if (settings.Username.HasValue()) + //{ + // start.UserName = settings.Username; + //} + //if (settings.Password.HasValue()) + //{ + // start.Password = settings.Password.ToSecureString(); + //} + using (var proc = new Process { StartInfo = start }) { - start.UserName = settings.Username; + proc.Start(); } - if (settings.Password.HasValue()) - { - start.Password = settings.Password.ToSecureString(); - } - var proc = new Process { StartInfo = start }; - - proc.Start(); Logger.LogDebug(LoggingEvents.Updater, "Bye bye"); } @@ -254,10 +261,10 @@ namespace Ombi.Schedule.Jobs.Ombi var sb = new StringBuilder(); sb.Append($"--applicationPath \"{currentLocation}\" --processname \"{processName}\" "); - if (settings.WindowsService) - { - sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" "); - } + //if (settings.WindowsService) + //{ + // sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" "); + //} var sb2 = new StringBuilder(); if (url?.Value.HasValue() ?? false) { diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index 225efb7d3..d58c29ddc 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Hangfire; using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; @@ -8,9 +10,10 @@ using Ombi.Api.TvMaze; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Plex; using Ombi.Store.Entities; using Ombi.Store.Repository; -using Ombi.Store.Repository.Requests; namespace Ombi.Schedule.Jobs.Ombi { @@ -18,7 +21,7 @@ namespace Ombi.Schedule.Jobs.Ombi { public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, ILogger log, ITvMazeApi tvApi, ISettingsService plexSettings, - IMovieDbApi movieApi) + IMovieDbApi movieApi, ISettingsService embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability) { _plexRepo = plexRepo; _embyRepo = embyRepo; @@ -26,14 +29,20 @@ namespace Ombi.Schedule.Jobs.Ombi _movieApi = movieApi; _tvApi = tvApi; _plexSettings = plexSettings; + _embySettings = embySettings; + _plexAvailabilityChecker = plexAvailability; + _embyAvaliabilityChecker = embyAvaliability; } private readonly IPlexContentRepository _plexRepo; private readonly IEmbyContentRepository _embyRepo; + private readonly IPlexAvailabilityChecker _plexAvailabilityChecker; + private readonly IEmbyAvaliabilityChecker _embyAvaliabilityChecker; private readonly ILogger _log; private readonly IMovieDbApi _movieApi; private readonly ITvMazeApi _tvApi; private readonly ISettingsService _plexSettings; + private readonly ISettingsService _embySettings; public async Task Start() { @@ -45,6 +54,12 @@ namespace Ombi.Schedule.Jobs.Ombi { await StartPlex(); } + + var embySettings = await _embySettings.GetSettingsAsync(); + if (embySettings.Enable) + { + await StartEmby(); + } } catch (Exception e) { @@ -53,18 +68,69 @@ namespace Ombi.Schedule.Jobs.Ombi } } - private async Task StartPlex() + public async Task ProcessPlexServerContent(IEnumerable contentIds) { - await StartPlexMovies(); + _log.LogInformation("Starting the Metadata refresh from RecentlyAddedSync"); + var plexSettings = await _plexSettings.GetSettingsAsync(); + var embySettings = await _embySettings.GetSettingsAsync(); + try + { + if (plexSettings.Enable) + { + await StartPlexWithKnownContent(contentIds); + } + } + catch (Exception e) + { + _log.LogError(e, "Exception when refreshing the Plex Metadata"); + throw; + } + finally + { + if (plexSettings.Enable) + { + BackgroundJob.Enqueue(() => _plexAvailabilityChecker.Start()); + } + + if (embySettings.Enable) + { + BackgroundJob.Enqueue(() => _embyAvaliabilityChecker.Start()); + + } + } + } + + private async Task StartPlexWithKnownContent(IEnumerable contentids) + { + var everything = _plexRepo.GetAll().Where(x => contentids.Contains(x.Id)); + var allMovies = everything.Where(x => x.Type == PlexMediaTypeEntity.Movie); + await StartPlexMovies(allMovies); // Now Tv - await StartPlexTv(); + var allTv = everything.Where(x => x.Type == PlexMediaTypeEntity.Show); + await StartPlexTv(allTv); } - private async Task StartPlexTv() + private async Task StartPlex() { + var allMovies = _plexRepo.GetAll().Where(x => + x.Type == PlexMediaTypeEntity.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); + await StartPlexMovies(allMovies); + + // Now Tv var allTv = _plexRepo.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Show && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue())); + await StartPlexTv(allTv); + } + + private async Task StartEmby() + { + await StartEmbyMovies(); + await StartEmbyTv(); + } + + private async Task StartPlexTv(IQueryable allTv) + { var tvCount = 0; foreach (var show in allTv) { @@ -74,7 +140,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!hasTheMovieDb) { - var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title); + var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title, false); show.TheMovieDbId = id; } @@ -101,10 +167,48 @@ namespace Ombi.Schedule.Jobs.Ombi await _plexRepo.SaveChangesAsync(); } - private async Task StartPlexMovies() + private async Task StartEmbyTv() + { + var allTv = _embyRepo.GetAll().Where(x => + x.Type == EmbyMediaType.Series && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue())); + var tvCount = 0; + foreach (var show in allTv) + { + var hasImdb = show.ImdbId.HasValue(); + var hasTheMovieDb = show.TheMovieDbId.HasValue(); + var hasTvDbId = show.TvDbId.HasValue(); + + if (!hasTheMovieDb) + { + var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title, false); + show.TheMovieDbId = id; + } + + if (!hasImdb) + { + var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId); + show.ImdbId = id; + _embyRepo.UpdateWithoutSave(show); + } + + if (!hasTvDbId) + { + var id = await GetTvDbId(hasTheMovieDb, hasImdb, show.TheMovieDbId, show.ImdbId, show.Title); + show.TvDbId = id; + _embyRepo.UpdateWithoutSave(show); + } + tvCount++; + if (tvCount >= 20) + { + await _embyRepo.SaveChangesAsync(); + tvCount = 0; + } + } + await _embyRepo.SaveChangesAsync(); + } + + private async Task StartPlexMovies(IQueryable allMovies) { - var allMovies = _plexRepo.GetAll().Where(x => - x.Type == PlexMediaTypeEntity.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); int movieCount = 0; foreach (var movie in allMovies) { @@ -120,7 +224,7 @@ namespace Ombi.Schedule.Jobs.Ombi } if (!hasTheMovieDb) { - var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title); + var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true); movie.TheMovieDbId = id; _plexRepo.UpdateWithoutSave(movie); } @@ -135,7 +239,41 @@ namespace Ombi.Schedule.Jobs.Ombi await _plexRepo.SaveChangesAsync(); } - private async Task GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title) + private async Task StartEmbyMovies() + { + var allMovies = _embyRepo.GetAll().Where(x => + x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); + int movieCount = 0; + foreach (var movie in allMovies) + { + var hasImdb = movie.ImdbId.HasValue(); + var hasTheMovieDb = movie.TheMovieDbId.HasValue(); + // Movies don't really use TheTvDb + + if (!hasImdb) + { + var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); + movie.ImdbId = imdbId; + _embyRepo.UpdateWithoutSave(movie); + } + if (!hasTheMovieDb) + { + var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true); + movie.TheMovieDbId = id; + _embyRepo.UpdateWithoutSave(movie); + } + movieCount++; + if (movieCount >= 20) + { + await _embyRepo.SaveChangesAsync(); + movieCount = 0; + } + } + + await _embyRepo.SaveChangesAsync(); + } + + private async Task GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title, bool movie) { _log.LogInformation("The Media item {0} does not have a TheMovieDbId, searching for TheMovieDbId", title); FindResult result = null; @@ -150,13 +288,29 @@ namespace Ombi.Schedule.Jobs.Ombi if (hasImdb && !hasResult) { result = await _movieApi.Find(imdbId, ExternalSource.imdb_id); - hasResult = result?.tv_results?.Length > 0; + if (movie) + { + hasResult = result?.movie_results?.Length > 0; + } + else + { + hasResult = result?.tv_results?.Length > 0; + + } _log.LogInformation("Setting Show {0} because we have ImdbId, result: {1}", title, hasResult); } if (hasResult) { - return result.tv_results?[0]?.id.ToString() ?? string.Empty; + if (movie) + { + return result.movie_results?[0]?.id.ToString() ?? string.Empty; + } + else + { + + return result.tv_results?[0]?.id.ToString() ?? string.Empty; + } } return string.Empty; } diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs index 3de9749d6..2d115ab88 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Ombi.Schedule.Jobs.Plex { diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs index a9fadae9d..17a8bbb4f 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs @@ -4,6 +4,6 @@ namespace Ombi.Schedule.Jobs { public interface IPlexContentSync : IBaseJob { - Task CacheContent(); + Task CacheContent(bool recentlyAddedSearch = false); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs index 7d97381be..7de7c3c0c 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs @@ -1,10 +1,15 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Ombi.Api.Plex.Models; +using Ombi.Store.Entities; namespace Ombi.Schedule.Jobs.Plex.Interfaces { public interface IPlexEpisodeSync : IBaseJob { Task Start(); + Task> ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs new file mode 100644 index 000000000..34f5562a6 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Plex +{ + public interface IPlexRecentlyAddedSync : IBaseJob + { + void Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs b/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs new file mode 100644 index 000000000..fc46e88b7 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Ombi.Schedule.Jobs.Plex.Models +{ + public class ProcessedContent + { + public IEnumerable Content { get; set; } + public IEnumerable Episodes { get; set; } + + public bool HasProcessedContent => Content?.Any() ?? false; + public bool HasProcessedEpisodes => Episodes?.Any() ?? false; + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 343c4256e..e0278f854 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -4,10 +4,12 @@ using System.Linq; using System.Threading.Tasks; using Hangfire; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; using Ombi.Notifications.Models; using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -16,13 +18,14 @@ namespace Ombi.Schedule.Jobs.Plex public class PlexAvailabilityChecker : IPlexAvailabilityChecker { public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies, - INotificationService notification, IBackgroundJobClient background) + INotificationService notification, IBackgroundJobClient background, ILogger log) { _tvRepo = tvRequest; _repo = repo; _movieRepo = movies; _notificationService = notification; _backgroundJobClient = background; + _log = log; } private readonly ITvRequestRepository _tvRepo; @@ -30,16 +33,29 @@ namespace Ombi.Schedule.Jobs.Plex private readonly IPlexContentRepository _repo; private readonly INotificationService _notificationService; private readonly IBackgroundJobClient _backgroundJobClient; + private readonly ILogger _log; public async Task Start() { - await ProcessMovies(); - await ProcessTv(); + try + { + await ProcessMovies(); + await ProcessTv(); + } + catch (Exception e) + { + _log.LogError(e, "Exception thrown in Plex availbility checker"); + } } - private async Task ProcessTv() + private Task ProcessTv() { var tv = _tvRepo.GetChild().Where(x => !x.Available); + return ProcessTv(tv); + } + + private async Task ProcessTv(IQueryable tv) + { var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series); foreach (var child in tv) @@ -63,11 +79,16 @@ namespace Ombi.Schedule.Jobs.Plex { seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); } - if (useTvDb) + if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()) ) { seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); } + if (seriesEpisodes == null) + { + continue; + } + if (!seriesEpisodes.Any()) { // Let's try and match the series by name @@ -81,6 +102,10 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var episode in season.Episodes) { + if (episode.Available) + { + continue; + } var foundEp = await seriesEpisodes.FirstOrDefaultAsync( x => x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == episode.Season.SeasonNumber); @@ -98,11 +123,12 @@ namespace Ombi.Schedule.Jobs.Plex { // We have fulfulled this request! child.Available = true; + child.MarkedAsAvailable = DateTime.Now; _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, - RequestId = child.ParentRequestId, + RequestId = child.Id, RequestType = RequestType.TvShow, Recipient = child.RequestedUser.Email })); @@ -138,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex } movie.Available = true; + movie.MarkedAsAvailable = DateTime.Now; if (movie.Available) { _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 3c00a7a29..c39b80c1f 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -37,7 +37,9 @@ using Ombi.Api.Plex.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex.Interfaces; +using Ombi.Schedule.Jobs.Plex.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -46,13 +48,15 @@ namespace Ombi.Schedule.Jobs.Plex public class PlexContentSync : IPlexContentSync { public PlexContentSync(ISettingsService plex, IPlexApi plexApi, ILogger logger, IPlexContentRepository repo, - IPlexEpisodeSync epsiodeSync) + IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh, IPlexAvailabilityChecker checker) { Plex = plex; PlexApi = plexApi; Logger = logger; Repo = repo; EpisodeSync = epsiodeSync; + Metadata = metadataRefresh; + Checker = checker; plex.ClearCache(); } @@ -61,8 +65,10 @@ namespace Ombi.Schedule.Jobs.Plex private ILogger Logger { get; } private IPlexContentRepository Repo { get; } private IPlexEpisodeSync EpisodeSync { get; } + private IRefreshMetadata Metadata { get; } + private IPlexAvailabilityChecker Checker { get; } - public async Task CacheContent() + public async Task CacheContent(bool recentlyAddedSearch = false) { var plexSettings = await Plex.GetSettingsAsync(); if (!plexSettings.Enable) @@ -74,233 +80,164 @@ namespace Ombi.Schedule.Jobs.Plex Logger.LogError("Plex Settings are not valid"); return; } - + var processedContent = new ProcessedContent(); Logger.LogInformation("Starting Plex Content Cacher"); try { - await StartTheCache(plexSettings); + if (recentlyAddedSearch) + { + processedContent = await StartTheCache(plexSettings, true); + } + else + { + await StartTheCache(plexSettings, false); + } } catch (Exception e) { Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content"); } - Logger.LogInformation("Starting EP Cacher"); - BackgroundJob.Enqueue(() => EpisodeSync.Start()); + if (!recentlyAddedSearch) + { + Logger.LogInformation("Starting EP Cacher"); + BackgroundJob.Enqueue(() => EpisodeSync.Start()); + } + + if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch) + { + // Just check what we send it + BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); + } + + if ((processedContent?.HasProcessedEpisodes ?? false) && recentlyAddedSearch) + { + BackgroundJob.Enqueue(() => Checker.Start()); + } } - private async Task StartTheCache(PlexSettings plexSettings) + private async Task StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch) { + var processedContent = new ProcessedContent(); foreach (var servers in plexSettings.Servers ?? new List()) { try { Logger.LogInformation("Starting to cache the content on server {0}", servers.Name); - await ProcessServer(servers); + + if (recentlyAddedSearch) + { + // If it's recently added search then we want the results to pass to the metadata job + // This way the metadata job is smaller in size to process, it only need to look at newly added shit + return await ProcessServer(servers, true); + } + else + { + await ProcessServer(servers, false); + } } catch (Exception e) { Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content in server {0}", servers.Name); } } + + return processedContent; } - private async Task ProcessServer(PlexServers servers) + private async Task ProcessServer(PlexServers servers, bool recentlyAddedSearch) { - Logger.LogInformation("Getting all content from server {0}", servers.Name); - var allContent = await GetAllContent(servers); - Logger.LogInformation("We found {0} items", allContent.Count); + var retVal = new ProcessedContent(); + var contentProcessed = new Dictionary(); + var episodesProcessed = new List(); + Logger.LogDebug("Getting all content from server {0}", servers.Name); + var allContent = await GetAllContent(servers, recentlyAddedSearch); + Logger.LogDebug("We found {0} items", allContent.Count); // Let's now process this. var contentToAdd = new HashSet(); + + var allEps = Repo.GetAllEpisodes(); + foreach (var content in allContent) { - if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) + if (content.viewGroup.Equals(PlexMediaType.Episode.ToString(), StringComparison.CurrentCultureIgnoreCase)) { - // Process Shows - Logger.LogInformation("Processing TV Shows"); - foreach (var show in content.Metadata ?? new Metadata[] { }) + Logger.LogDebug("Found some episodes, this must be a recently added sync"); + var count = 0; + foreach (var epInfo in content.Metadata ?? new Metadata[]{}) { - var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri, - show.ratingKey); - var seasonsContent = new List(); - foreach (var season in seasonList.MediaContainer.Metadata) + count++; + var grandParentKey = epInfo.grandparentRatingKey; + // Lookup the rating key + var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, grandParentKey); + var show = showMetadata.MediaContainer.Metadata.FirstOrDefault(); + if (show == null) { - seasonsContent.Add(new PlexSeasonsContent - { - ParentKey = season.parentRatingKey, - SeasonKey = season.ratingKey, - SeasonNumber = season.index, - PlexContentId = show.ratingKey - }); + continue; } - // Do we already have this item? - // Let's try and match - var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title - && x.ReleaseYear == show.year.ToString() - && x.Type == PlexMediaTypeEntity.Show); - - // Just double check the rating key, since this is our unique constraint - var existingKey = await Repo.GetByKey(show.ratingKey); - - if (existingKey != null) + await ProcessTvShow(servers, show, contentToAdd, contentProcessed); + if (contentToAdd.Any()) { - // Damn son. - // Let's check if they match up - var doesMatch = show.title.Equals(existingKey.Title, - StringComparison.CurrentCulture); - if (!doesMatch) - { - // Something fucked up on Plex at somepoint... Damn, rebuild of lib maybe? - // Lets delete the matching key - await Repo.Delete(existingKey); - existingKey = null; - } - } - - if (existingContent != null) - { - // Just check the key - if (existingKey != null) - { - // The rating key is all good! - } - else + await Repo.AddRange(contentToAdd, false); + if (recentlyAddedSearch) { - // This means the rating key has changed somehow. - // Should probably delete this and get the new one - var oldKey = existingContent.Key; - Repo.DeleteWithoutSave(existingContent); - - // Because we have changed the rating key, we need to change all children too - var episodeToChange = Repo.GetAllEpisodes().Where(x => x.GrandparentKey == oldKey); - if (episodeToChange.Any()) + foreach (var plexServerContent in contentToAdd) { - foreach (var e in episodeToChange) - { - Repo.DeleteWithoutSave(e); - } + contentProcessed.Add(plexServerContent.Id, plexServerContent.Key); } - await Repo.SaveChangesAsync(); - existingContent = null; } + contentToAdd.Clear(); } - // The ratingKey keeps changing... - //var existingContent = await Repo.GetByKey(show.ratingKey); - if (existingContent != null) + if (count > 200) { - try - { - Logger.LogInformation("We already have show {0} checking for new seasons", - existingContent.Title); - // Ok so we have it, let's check if there are any new seasons - var itemAdded = false; - foreach (var season in seasonsContent) - { - var seasonExists = - existingContent.Seasons.FirstOrDefault(x => x.SeasonKey == season.SeasonKey); + await Repo.SaveChangesAsync(); - if (seasonExists != null) - { - // We already have this season - continue; - } + } + } - existingContent.Seasons.Add(season); - itemAdded = true; - } + // Save just to make sure we don't leave anything hanging + await Repo.SaveChangesAsync(); + if (content.Metadata != null) + { + var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); + episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); + } + } + if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) + { + // Process Shows + Logger.LogDebug("Processing TV Shows"); + var count = 0; + foreach (var show in content.Metadata ?? new Metadata[] { }) + { + count++; + await ProcessTvShow(servers, show, contentToAdd, contentProcessed); - if (itemAdded) await Repo.Update(existingContent); - } - catch (Exception e) - { - Logger.LogError(LoggingEvents.PlexContentCacher, e, - "Exception when adding new seasons to title {0}", existingContent.Title); - } - } - else + if (contentToAdd.Any()) { - try + await Repo.AddRange(contentToAdd, false); + if (recentlyAddedSearch) { - Logger.LogInformation("New show {0}, so add it", show.title); - - // Get the show metadata... This sucks since the `metadata` var contains all information about the show - // But it does not contain the `guid` property that we need to pull out thetvdb id... - var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, - show.ratingKey); - var providerIds = - PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault() - .guid); - - var item = new PlexServerContent - { - AddedAt = DateTime.Now, - Key = show.ratingKey, - ReleaseYear = show.year.ToString(), - Type = PlexMediaTypeEntity.Show, - Title = show.title, - Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), - Seasons = new List() - }; - if (providerIds.Type == ProviderType.ImdbId) - { - item.ImdbId = providerIds.ImdbId; - } - if (providerIds.Type == ProviderType.TheMovieDbId) - { - item.TheMovieDbId = providerIds.TheMovieDb; - } - if (providerIds.Type == ProviderType.TvDbId) - { - item.TvDbId = providerIds.TheTvDb; - } - - // Let's just double check to make sure we do not have it now we have some id's - var existingImdb = false; - var existingMovieDbId = false; - var existingTvDbId = false; - if (item.ImdbId.HasValue()) - { - existingImdb = await Repo.GetAll().AnyAsync(x => - x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show); - } - if (item.TheMovieDbId.HasValue()) - { - existingMovieDbId = await Repo.GetAll().AnyAsync(x => - x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show); - } - if (item.TvDbId.HasValue()) + foreach (var plexServerContent in contentToAdd) { - existingTvDbId = await Repo.GetAll().AnyAsync(x => - x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show); + contentProcessed.Add(plexServerContent.Id, plexServerContent.Key); } - if (existingImdb || existingTvDbId || existingMovieDbId) - { - // We already have it! - continue; - } - - item.Seasons.ToList().AddRange(seasonsContent); - - contentToAdd.Add(item); - } - catch (Exception e) - { - Logger.LogError(LoggingEvents.PlexContentCacher, e, "Exception when adding tv show {0}", - show.title); } + contentToAdd.Clear(); } - if (contentToAdd.Count > 500) + if (count > 200) { - await Repo.AddRange(contentToAdd); - contentToAdd.Clear(); + await Repo.SaveChangesAsync(); } } + + await Repo.SaveChangesAsync(); } if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)) { - Logger.LogInformation("Processing Movies"); + Logger.LogDebug("Processing Movies"); foreach (var movie in content?.Metadata ?? new Metadata[] { }) { // Let's check if we have this movie @@ -314,7 +251,7 @@ namespace Ombi.Schedule.Jobs.Plex //var existing = await Repo.GetByKey(movie.ratingKey); if (existing != null) { - Logger.LogInformation("We already have movie {0}", movie.title); + Logger.LogDebug("We already have movie {0}", movie.title); continue; } @@ -324,7 +261,7 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.Delete(hasSameKey); } - Logger.LogInformation("Adding movie {0}", movie.title); + Logger.LogDebug("Adding movie {0}", movie.title); var metaData = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, movie.ratingKey); var providerIds = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer.Metadata @@ -365,6 +302,10 @@ namespace Ombi.Schedule.Jobs.Plex if (contentToAdd.Count > 500) { await Repo.AddRange(contentToAdd); + foreach (var c in contentToAdd) + { + contentProcessed.Add(c.Id, c.Key); + } contentToAdd.Clear(); } } @@ -372,6 +313,10 @@ namespace Ombi.Schedule.Jobs.Plex if (contentToAdd.Count > 500) { await Repo.AddRange(contentToAdd); + foreach (var c in contentToAdd) + { + contentProcessed.Add(c.Id, c.Key); + } contentToAdd.Clear(); } } @@ -379,6 +324,244 @@ namespace Ombi.Schedule.Jobs.Plex if (contentToAdd.Any()) { await Repo.AddRange(contentToAdd); + foreach (var c in contentToAdd) + { + contentProcessed.Add(c.Id, c.Key); + } + } + + retVal.Content = contentProcessed.Values; + retVal.Episodes = episodesProcessed; + return retVal; + } + + private async Task ProcessTvShow(PlexServers servers, Metadata show, HashSet contentToAdd, Dictionary contentProcessed) + { + var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri, + show.ratingKey); + var seasonsContent = new List(); + foreach (var season in seasonList.MediaContainer.Metadata) + { + seasonsContent.Add(new PlexSeasonsContent + { + ParentKey = season.parentRatingKey, + SeasonKey = season.ratingKey, + SeasonNumber = season.index, + PlexContentId = show.ratingKey + }); + } + + // Do we already have this item? + // Let's try and match + var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title + && x.ReleaseYear == show.year.ToString() + && x.Type == PlexMediaTypeEntity.Show); + + // Just double check the rating key, since this is our unique constraint + var existingKey = await Repo.GetByKey(show.ratingKey); + + if (existingKey != null) + { + // Damn son. + // Let's check if they match up + var doesMatch = show.title.Equals(existingKey.Title, + StringComparison.CurrentCulture); + if (!doesMatch) + { + // Something fucked up on Plex at somepoint... Damn, rebuild of lib maybe? + // Lets delete the matching key + await Repo.Delete(existingKey); + existingKey = null; + } + else if(existingContent == null) + { + existingContent = await Repo.GetFirstContentByCustom(x => x.Key == show.ratingKey); + } + } + + if (existingContent != null) + { + // Let's make sure that we have some sort of ID e.g. Imdbid for this, + // Looks like it's possible to not have an Id for a show + // I suspect we cached that show just as it was added to Plex. + + if (!existingContent.HasImdb && !existingContent.HasTheMovieDb && !existingContent.HasTvDb) + { + var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, + existingContent.Key); + GetProviderIds(showMetadata, existingContent); + + await Repo.Update(existingContent); + } + + // Just check the key + if (existingKey != null) + { + // The rating key is all good! + } + else + { + // This means the rating key has changed somehow. + // Should probably delete this and get the new one + var oldKey = existingContent.Key; + Repo.DeleteWithoutSave(existingContent); + + // Because we have changed the rating key, we need to change all children too + var episodeToChange = Repo.GetAllEpisodes().Where(x => x.GrandparentKey == oldKey); + if (episodeToChange.Any()) + { + foreach (var e in episodeToChange) + { + Repo.DeleteWithoutSave(e); + } + } + + await Repo.SaveChangesAsync(); + existingContent = null; + } + } + + // Also make sure it's not already being processed... + var alreadyProcessed = contentProcessed.Select(x => x.Value).Any(x => x == show.ratingKey); + if (alreadyProcessed) + { + return; + } + + // The ratingKey keeps changing... + //var existingContent = await Repo.GetByKey(show.ratingKey); + if (existingContent != null) + { + try + { + Logger.LogDebug("We already have show {0} checking for new seasons", + existingContent.Title); + // Ok so we have it, let's check if there are any new seasons + var itemAdded = false; + foreach (var season in seasonsContent) + { + var seasonExists = + existingContent.Seasons.FirstOrDefault(x => x.SeasonKey == season.SeasonKey); + + if (seasonExists != null) + { + // We already have this season + // check if we have the episode + //if (episode != null) + //{ + // var existing = existingContent.Episodes.Any(x => + // x.SeasonNumber == episode.parentIndex && x.EpisodeNumber == episode.index); + // if (!existing) + // { + // // We don't have this episode, lets add it + // existingContent.Episodes.Add(new PlexEpisode + // { + // EpisodeNumber = episode.index, + // SeasonNumber = episode.parentIndex, + // GrandparentKey = episode.grandparentRatingKey, + // ParentKey = episode.parentRatingKey, + // Key = episode.ratingKey, + // Title = episode.title + // }); + // itemAdded = true; + // } + //} + continue; + } + + existingContent.Seasons.Add(season); + itemAdded = true; + } + + if (itemAdded) await Repo.Update(existingContent); + } + catch (Exception e) + { + Logger.LogError(LoggingEvents.PlexContentCacher, e, + "Exception when adding new seasons to title {0}", existingContent.Title); + } + } + else + { + try + { + Logger.LogDebug("New show {0}, so add it", show.title); + + // Get the show metadata... This sucks since the `metadata` var contains all information about the show + // But it does not contain the `guid` property that we need to pull out thetvdb id... + var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, + show.ratingKey); + + var item = new PlexServerContent + { + AddedAt = DateTime.Now, + Key = show.ratingKey, + ReleaseYear = show.year.ToString(), + Type = PlexMediaTypeEntity.Show, + Title = show.title, + Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), + Seasons = new List() + }; + GetProviderIds(showMetadata, item); + + // Let's just double check to make sure we do not have it now we have some id's + var existingImdb = false; + var existingMovieDbId = false; + var existingTvDbId = false; + if (item.ImdbId.HasValue()) + { + existingImdb = await Repo.GetAll().AnyAsync(x => + x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show); + } + + if (item.TheMovieDbId.HasValue()) + { + existingMovieDbId = await Repo.GetAll().AnyAsync(x => + x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show); + } + + if (item.TvDbId.HasValue()) + { + existingTvDbId = await Repo.GetAll().AnyAsync(x => + x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show); + } + + if (existingImdb || existingTvDbId || existingMovieDbId) + { + // We already have it! + return; + } + + item.Seasons.ToList().AddRange(seasonsContent); + + contentToAdd.Add(item); + } + catch (Exception e) + { + Logger.LogError(LoggingEvents.PlexContentCacher, e, "Exception when adding tv show {0}", + show.title); + } + } + } + + private static void GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent) + { + var providerIds = + PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault() + .guid); + if (providerIds.Type == ProviderType.ImdbId) + { + existingContent.ImdbId = providerIds.ImdbId; + } + + if (providerIds.Type == ProviderType.TheMovieDbId) + { + existingContent.TheMovieDbId = providerIds.TheMovieDb; + } + + if (providerIds.Type == ProviderType.TvDbId) + { + existingContent.TvDbId = providerIds.TheTvDb; } } @@ -388,8 +571,9 @@ namespace Ombi.Schedule.Jobs.Plex /// If they have not set the settings then we will monitor them all /// /// The plex settings. + /// /// - private async Task> GetAllContent(PlexServers plexSettings) + private async Task> GetAllContent(PlexServers plexSettings, bool recentlyAddedSearch) { var sections = await PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri); @@ -407,16 +591,29 @@ namespace Ombi.Schedule.Jobs.Plex .Select(x => x.Key.ToString()).ToList(); if (!keys.Contains(dir.key)) { - Logger.LogInformation("Lib {0} is not monitored, so skipping", dir.key); + Logger.LogDebug("Lib {0} is not monitored, so skipping", dir.key); // We are not monitoring this lib continue; } } } - var lib = await PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.key); - if (lib != null) + + if (recentlyAddedSearch) { - libs.Add(lib.MediaContainer); + var container = await PlexApi.GetRecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, + dir.key); + if (container != null) + { + libs.Add(container.MediaContainer); + } + } + else + { + var lib = await PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.key); + if (lib != null) + { + libs.Add(lib.MediaContainer); + } } } } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs index 7d0fde4c7..5652d126b 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs @@ -50,12 +50,13 @@ namespace Ombi.Schedule.Jobs.Plex await Cache(server); } - BackgroundJob.Enqueue(() => _availabilityChecker.Start()); } catch (Exception e) { _log.LogError(LoggingEvents.Cacher, e, "Caching Episodes Failed"); } + + BackgroundJob.Enqueue(() => _availabilityChecker.Start()); } private async Task Cache(PlexServers settings) @@ -110,7 +111,7 @@ namespace Ombi.Schedule.Jobs.Plex // 12.03.2017 - I think we should be able to match them now //await _repo.ExecuteSql("DELETE FROM PlexEpisode"); - await ProcessEpsiodes(episodes, currentEpisodes); + await ProcessEpsiodes(episodes?.MediaContainer?.Metadata ?? new Metadata[] { }, currentEpisodes); currentPosition += resultCount; while (currentPosition < episodes.MediaContainer.totalSize) @@ -118,7 +119,7 @@ namespace Ombi.Schedule.Jobs.Plex var ep = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount); - await ProcessEpsiodes(ep, currentEpisodes); + await ProcessEpsiodes(ep?.MediaContainer?.Metadata ?? new Metadata[] { }, currentEpisodes); _log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Processed {resultCount} more episodes. Total Remaining {episodes.MediaContainer.totalSize - currentPosition}"); currentPosition += resultCount; } @@ -128,12 +129,12 @@ namespace Ombi.Schedule.Jobs.Plex await _repo.SaveChangesAsync(); } - private async Task ProcessEpsiodes(PlexContainer episodes, IQueryable currentEpisodes) + public async Task> ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes) { var ep = new HashSet(); try { - foreach (var episode in episodes?.MediaContainer?.Metadata ?? new Metadata[] { }) + foreach (var episode in episodes) { // I don't think we need to get the metadata, we only need to get the metadata if we need the provider id (TheTvDbid). Why do we need it for episodes? // We have the parent and grandparent rating keys to link up to the season and series @@ -178,6 +179,7 @@ namespace Ombi.Schedule.Jobs.Plex } await _repo.AddRange(ep); + return ep; } catch (Exception e) { diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexMediaType.cs b/src/Ombi.Schedule/Jobs/Plex/PlexMediaType.cs index bf8ac1a75..2e2e9229e 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexMediaType.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexMediaType.cs @@ -31,6 +31,7 @@ namespace Ombi.Schedule.Jobs public enum PlexMediaType { Movie = 0, - Show = 1 + Show = 1, + Episode = 2, } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs new file mode 100644 index 000000000..51596f891 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Hangfire; + +namespace Ombi.Schedule.Jobs.Plex +{ + public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync + { + public PlexRecentlyAddedSync(IPlexContentSync sync) + { + _sync = sync; + } + + private readonly IPlexContentSync _sync; + + public void Start() + { + BackgroundJob.Enqueue(() => _sync.CacheContent(true)); + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _sync?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs index 8e13d6f9e..ec5502581 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -43,17 +45,22 @@ namespace Ombi.Schedule.Jobs.Sonarr var series = await _api.GetSeries(settings.ApiKey, settings.FullUri); if (series != null) { - var sonarrSeries = series as IList ?? series.ToList(); + var sonarrSeries = series as ImmutableHashSet ?? series.ToImmutableHashSet(); var ids = sonarrSeries.Select(x => x.tvdbId); await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrCache"); - var entites = ids.Select(id => new SonarrCache { TvDbId = id }).ToList(); + var entites = ids.Select(id => new SonarrCache { TvDbId = id }).ToImmutableHashSet(); await _ctx.SonarrCache.AddRangeAsync(entites); - + entites.Clear(); + await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrEpisodeCache"); foreach (var s in sonarrSeries) { + if (!s.monitored) + { + continue; + } _log.LogDebug("Syncing series: {0}", s.title); var episodes = await _api.GetEpisodes(s.id, settings.ApiKey, settings.FullUri); var monitoredEpisodes = episodes.Where(x => x.monitored || x.hasFile); @@ -67,10 +74,10 @@ namespace Ombi.Schedule.Jobs.Sonarr TvDbId = s.tvdbId, HasFile = episode.hasFile })); + _log.LogDebug("Commiting the transaction"); + await _ctx.SaveChangesAsync(); } - _log.LogDebug("Commiting the transaction"); - await _ctx.SaveChangesAsync(); } } catch (Exception e) diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 5088bc9f8..06cc2bb49 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -10,9 +10,9 @@ - - - + + + @@ -27,6 +27,7 @@ + diff --git a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs index f3053876b..e645097ef 100644 --- a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs +++ b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs @@ -90,15 +90,15 @@ namespace Ombi.Schedule.Processor } - return TransformUpdate(release); + return TransformUpdate(release,!masterBranch); } - private UpdateModel TransformUpdate(Release release) + private UpdateModel TransformUpdate(Release release, bool develop) { var newUpdate = new UpdateModel { - UpdateVersionString = release.Version, + UpdateVersionString = develop ? release.Version : release.Version.Substring(1,8), UpdateVersion = release.Version == "(unreleased)" ? 0 : int.Parse(release.Version.Substring(1, 5).Replace(".", "")), UpdateDate = DateTime.Now, ChangeLogs = new List(), diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 5a99cc830..19a415a47 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs index 9ce5c72ac..f6736e7c5 100644 --- a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs @@ -12,5 +12,6 @@ namespace Ombi.Settings.Settings.Models public bool RequireLowercase { get; set; } public bool RequireNonAlphanumeric { get; set; } public bool RequireUppercase { get; set; } + public bool EnableOAuth { get; set; } // Plex OAuth } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs index 515c2fc85..120c14bd8 100644 --- a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs @@ -9,7 +9,6 @@ namespace Ombi.Settings.Settings.Models { public string ApplicationName { get; set; } public string ApplicationUrl { get; set; } - public bool Mobile { get; set; } public string CustomCssLink { get; set; } public bool EnableCustomDonations { get; set; } public string CustomDonationUrl { get; set; } diff --git a/src/Ombi.Settings/Settings/Models/External/LidarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/LidarrSettings.cs new file mode 100644 index 000000000..3a37b7d43 --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/External/LidarrSettings.cs @@ -0,0 +1,16 @@ +using Ombi.Core.Settings.Models.External; + +namespace Ombi.Settings.Settings.Models.External +{ + public class LidarrSettings : ExternalSettings + { + public bool Enabled { get; set; } + public string ApiKey { get; set; } + public string DefaultQualityProfile { get; set; } + public string DefaultRootPath { get; set; } + public bool AlbumFolder { get; set; } + public int LanguageProfileId { get; set; } + public int MetadataProfileId { get; set; } + public bool AddOnly { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index 3fcde951a..8fc8111f7 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Ombi.Settings.Settings.Models.External; namespace Ombi.Core.Settings.Models.External @@ -6,8 +7,11 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } + /// + /// This is the ClientId for OAuth + /// + public Guid InstallId { get; set; } public List Servers { get; set; } - } public class PlexServers : ExternalSettings diff --git a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs index 789b8f384..0c7e17900 100644 --- a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs @@ -13,6 +13,10 @@ /// The root path. /// public string RootPath { get; set; } + + + public string QualityProfileAnime { get; set; } + public string RootPathAnime { get; set; } public bool AddOnly { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/IssueSettings.cs b/src/Ombi.Settings/Settings/Models/IssueSettings.cs index e025c82d1..d7a35c0d9 100644 --- a/src/Ombi.Settings/Settings/Models/IssueSettings.cs +++ b/src/Ombi.Settings/Settings/Models/IssueSettings.cs @@ -4,5 +4,8 @@ { public bool Enabled { get; set; } public bool EnableInProgress { get; set; } + + public bool DeleteIssues { get; set; } + public int DaysAfterResolvedToDelete { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index a68ceb8bb..8b283cdf7 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -6,11 +6,14 @@ public string SonarrSync { get; set; } public string RadarrSync { get; set; } public string PlexContentSync { get; set; } + public string PlexRecentlyAddedSync { get; set; } public string CouchPotatoSync { get; set; } public string AutomaticUpdater { get; set; } public string UserImporter { get; set; } public string SickRageSync { get; set; } public string RefreshMetadata { get; set; } public string Newsletter { get; set; } + public string LidarrArtistSync { get; set; } + public string IssuesPurge { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index db4083fcd..4491ca27a 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -21,7 +21,11 @@ namespace Ombi.Settings.Settings.Models } public static string PlexContent(JobSettings s) { - return Get(s.PlexContentSync, Cron.Hourly(20)); + return Get(s.PlexContentSync, Cron.Daily(2)); + } + public static string PlexRecentlyAdded(JobSettings s) + { + return Get(s.PlexRecentlyAddedSync, Cron.MinuteInterval(30)); } public static string CouchPotato(JobSettings s) { @@ -46,9 +50,17 @@ namespace Ombi.Settings.Settings.Models } public static string RefreshMetadata(JobSettings s) { - return Get(s.RefreshMetadata, Cron.Daily(3)); + return Get(s.RefreshMetadata, Cron.DayInterval(3)); + } + public static string LidarrArtistSync(JobSettings s) + { + return Get(s.LidarrArtistSync, Cron.Hourly(40)); } + public static string IssuePurge(JobSettings s) + { + return Get(s.IssuesPurge, Cron.Daily()); + } private static string Get(string settings, string defaultCron) { diff --git a/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs index 380e2d743..3f6416af5 100644 --- a/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs +++ b/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs @@ -1,7 +1,13 @@ -namespace Ombi.Settings.Settings.Models.Notifications +using System.Collections.Generic; + +namespace Ombi.Settings.Settings.Models.Notifications { public class NewsletterSettings : Settings { + public bool DisableTv { get; set; } + public bool DisableMovies { get; set; } + public bool DisableMusic { get; set; } public bool Enabled { get; set; } + public List ExternalEmails { get; set; } = new List(); } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/Notifications/PushoverSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/PushoverSettings.cs index d845e8695..9c3dfc350 100644 --- a/src/Ombi.Settings/Settings/Models/Notifications/PushoverSettings.cs +++ b/src/Ombi.Settings/Settings/Models/Notifications/PushoverSettings.cs @@ -8,5 +8,7 @@ namespace Ombi.Settings.Settings.Models.Notifications public bool Enabled { get; set; } public string AccessToken { get; set; } public string UserToken { get; set; } + public sbyte Priority { get; set; } = 0; + public string Sound { get; set; } = "pushover"; } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/UpdateSettings.cs b/src/Ombi.Settings/Settings/Models/UpdateSettings.cs index 23c80c392..4180ab760 100644 --- a/src/Ombi.Settings/Settings/Models/UpdateSettings.cs +++ b/src/Ombi.Settings/Settings/Models/UpdateSettings.cs @@ -10,5 +10,6 @@ public string ScriptLocation { get; set; } public string WindowsServiceName { get; set; } public bool WindowsService { get; set; } + public bool TestMode { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 03e528412..77134e1a3 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -29,6 +29,7 @@ namespace Ombi.Store.Context void Seed(); DbSet Audit { get; set; } DbSet MovieRequests { get; set; } + DbSet AlbumRequests { get; set; } DbSet TvRequests { get; set; } DbSet ChildRequests { get; set; } DbSet Issues { get; set; } @@ -40,8 +41,11 @@ namespace Ombi.Store.Context EntityEntry Update(TEntity entity) where TEntity : class; DbSet CouchPotatoCache { get; set; } DbSet SickRageCache { get; set; } + DbSet LidarrArtistCache { get; set; } + DbSet LidarrAlbumCache { get; set; } DbSet SickRageEpisodeCache { get; set; } DbSet RequestLogs { get; set; } DbSet RecentlyAddedLogs { get; set; } + DbSet RequestSubscription { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 6a408144f..66631dfc6 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -31,6 +31,7 @@ namespace Ombi.Store.Context public DbSet EmbyEpisode { get; set; } public DbSet MovieRequests { get; set; } + public DbSet AlbumRequests { get; set; } public DbSet TvRequests { get; set; } public DbSet ChildRequests { get; set; } @@ -45,10 +46,14 @@ namespace Ombi.Store.Context public DbSet Audit { get; set; } public DbSet Tokens { get; set; } public DbSet SonarrCache { get; set; } + public DbSet LidarrArtistCache { get; set; } + public DbSet LidarrAlbumCache { get; set; } public DbSet SonarrEpisodeCache { get; set; } public DbSet SickRageCache { get; set; } public DbSet SickRageEpisodeCache { get; set; } - + public DbSet RequestSubscription { get; set; } + public DbSet UserNotificationPreferences { get; set; } + public DbSet UserQualityProfileses { get; set; } public DbSet ApplicationConfigurations { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -117,14 +122,43 @@ namespace Ombi.Store.Context Database.ExecuteSqlCommand("VACUUM;"); // Make sure we have the roles - var roles = Roles.Where(x => x.Name == OmbiRoles.RecievesNewsletter); - if (!roles.Any()) + var newsletterRole = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter); + if (!newsletterRole.Any()) { - Roles.Add(new IdentityRole(OmbiRoles.RecievesNewsletter) + Roles.Add(new IdentityRole(OmbiRoles.ReceivesNewsletter) { - NormalizedName = OmbiRoles.RecievesNewsletter.ToUpper() + NormalizedName = OmbiRoles.ReceivesNewsletter.ToUpper() }); + SaveChanges(); + } + var requestMusicRole = Roles.Where(x => x.Name == OmbiRoles.RequestMusic); + if (!requestMusicRole.Any()) + { + Roles.Add(new IdentityRole(OmbiRoles.RequestMusic) + { + NormalizedName = OmbiRoles.RequestMusic.ToUpper() + }); + Roles.Add(new IdentityRole(OmbiRoles.AutoApproveMusic) + { + NormalizedName = OmbiRoles.AutoApproveMusic.ToUpper() + }); + SaveChanges(); } + + // Make sure we have the API User + var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase)); + if (!apiUserExists) + { + Users.Add(new OmbiUser + { + UserName = "Api", + UserType = UserType.SystemUser, + NormalizedUserName = "API", + + }); + SaveChanges(); + } + //Check if templates exist var templates = NotificationTemplates.ToList(); @@ -167,7 +201,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! You {Title} on {ApplicationName}! This is now available! :)", + Message = "Hello! Your request for {Title} on {ApplicationName}! This is now available! :)", Subject = "{ApplicationName}: {Title} is now available!", Agent = agent, Enabled = true, diff --git a/src/Ombi.Store/Entities/ApplicationConfiguration.cs b/src/Ombi.Store/Entities/ApplicationConfiguration.cs index 1499ca34f..809264312 100644 --- a/src/Ombi.Store/Entities/ApplicationConfiguration.cs +++ b/src/Ombi.Store/Entities/ApplicationConfiguration.cs @@ -17,5 +17,6 @@ namespace Ombi.Store.Entities TheMovieDb = 4, StoragePath = 5, Notification = 6, + BaseUrl=7, } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/EmbyContent.cs b/src/Ombi.Store/Entities/EmbyContent.cs index d9d6e6983..348573f28 100644 --- a/src/Ombi.Store/Entities/EmbyContent.cs +++ b/src/Ombi.Store/Entities/EmbyContent.cs @@ -36,13 +36,30 @@ namespace Ombi.Store.Entities { public string Title { get; set; } + /// + /// OBSOLETE, Cannot delete due to DB migration issues with SQLite + /// public string ProviderId { get; set; } public string EmbyId { get; set; } public EmbyMediaType Type { get; set; } public DateTime AddedAt { get; set; } + public string ImdbId { get; set; } + public string TheMovieDbId { get; set; } + public string TvDbId { get; set; } + + public string Url { get; set; } public ICollection Episodes { get; set; } + + [NotMapped] + public bool HasImdb => !string.IsNullOrEmpty(ImdbId); + + [NotMapped] + public bool HasTvDb => !string.IsNullOrEmpty(TvDbId); + + [NotMapped] + public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId); } public enum EmbyMediaType diff --git a/src/Ombi.Store/Entities/EmbyEpisode.cs b/src/Ombi.Store/Entities/EmbyEpisode.cs index 150829240..e4e5b6a4b 100644 --- a/src/Ombi.Store/Entities/EmbyEpisode.cs +++ b/src/Ombi.Store/Entities/EmbyEpisode.cs @@ -39,8 +39,14 @@ namespace Ombi.Store.Entities public int EpisodeNumber { get; set; } public int SeasonNumber { get; set; } public string ParentId { get; set; } + /// + /// NOT USED + /// public string ProviderId { get; set; } public DateTime AddedAt { get; set; } + public string TvDbId { get; set; } + public string ImdbId { get; set; } + public string TheMovieDbId { get; set; } public EmbyContent Series { get; set; } } diff --git a/src/Ombi.Store/Entities/LidarrAlbumCache.cs b/src/Ombi.Store/Entities/LidarrAlbumCache.cs new file mode 100644 index 000000000..03099face --- /dev/null +++ b/src/Ombi.Store/Entities/LidarrAlbumCache.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("LidarrAlbumCache")] + public class LidarrAlbumCache : Entity + { + public int ArtistId { get; set; } + public string ForeignAlbumId { get; set; } + public int TrackCount { get; set; } + public DateTime ReleaseDate { get; set; } + public bool Monitored { get; set; } + public string Title { get; set; } + public decimal PercentOfTracks { get; set; } + public DateTime AddedAt { get; set; } + + [NotMapped] + public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0; + [NotMapped] + public bool FullyAvailable => PercentOfTracks == 100; + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/LidarrArtistCache.cs b/src/Ombi.Store/Entities/LidarrArtistCache.cs new file mode 100644 index 000000000..dd78b4e2c --- /dev/null +++ b/src/Ombi.Store/Entities/LidarrArtistCache.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("LidarrArtistCache")] + public class LidarrArtistCache : Entity + { + public int ArtistId { get; set; } + public string ArtistName { get; set; } + public string ForeignArtistId { get; set; } + public bool Monitored { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/OmbiUser.cs b/src/Ombi.Store/Entities/OmbiUser.cs index b3e82390a..801a50cb1 100644 --- a/src/Ombi.Store/Entities/OmbiUser.cs +++ b/src/Ombi.Store/Entities/OmbiUser.cs @@ -23,10 +23,12 @@ namespace Ombi.Store.Entities public int? MovieRequestLimit { get; set; } public int? EpisodeRequestLimit { get; set; } + public int? MusicRequestLimit { get; set; } public string UserAccessToken { get; set; } public List NotificationUserIds { get; set; } + public List UserNotificationPreferences { get; set; } [NotMapped] public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue(); @@ -36,6 +38,8 @@ namespace Ombi.Store.Entities [NotMapped] public bool EmailLogin { get; set; } + + [NotMapped] public bool IsSystemUser => UserType == UserType.SystemUser; [JsonIgnore] public override string PasswordHash @@ -57,5 +61,6 @@ namespace Ombi.Store.Entities get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } + } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RecentlyAddedLog.cs b/src/Ombi.Store/Entities/RecentlyAddedLog.cs index ba26eb566..782d89e3f 100644 --- a/src/Ombi.Store/Entities/RecentlyAddedLog.cs +++ b/src/Ombi.Store/Entities/RecentlyAddedLog.cs @@ -8,19 +8,24 @@ namespace Ombi.Store.Entities { public RecentlyAddedType Type { get; set; } public ContentType ContentType { get; set; } - public int ContentId { get; set; } // This is dependant on the type + public int ContentId { get; set; } // This is dependant on the type, it's either TMDBID or TVDBID + public int? EpisodeNumber { get; set; } + public int? SeasonNumber { get; set; } + public string AlbumId { get; set; } public DateTime AddedAt { get; set; } } public enum RecentlyAddedType { Plex = 0, - Emby = 1 + Emby = 1, + Lidarr = 2 } public enum ContentType { Parent = 0, - Episode = 1 + Episode = 1, + Album = 2, } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RequestSubscription.cs b/src/Ombi.Store/Entities/RequestSubscription.cs new file mode 100644 index 000000000..2f6d530c9 --- /dev/null +++ b/src/Ombi.Store/Entities/RequestSubscription.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("RequestSubscription")] + public class RequestSubscription : Entity + { + public string UserId { get; set; } + public int RequestId { get; set; } + public RequestType RequestType { get; set; } + + [ForeignKey(nameof(UserId))] + public OmbiUser User { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RequestType.cs b/src/Ombi.Store/Entities/RequestType.cs index 42356985f..151453bdd 100644 --- a/src/Ombi.Store/Entities/RequestType.cs +++ b/src/Ombi.Store/Entities/RequestType.cs @@ -6,7 +6,8 @@ namespace Ombi.Store.Entities { public enum RequestType { - TvShow, - Movie + TvShow = 0, + Movie = 1, + Album = 2, } } diff --git a/src/Ombi.Store/Entities/Requests/AlbumRequest.cs b/src/Ombi.Store/Entities/Requests/AlbumRequest.cs new file mode 100644 index 000000000..2735603c6 --- /dev/null +++ b/src/Ombi.Store/Entities/Requests/AlbumRequest.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities.Requests +{ + [Table("AlbumRequests")] + public class AlbumRequest : BaseRequest + { + public string ForeignAlbumId { get; set; } + public string ForeignArtistId { get; set; } + public string Disk { get; set; } + public string Cover { get; set; } + public decimal Rating { get; set; } + public DateTime ReleaseDate { get; set; } + public string ArtistName { get; set; } + [NotMapped] + public bool Subscribed { get; set; } + [NotMapped] + public bool ShowSubscribe { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/BaseRequest.cs b/src/Ombi.Store/Entities/Requests/BaseRequest.cs index 95395d0bf..f224032f1 100644 --- a/src/Ombi.Store/Entities/Requests/BaseRequest.cs +++ b/src/Ombi.Store/Entities/Requests/BaseRequest.cs @@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests { public string Title { get; set; } public bool Approved { get; set; } + public DateTime MarkedAsApproved { get; set; } public DateTime RequestedDate { get; set; } public bool Available { get; set; } + public DateTime? MarkedAsAvailable { get; set; } public string RequestedUserId { get; set; } public bool? Denied { get; set; } + public DateTime MarkedAsDenied { get; set; } public string DeniedReason { get; set; } public RequestType RequestType { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/ChildRequests.cs b/src/Ombi.Store/Entities/Requests/ChildRequests.cs index 64c0bcc5c..3b5156ce5 100644 --- a/src/Ombi.Store/Entities/Requests/ChildRequests.cs +++ b/src/Ombi.Store/Entities/Requests/ChildRequests.cs @@ -13,6 +13,15 @@ namespace Ombi.Store.Entities.Requests public int? IssueId { get; set; } public SeriesType SeriesType { get; set; } + /// + /// This is to see if the user is subscribed in the UI + /// + [NotMapped] + public bool Subscribed { get; set; } + + [NotMapped] + public bool ShowSubscribe { get; set; } + [ForeignKey(nameof(IssueId))] public List Issues { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/Issues.cs b/src/Ombi.Store/Entities/Requests/Issues.cs index b1021e362..9fbc6a83e 100644 --- a/src/Ombi.Store/Entities/Requests/Issues.cs +++ b/src/Ombi.Store/Entities/Requests/Issues.cs @@ -29,5 +29,6 @@ namespace Ombi.Store.Entities.Requests Pending = 0, InProgress = 1, Resolved = 2, + Deleted = 3, } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/MovieRequests.cs b/src/Ombi.Store/Entities/Requests/MovieRequests.cs index 998c27707..675035140 100644 --- a/src/Ombi.Store/Entities/Requests/MovieRequests.cs +++ b/src/Ombi.Store/Entities/Requests/MovieRequests.cs @@ -12,6 +12,11 @@ namespace Ombi.Store.Entities.Requests [ForeignKey(nameof(IssueId))] public List Issues { get; set; } + [NotMapped] + public bool Subscribed { get; set; } + [NotMapped] + public bool ShowSubscribe { get; set; } + public int RootPathOverride { get; set; } public int QualityOverride { get; set; } } diff --git a/src/Ombi.Store/Entities/Requests/TvRequests.cs b/src/Ombi.Store/Entities/Requests/TvRequests.cs index 88e1c36d7..432bc88ab 100644 --- a/src/Ombi.Store/Entities/Requests/TvRequests.cs +++ b/src/Ombi.Store/Entities/Requests/TvRequests.cs @@ -13,8 +13,10 @@ namespace Ombi.Store.Entities.Requests public string Overview { get; set; } public string Title { get; set; } public string PosterPath { get; set; } + public string Background { get; set; } public DateTime ReleaseDate { get; set; } public string Status { get; set; } + /// /// This is so we can correctly send the right amount of seasons to Sonarr /// diff --git a/src/Ombi.Store/Entities/User.cs b/src/Ombi.Store/Entities/User.cs index d9a3207b2..68d1dbe00 100644 --- a/src/Ombi.Store/Entities/User.cs +++ b/src/Ombi.Store/Entities/User.cs @@ -29,6 +29,7 @@ namespace Ombi.Store.Entities { public enum UserType { + SystemUser = 0, LocalUser = 1, PlexUser = 2, EmbyUser = 3, diff --git a/src/Ombi.Store/Entities/UserNotificationPreferences.cs b/src/Ombi.Store/Entities/UserNotificationPreferences.cs new file mode 100644 index 000000000..7196d38ca --- /dev/null +++ b/src/Ombi.Store/Entities/UserNotificationPreferences.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; +using Ombi.Helpers; + +namespace Ombi.Store.Entities +{ + [Table(nameof(UserNotificationPreferences))] + public class UserNotificationPreferences : Entity + { + public string UserId { get; set; } + public NotificationAgent Agent { get; set; } + public bool Enabled { get; set; } + public string Value { get; set; } + + [ForeignKey(nameof(UserId))] + [JsonIgnore] + public OmbiUser User { get; set; } + } +} diff --git a/src/Ombi.Store/Entities/UserQualityProfiles.cs b/src/Ombi.Store/Entities/UserQualityProfiles.cs new file mode 100644 index 000000000..d944c81b2 --- /dev/null +++ b/src/Ombi.Store/Entities/UserQualityProfiles.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; +using Ombi.Helpers; + +namespace Ombi.Store.Entities +{ + [Table(nameof(UserQualityProfiles))] + public class UserQualityProfiles : Entity + { + public string UserId { get; set; } + + public int SonarrQualityProfileAnime { get; set; } + public int SonarrRootPathAnime { get; set; } + public int SonarrRootPath { get; set; } + public int SonarrQualityProfile { get; set; } + public int RadarrRootPath { get; set; } + public int RadarrQualityProfile { get; set; } + + [ForeignKey(nameof(UserId))] + [JsonIgnore] + public OmbiUser User { get; set; } + } +} diff --git a/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.Designer.cs b/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.Designer.cs new file mode 100644 index 000000000..644119ea0 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.Designer.cs @@ -0,0 +1,948 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180406224743_EmbyMetadata")] + partial class EmbyMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.cs b/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.cs new file mode 100644 index 000000000..b7f98525d --- /dev/null +++ b/src/Ombi.Store/Migrations/20180406224743_EmbyMetadata.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class EmbyMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ImdbId", + table: "EmbyEpisode", + nullable: true); + + migrationBuilder.AddColumn( + name: "TheMovieDbId", + table: "EmbyEpisode", + nullable: true); + + migrationBuilder.AddColumn( + name: "TvDbId", + table: "EmbyEpisode", + nullable: true); + + migrationBuilder.AddColumn( + name: "ImdbId", + table: "EmbyContent", + nullable: true); + + migrationBuilder.AddColumn( + name: "TheMovieDbId", + table: "EmbyContent", + nullable: true); + + migrationBuilder.AddColumn( + name: "TvDbId", + table: "EmbyContent", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ImdbId", + table: "EmbyEpisode"); + + migrationBuilder.DropColumn( + name: "TheMovieDbId", + table: "EmbyEpisode"); + + migrationBuilder.DropColumn( + name: "TvDbId", + table: "EmbyEpisode"); + + migrationBuilder.DropColumn( + name: "ImdbId", + table: "EmbyContent"); + + migrationBuilder.DropColumn( + name: "TheMovieDbId", + table: "EmbyContent"); + + migrationBuilder.DropColumn( + name: "TvDbId", + table: "EmbyContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs new file mode 100644 index 000000000..b79528e34 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs @@ -0,0 +1,950 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180413021646_tvrequestsbackground")] + partial class tvrequestsbackground + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs new file mode 100644 index 000000000..d385d6000 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class tvrequestsbackground : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Background", + table: "TvRequests", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Background", + table: "TvRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs new file mode 100644 index 000000000..c5ddc8fa2 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs @@ -0,0 +1,950 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180419054711_EmbyButton")] + partial class EmbyButton + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs new file mode 100644 index 000000000..07e5cb730 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class EmbyButton : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Url", + table: "EmbyContent", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Url", + table: "EmbyContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs new file mode 100644 index 000000000..64c581108 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs @@ -0,0 +1,956 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180420225638_NewsletterChanges")] + partial class NewsletterChanges + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs new file mode 100644 index 000000000..ad4786772 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class NewsletterChanges : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "EpisodeNumber", + table: "RecentlyAddedLog", + nullable: true); + + migrationBuilder.AddColumn( + name: "SeasonNumber", + table: "RecentlyAddedLog", + nullable: true); + + migrationBuilder.Sql("DELETE FROM RecentlyAddedLog"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "EpisodeNumber", + table: "RecentlyAddedLog"); + + migrationBuilder.DropColumn( + name: "SeasonNumber", + table: "RecentlyAddedLog"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs new file mode 100644 index 000000000..e83458fef --- /dev/null +++ b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs @@ -0,0 +1,981 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180516090124_RequestSubscription")] + partial class RequestSubscription + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "user") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.cs b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.cs new file mode 100644 index 000000000..9ba204b60 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RequestSubscription : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RequestSubscription", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + RequestType = table.Column(nullable: false), + UserId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RequestSubscription", x => x.Id); + table.ForeignKey( + name: "FK_RequestSubscription_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_RequestSubscription_UserId", + table: "RequestSubscription", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RequestSubscription"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs b/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs new file mode 100644 index 000000000..c370e06ce --- /dev/null +++ b/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs @@ -0,0 +1,981 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180613203443_RoleRename")] + partial class RoleRename + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs b/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs new file mode 100644 index 000000000..76c0023f1 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RoleRename : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@" + UPDATE AspNetRoles +SET Name = 'ReceivesNewsletter', +NORMALIZEDNAME = 'RECEIVESNEWSLETTER' +where Name = 'RecievesNewsletter' + "); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs new file mode 100644 index 000000000..cebb7d22e --- /dev/null +++ b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs @@ -0,0 +1,981 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180703200952_EmbyUrlFix")] + partial class EmbyUrlFix + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs new file mode 100644 index 000000000..97e714a65 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class EmbyUrlFix : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql( + @"UPDATE EmbyContent SET Url = replace( Url, 'http://app.emby.media/itemdetails.html', 'http://app.emby.media/#!/itemdetails.html' ) WHERE Url LIKE 'http://app.emby.media/itemdetails.html%';"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs b/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs new file mode 100644 index 000000000..1f34d280f --- /dev/null +++ b/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs @@ -0,0 +1,988 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180730085903_UserStats")] + partial class UserStats + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180730085903_UserStats.cs b/src/Ombi.Store/Migrations/20180730085903_UserStats.cs new file mode 100644 index 000000000..576ff28c6 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180730085903_UserStats.cs @@ -0,0 +1,72 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class UserStats : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MarkedAsApproved", + table: "MovieRequests", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "MarkedAsAvailable", + table: "MovieRequests", + nullable: true); + + migrationBuilder.AddColumn( + name: "MarkedAsDenied", + table: "MovieRequests", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "MarkedAsApproved", + table: "ChildRequests", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "MarkedAsAvailable", + table: "ChildRequests", + nullable: true); + + migrationBuilder.AddColumn( + name: "MarkedAsDenied", + table: "ChildRequests", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MarkedAsApproved", + table: "MovieRequests"); + + migrationBuilder.DropColumn( + name: "MarkedAsAvailable", + table: "MovieRequests"); + + migrationBuilder.DropColumn( + name: "MarkedAsDenied", + table: "MovieRequests"); + + migrationBuilder.DropColumn( + name: "MarkedAsApproved", + table: "ChildRequests"); + + migrationBuilder.DropColumn( + name: "MarkedAsAvailable", + table: "ChildRequests"); + + migrationBuilder.DropColumn( + name: "MarkedAsDenied", + table: "ChildRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180824152254_MusicRequests.Designer.cs b/src/Ombi.Store/Migrations/20180824152254_MusicRequests.Designer.cs new file mode 100644 index 000000000..415563212 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180824152254_MusicRequests.Designer.cs @@ -0,0 +1,1045 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180824152254_MusicRequests")] + partial class MusicRequests + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180824152254_MusicRequests.cs b/src/Ombi.Store/Migrations/20180824152254_MusicRequests.cs new file mode 100644 index 000000000..4810a39eb --- /dev/null +++ b/src/Ombi.Store/Migrations/20180824152254_MusicRequests.cs @@ -0,0 +1,67 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class MusicRequests : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MusicRequestLimit", + table: "AspNetUsers", + nullable: true); + + migrationBuilder.CreateTable( + name: "AlbumRequests", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + Approved = table.Column(nullable: false), + MarkedAsApproved = table.Column(nullable: false), + RequestedDate = table.Column(nullable: false), + Available = table.Column(nullable: false), + MarkedAsAvailable = table.Column(nullable: true), + RequestedUserId = table.Column(nullable: true), + Denied = table.Column(nullable: true), + MarkedAsDenied = table.Column(nullable: false), + DeniedReason = table.Column(nullable: true), + RequestType = table.Column(nullable: false), + ForeignAlbumId = table.Column(nullable: true), + ForeignArtistId = table.Column(nullable: true), + Disk = table.Column(nullable: true), + Cover = table.Column(nullable: true), + Rating = table.Column(nullable: false), + ReleaseDate = table.Column(nullable: false), + ArtistName = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AlbumRequests", x => x.Id); + table.ForeignKey( + name: "FK_AlbumRequests_AspNetUsers_RequestedUserId", + column: x => x.RequestedUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_AlbumRequests_RequestedUserId", + table: "AlbumRequests", + column: "RequestedUserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AlbumRequests"); + + migrationBuilder.DropColumn( + name: "MusicRequestLimit", + table: "AspNetUsers"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.Designer.cs b/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.Designer.cs new file mode 100644 index 000000000..c97886525 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.Designer.cs @@ -0,0 +1,1087 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180824211553_LidarrSyncJobs")] + partial class LidarrSyncJobs + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.cs b/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.cs new file mode 100644 index 000000000..2b843d3e2 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180824211553_LidarrSyncJobs.cs @@ -0,0 +1,55 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class LidarrSyncJobs : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "LidarrAlbumCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ArtistId = table.Column(nullable: false), + ForeignAlbumId = table.Column(nullable: true), + TrackCount = table.Column(nullable: false), + ReleaseDate = table.Column(nullable: false), + Monitored = table.Column(nullable: false), + Title = table.Column(nullable: true), + PercentOfTracks = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LidarrAlbumCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "LidarrArtistCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ArtistId = table.Column(nullable: false), + ArtistName = table.Column(nullable: true), + ForeignArtistId = table.Column(nullable: true), + Monitored = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LidarrArtistCache", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LidarrAlbumCache"); + + migrationBuilder.DropTable( + name: "LidarrArtistCache"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180828083219_MusicIssues.Designer.cs b/src/Ombi.Store/Migrations/20180828083219_MusicIssues.Designer.cs new file mode 100644 index 000000000..52f00c840 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180828083219_MusicIssues.Designer.cs @@ -0,0 +1,1091 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180828083219_MusicIssues")] + partial class MusicIssues + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180828083219_MusicIssues.cs b/src/Ombi.Store/Migrations/20180828083219_MusicIssues.cs new file mode 100644 index 000000000..94a06ff18 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180828083219_MusicIssues.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class MusicIssues : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AlbumId", + table: "RecentlyAddedLog", + nullable: true); + + migrationBuilder.AddColumn( + name: "AddedAt", + table: "LidarrAlbumCache", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AlbumId", + table: "RecentlyAddedLog"); + + migrationBuilder.DropColumn( + name: "AddedAt", + table: "LidarrAlbumCache"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.Designer.cs b/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.Designer.cs new file mode 100644 index 000000000..d61ea31ba --- /dev/null +++ b/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.Designer.cs @@ -0,0 +1,1118 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180919073124_UserNotificationPreferences")] + partial class UserNotificationPreferences + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.cs b/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.cs new file mode 100644 index 000000000..adb062af7 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class UserNotificationPreferences : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserNotificationPreferences", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: true), + Agent = table.Column(nullable: false), + Enabled = table.Column(nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserNotificationPreferences", x => x.Id); + table.ForeignKey( + name: "FK_UserNotificationPreferences_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_UserNotificationPreferences_UserId", + table: "UserNotificationPreferences", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserNotificationPreferences"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.Designer.cs b/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.Designer.cs new file mode 100644 index 000000000..c9e18ef3a --- /dev/null +++ b/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.Designer.cs @@ -0,0 +1,1151 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180920201101_UserQualityProfiles")] + partial class UserQualityProfiles + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.cs b/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.cs new file mode 100644 index 000000000..bbcc2e79e --- /dev/null +++ b/src/Ombi.Store/Migrations/20180920201101_UserQualityProfiles.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class UserQualityProfiles : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserQualityProfiles", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: true), + SonarrQualityProfileAnime = table.Column(nullable: false), + SonarrRootPathAnime = table.Column(nullable: false), + SonarrRootPath = table.Column(nullable: false), + SonarrQualityProfile = table.Column(nullable: false), + RadarrRootPath = table.Column(nullable: false), + RadarrQualityProfile = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserQualityProfiles", x => x.Id); + table.ForeignKey( + name: "FK_UserQualityProfiles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_UserQualityProfiles_UserId", + table: "UserQualityProfiles", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserQualityProfiles"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index a24aa583a..86694ec23 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -1,15 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using Ombi.Helpers; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Ombi.Store.Context; -using Ombi.Store.Entities; -using Ombi.Store.Entities.Requests; -using System; namespace Ombi.Store.Migrations { @@ -20,7 +14,7 @@ namespace Ombi.Store.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -185,12 +179,20 @@ namespace Ombi.Store.Migrations b.Property("EmbyId") .IsRequired(); + b.Property("ImdbId"); + b.Property("ProviderId"); + b.Property("TheMovieDbId"); + b.Property("Title"); + b.Property("TvDbId"); + b.Property("Type"); + b.Property("Url"); + b.HasKey("Id"); b.ToTable("EmbyContent"); @@ -207,14 +209,20 @@ namespace Ombi.Store.Migrations b.Property("EpisodeNumber"); + b.Property("ImdbId"); + b.Property("ParentId"); b.Property("ProviderId"); b.Property("SeasonNumber"); + b.Property("TheMovieDbId"); + b.Property("Title"); + b.Property("TvDbId"); + b.HasKey("Id"); b.HasIndex("ParentId"); @@ -236,6 +244,50 @@ namespace Ombi.Store.Migrations b.ToTable("GlobalSettings"); }); + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => { b.Property("Id") @@ -303,6 +355,8 @@ namespace Ombi.Store.Migrations b.Property("MovieRequestLimit"); + b.Property("MusicRequestLimit"); + b.Property("NormalizedEmail") .HasMaxLength(256); @@ -437,10 +491,16 @@ namespace Ombi.Store.Migrations b.Property("AddedAt"); + b.Property("AlbumId"); + b.Property("ContentId"); b.Property("ContentType"); + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + b.Property("Type"); b.HasKey("Id"); @@ -448,6 +508,54 @@ namespace Ombi.Store.Migrations b.ToTable("RecentlyAddedLog"); }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => { b.Property("Id") @@ -463,6 +571,12 @@ namespace Ombi.Store.Migrations b.Property("IssueId"); + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + b.Property("ParentRequestId"); b.Property("RequestType"); @@ -577,6 +691,12 @@ namespace Ombi.Store.Migrations b.Property("IssueId"); + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + b.Property("Overview"); b.Property("PosterPath"); @@ -633,6 +753,8 @@ namespace Ombi.Store.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("Background"); + b.Property("ImdbId"); b.Property("Overview"); @@ -656,6 +778,24 @@ namespace Ombi.Store.Migrations b.ToTable("TvRequests"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => { b.Property("Id") @@ -730,6 +870,52 @@ namespace Ombi.Store.Migrations b.ToTable("Tokens"); }); + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => { b.Property("Id") @@ -850,6 +1036,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("PlexServerContentId"); }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => { b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") @@ -907,6 +1100,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("UserId"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") @@ -914,6 +1114,20 @@ namespace Ombi.Store.Migrations .HasForeignKey("UserId"); }); + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => { b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 901882669..cdbd3fe84 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/src/Ombi.Store/Repository/EmbyContentRepository.cs b/src/Ombi.Store/Repository/EmbyContentRepository.cs index 280243455..c4377f929 100644 --- a/src/Ombi.Store/Repository/EmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/EmbyContentRepository.cs @@ -35,42 +35,28 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class EmbyContentRepository : IEmbyContentRepository + public class EmbyContentRepository : Repository, IEmbyContentRepository { - public EmbyContentRepository(IOmbiContext db) + public EmbyContentRepository(IOmbiContext db):base(db) { Db = db; } private IOmbiContext Db { get; } - public IQueryable GetAll() + + public async Task GetByImdbId(string imdbid) { - return Db.EmbyContent.AsQueryable(); + return await Db.EmbyContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid); } - - public async Task AddRange(IEnumerable content) + public async Task GetByTvDbId(string tv) { - Db.EmbyContent.AddRange(content); - await Db.SaveChangesAsync(); + return await Db.EmbyContent.FirstOrDefaultAsync(x => x.TvDbId == tv); } - - public async Task ContentExists(string providerId) + public async Task GetByTheMovieDbId(string mov) { - return await Db.EmbyContent.AnyAsync(x => x.ProviderId == providerId); - } - - public async Task Add(EmbyContent content) - { - await Db.EmbyContent.AddAsync(content); - await Db.SaveChangesAsync(); - return content; - } - - public async Task Get(string providerId) - { - return await Db.EmbyContent.FirstOrDefaultAsync(x => x.ProviderId == providerId); + return await Db.EmbyContent.FirstOrDefaultAsync(x => x.TheMovieDbId == mov); } public IQueryable Get() @@ -111,23 +97,9 @@ namespace Ombi.Store.Repository await Db.SaveChangesAsync(); } - private bool _disposed; - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - Db?.Dispose(); - } - _disposed = true; - } - - public void Dispose() + public void UpdateWithoutSave(EmbyContent existingContent) { - Dispose(true); - GC.SuppressFinalize(this); + Db.EmbyContent.Update(existingContent); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IEmbyContentRepository.cs b/src/Ombi.Store/Repository/IEmbyContentRepository.cs index 3ed8d8abd..a893e9aca 100644 --- a/src/Ombi.Store/Repository/IEmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/IEmbyContentRepository.cs @@ -6,19 +6,19 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IEmbyContentRepository : IDisposable + public interface IEmbyContentRepository : IRepository { - Task Add(EmbyContent content); - Task AddRange(IEnumerable content); - Task ContentExists(string providerId); IQueryable Get(); - Task Get(string providerId); - IQueryable GetAll(); + Task GetByTheMovieDbId(string mov); + Task GetByTvDbId(string tv); + Task GetByImdbId(string imdbid); Task GetByEmbyId(string embyId); Task Update(EmbyContent existingContent); IQueryable GetAllEpisodes(); Task Add(EmbyEpisode content); Task GetEpisodeByEmbyId(string key); Task AddRange(IEnumerable content); + + void UpdateWithoutSave(EmbyContent existingContent); } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IRepository.cs b/src/Ombi.Store/Repository/IRepository.cs index c85b45d8f..810b586a3 100644 --- a/src/Ombi.Store/Repository/IRepository.cs +++ b/src/Ombi.Store/Repository/IRepository.cs @@ -14,7 +14,7 @@ namespace Ombi.Store.Repository Task Find(object key); IQueryable GetAll(); Task FirstOrDefaultAsync(Expression> predicate); - Task AddRange(IEnumerable content); + Task AddRange(IEnumerable content, bool save = true); Task Add(T content); Task DeleteRange(IEnumerable req); Task Delete(T request); diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index 098466310..e452eeb7d 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -89,6 +89,7 @@ namespace Ombi.Store.Repository { return await Db.PlexServerContent .Include(x => x.Seasons) + .Include(x => x.Episodes) .FirstOrDefaultAsync(predicate); } diff --git a/src/Ombi.Store/Repository/Repository.cs b/src/Ombi.Store/Repository/Repository.cs index 049da0356..8c07c2371 100644 --- a/src/Ombi.Store/Repository/Repository.cs +++ b/src/Ombi.Store/Repository/Repository.cs @@ -35,10 +35,13 @@ namespace Ombi.Store.Repository return await _db.FirstOrDefaultAsync(predicate); } - public async Task AddRange(IEnumerable content) + public async Task AddRange(IEnumerable content, bool save = true) { _db.AddRange(content); - await _ctx.SaveChangesAsync(); + if (save) + { + await _ctx.SaveChangesAsync(); + } } public async Task Add(T content) diff --git a/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs b/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs new file mode 100644 index 000000000..28cb0b2f9 --- /dev/null +++ b/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs @@ -0,0 +1,17 @@ +using System.Linq; +using System.Threading.Tasks; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Store.Repository.Requests +{ + public interface IMusicRequestRepository : IRepository + { + IQueryable GetAll(string userId); + AlbumRequest GetRequest(string foreignAlbumId); + Task GetRequestAsync(string foreignAlbumId); + IQueryable GetWithUser(); + IQueryable GetWithUser(string userId); + Task Save(); + Task Update(AlbumRequest request); + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs index 749b67c73..f08f7812f 100644 --- a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs @@ -14,7 +14,9 @@ namespace Ombi.Store.Repository.Requests Task Delete(TvRequests request); Task DeleteChild(ChildRequests request); IQueryable Get(); + IQueryable GetLite(); IQueryable Get(string userId); + IQueryable GetLite(string userId); Task GetRequestAsync(int tvDbId); TvRequests GetRequest(int tvDbId); Task Update(TvRequests request); diff --git a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs index 78c3da7dd..d4a550528 100644 --- a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs @@ -57,7 +57,7 @@ namespace Ombi.Store.Repository.Requests public IQueryable GetWithUser(string userId) { return Db.MovieRequests - .Where(x => x.RequestedUserId == userId) + .Where(x => x.RequestedUserId == userId) .Include(x => x.RequestedUser) .ThenInclude(x => x.NotificationUserIds) .AsQueryable(); diff --git a/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs new file mode 100644 index 000000000..59edf265a --- /dev/null +++ b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Store.Context; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Store.Repository.Requests +{ + public class MusicRequestRepository : Repository, IMusicRequestRepository + { + public MusicRequestRepository(IOmbiContext ctx) : base(ctx) + { + Db = ctx; + } + + private IOmbiContext Db { get; } + + public Task GetRequestAsync(string foreignAlbumId) + { + return Db.AlbumRequests.Where(x => x.ForeignAlbumId == foreignAlbumId) + .Include(x => x.RequestedUser) + .FirstOrDefaultAsync(); + } + + public IQueryable GetAll(string userId) + { + return GetWithUser().Where(x => x.RequestedUserId == userId); + } + + public AlbumRequest GetRequest(string foreignAlbumId) + { + return Db.AlbumRequests.Where(x => x.ForeignAlbumId == foreignAlbumId) + .Include(x => x.RequestedUser) + .FirstOrDefault(); + } + + public IQueryable GetWithUser() + { + return Db.AlbumRequests + .Include(x => x.RequestedUser) + .ThenInclude(x => x.NotificationUserIds) + .AsQueryable(); + } + + + public IQueryable GetWithUser(string userId) + { + return Db.AlbumRequests + .Where(x => x.RequestedUserId == userId) + .Include(x => x.RequestedUser) + .ThenInclude(x => x.NotificationUserIds) + .AsQueryable(); + } + + public async Task Update(AlbumRequest request) + { + if (Db.Entry(request).State == EntityState.Detached) + { + Db.AlbumRequests.Attach(request); + Db.Update(request); + } + await Db.SaveChangesAsync(); + } + + public async Task Save() + { + await Db.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs index 28a141908..daac7d4df 100644 --- a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs @@ -60,6 +60,24 @@ namespace Ombi.Store.Repository.Requests .Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId)) .AsQueryable(); } + + public IQueryable GetLite(string userId) + { + return Db.TvRequests + .Include(x => x.ChildRequests) + .ThenInclude(x => x.RequestedUser) + .Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId)) + .AsQueryable(); + } + + public IQueryable GetLite() + { + return Db.TvRequests + .Include(x => x.ChildRequests) + .ThenInclude(x => x.RequestedUser) + .AsQueryable(); + } + public IQueryable GetChild() { return Db.ChildRequests diff --git a/src/Ombi.Store/Repository/SettingsJsonRepository.cs b/src/Ombi.Store/Repository/SettingsJsonRepository.cs index 09bf61695..248413ccc 100644 --- a/src/Ombi.Store/Repository/SettingsJsonRepository.cs +++ b/src/Ombi.Store/Repository/SettingsJsonRepository.cs @@ -81,6 +81,7 @@ namespace Ombi.Store.Repository public void Update(GlobalSettings entity) { + Db.Update(entity); //_cache.Remove(GetName(entity.SettingsName)); Db.SaveChanges(); } diff --git a/src/Ombi.Tests/IdentityControllerTests.cs b/src/Ombi.Tests/IdentityControllerTests.cs index 978ec769c..72e76842d 100644 --- a/src/Ombi.Tests/IdentityControllerTests.cs +++ b/src/Ombi.Tests/IdentityControllerTests.cs @@ -1,132 +1,134 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AutoMapper; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; -using Ombi.Api.Emby; -using Ombi.Api.Plex; -using Ombi.Config; -using Ombi.Controllers; -using Ombi.Core.Authentication; -using Ombi.Core.Settings; -using Ombi.Core.Settings.Models.External; -using Ombi.Models; -using Ombi.Notifications; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Settings.Settings.Models; -using Ombi.Settings.Settings.Models.Notifications; -using Ombi.Store.Context; -using Ombi.Store.Entities; +//using System.Collections.Generic; +//using System.Linq; +//using System.Threading; +//using System.Threading.Tasks; +//using AutoMapper; +//using Microsoft.AspNetCore.Http; +//using Microsoft.AspNetCore.Http.Features.Authentication; +//using Microsoft.AspNetCore.Identity; +//using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore.Infrastructure; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Options; +//using Moq; +//using NUnit.Framework; +//using Ombi.Api.Emby; +//using Ombi.Api.Plex; +//using Ombi.Config; +//using Ombi.Controllers; +//using Ombi.Core.Authentication; +//using Ombi.Core.Settings; +//using Ombi.Core.Settings.Models.External; +//using Ombi.Models; +//using Ombi.Notifications; +//using Ombi.Schedule.Jobs.Ombi; +//using Ombi.Settings.Settings.Models; +//using Ombi.Settings.Settings.Models.Notifications; +//using Ombi.Store.Context; +//using Ombi.Store.Entities; -namespace Ombi.Tests -{ - [TestFixture] - [Ignore("Need to sort out the DB, looks like it's using the real one...")] - public class IdentityControllerTests - { - [SetUp] - public void Setup() - { - _plexApi = new Mock(); - _embyApi = new Mock(); - _mapper = new Mock(); - _emailProvider = new Mock(); - _emailSettings = new Mock>(); - _customizationSettings = new Mock>(); - _welcomeEmail = new Mock(); - _embySettings = new Mock>(); - _plexSettings = new Mock>(); +//namespace Ombi.Tests +//{ +// [TestFixture] +// [Ignore("Need to sort out the DB, looks like it's using the real one...")] +// public class IdentityControllerTests +// { +// [SetUp] +// public void Setup() +// { +// _plexApi = new Mock(); +// _embyApi = new Mock(); +// _mapper = new Mock(); +// _emailProvider = new Mock(); +// _emailSettings = new Mock>(); +// _customizationSettings = new Mock>(); +// _welcomeEmail = new Mock(); +// _embySettings = new Mock>(); +// _plexSettings = new Mock>(); - var services = new ServiceCollection(); - services.AddEntityFrameworkInMemoryDatabase() - .AddDbContext(); - services.AddIdentity() - .AddEntityFrameworkStores().AddUserManager(); +// var services = new ServiceCollection(); +// services.AddEntityFrameworkInMemoryDatabase() +// .AddDbContext(); +// services.AddIdentity() +// .AddEntityFrameworkStores().AddUserManager(); - services.AddTransient(x => _plexApi.Object); - services.AddTransient(x => _embyApi.Object); - services.AddTransient(x => _customizationSettings.Object); - services.AddTransient(x => _welcomeEmail.Object); - services.AddTransient(x => _emailSettings.Object); - services.AddTransient(x => _emailProvider.Object); - services.AddTransient(x => _mapper.Object); - services.AddTransient(x => _embySettings.Object); - services.AddTransient(x => _plexSettings.Object); +// services.AddTransient(x => _plexApi.Object); +// services.AddTransient(x => _embyApi.Object); +// services.AddTransient(x => _customizationSettings.Object); +// services.AddTransient(x => _welcomeEmail.Object); +// services.AddTransient(x => _emailSettings.Object); +// services.AddTransient(x => _emailProvider.Object); +// services.AddTransient(x => _mapper.Object); +// services.AddTransient(x => _embySettings.Object); +// services.AddTransient(x => _plexSettings.Object); - services.Configure(options => - { - options.Password.RequireDigit = false; - options.Password.RequiredLength = 1; - options.Password.RequireLowercase = false; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.User.AllowedUserNameCharacters = string.Empty; - }); +// services.Configure(options => +// { +// options.Password.RequireDigit = false; +// options.Password.RequiredLength = 1; +// options.Password.RequireLowercase = false; +// options.Password.RequireNonAlphanumeric = false; +// options.Password.RequireUppercase = false; +// options.User.AllowedUserNameCharacters = string.Empty; +// }); - // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified) - var context = new DefaultHttpContext(); - context.Features.Set(new HttpAuthenticationFeature()); - services.AddSingleton(h => new HttpContextAccessor { HttpContext = context }); - _serviceProvider = services.BuildServiceProvider(); - _userManager = _serviceProvider.GetRequiredService(); - - Controller = new IdentityController(_userManager, _mapper.Object, _serviceProvider.GetService>(), _emailProvider.Object, - _emailSettings.Object, _customizationSettings.Object,_welcomeEmail.Object, null, null, null, null, null, null, null, null); - } +// // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified) +// var context = new DefaultHttpContext(); +// context.Features.Set(new HttpAuthenticationFeature()); +// services.AddSingleton(h => new HttpContextAccessor { HttpContext = context }); +// _serviceProvider = services.BuildServiceProvider(); +// _userManager = _serviceProvider.GetRequiredService(); - private OmbiUserManager _userManager; - private Mock _emailProvider; - private Mock> _emailSettings; - private Mock> _customizationSettings; - private Mock> _embySettings; - private Mock> _plexSettings; - private Mock _welcomeEmail; - private Mock _mapper; - private Mock _plexApi; - private Mock _embyApi; - private ServiceProvider _serviceProvider; +// Controller = new IdentityController(_userManager, _mapper.Object, +// _serviceProvider.GetService>(), _emailProvider.Object, +// _emailSettings.Object, _customizationSettings.Object, _welcomeEmail.Object, null, null, null, null, +// null, null, null, null, null); +// } - private IdentityController Controller { get; set; } +// private OmbiUserManager _userManager; +// private Mock _emailProvider; +// private Mock> _emailSettings; +// private Mock> _customizationSettings; +// private Mock> _embySettings; +// private Mock> _plexSettings; +// private Mock _welcomeEmail; +// private Mock _mapper; +// private Mock _plexApi; +// private Mock _embyApi; +// private ServiceProvider _serviceProvider; - [Test] - public async Task CreateWizardUser_Should_CreateUser_WhenThereAreNoOtherUsers() - { - var model = new CreateUserWizardModel() - { - Password = "a", - Username = "b" - }; +// private IdentityController Controller { get; set; } - var result = await Controller.CreateWizardUser(model); +// [Test] +// public async Task CreateWizardUser_Should_CreateUser_WhenThereAreNoOtherUsers() +// { +// var model = new CreateUserWizardModel() +// { +// Password = "a", +// Username = "b" +// }; - Assert.That(result, Is.True); - } +// var result = await Controller.CreateWizardUser(model); - [Test] - public async Task CreateWizardUser_ShouldNot_CreateUser_WhenThereAreOtherUsers() - { - var um = _serviceProvider.GetService(); - var r = await um.CreateAsync(new OmbiUser { UserName = "aaaa",UserType = UserType.LocalUser}, "bbb"); - var model = new CreateUserWizardModel - { - Password = "a", - Username = "b" - }; +// Assert.That(result, Is.True); +// } - var result = await Controller.CreateWizardUser(model); +// [Test] +// public async Task CreateWizardUser_ShouldNot_CreateUser_WhenThereAreOtherUsers() +// { +// var um = _serviceProvider.GetService(); +// var r = await um.CreateAsync(new OmbiUser { UserName = "aaaa",UserType = UserType.LocalUser}, "bbb"); +// var model = new CreateUserWizardModel +// { +// Password = "a", +// Username = "b" +// }; - Assert.That(result, Is.False); - } - } -} +// var result = await Controller.CreateWizardUser(model); + +// Assert.That(result, Is.False); +// } +// } +//} diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index f3d041122..95eb09353 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,18 +1,18 @@ - netcoreapp2.0 + netcoreapp2.1 false - + - + diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 787902a4b..0b24bd55c 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -12,10 +12,12 @@ namespace Ombi.Api.TheMovieDb Task> NowPlaying(); Task> PopularMovies(); Task> SearchMovie(string searchTerm); + Task> SearchTv(string searchTerm); Task> TopRated(); Task> Upcoming(); Task> SimilarMovies(int movieId); Task Find(string externalId, ExternalSource source); Task GetTvExternals(int theMovieDbId); + Task GetTVInfo(string themoviedbid); } } \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/SearchResult.cs b/src/Ombi.TheMovieDbApi/Models/SearchResult.cs index 81d8115f6..7b09b5e4b 100644 --- a/src/Ombi.TheMovieDbApi/Models/SearchResult.cs +++ b/src/Ombi.TheMovieDbApi/Models/SearchResult.cs @@ -32,9 +32,12 @@ namespace Ombi.TheMovieDbApi.Models public bool adult { get; set; } public string overview { get; set; } public string release_date { get; set; } + public string first_air_date { get; set; } public int?[] genre_ids { get; set; } public int id { get; set; } public string original_title { get; set; } + public string original_name { get; set; } + public string name { get; set; } public string original_language { get; set; } public string title { get; set; } public string backdrop_path { get; set; } diff --git a/src/Ombi.TheMovieDbApi/Models/TvInfo.cs b/src/Ombi.TheMovieDbApi/Models/TvInfo.cs new file mode 100644 index 000000000..5692fcc17 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/TvInfo.cs @@ -0,0 +1,74 @@ +namespace Ombi.Api.TheMovieDb.Models +{ + public class TvInfo + { + public string backdrop_path { get; set; } + public Created_By[] created_by { get; set; } + public int[] episode_run_time { get; set; } + public string first_air_date { get; set; } + public Genre[] genres { get; set; } + public string homepage { get; set; } + public int id { get; set; } + public bool in_production { get; set; } + public string[] languages { get; set; } + public string last_air_date { get; set; } + public string name { get; set; } + public Network[] networks { get; set; } + public int number_of_episodes { get; set; } + public int number_of_seasons { get; set; } + public string[] origin_country { get; set; } + public string original_language { get; set; } + public string original_name { get; set; } + public string overview { get; set; } + public float popularity { get; set; } + public string poster_path { get; set; } + public Production_Companies[] production_companies { get; set; } + public Season[] seasons { get; set; } + public string status { get; set; } + public string type { get; set; } + public float vote_average { get; set; } + public int vote_count { get; set; } + } + + public class Created_By + { + public int id { get; set; } + public string name { get; set; } + public int gender { get; set; } + public string profile_path { get; set; } + } + + public class Genre + { + public int id { get; set; } + public string name { get; set; } + } + + public class Network + { + public string name { get; set; } + public int id { get; set; } + public string logo_path { get; set; } + public string origin_country { get; set; } + } + + public class Production_Companies + { + public int id { get; set; } + public string logo_path { get; set; } + public string name { get; set; } + public string origin_country { get; set; } + } + + public class Season + { + public string air_date { get; set; } + public int episode_count { get; set; } + public int id { get; set; } + public string name { get; set; } + public string overview { get; set; } + public string poster_path { get; set; } + public int season_number { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs b/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs new file mode 100644 index 000000000..eaf93d7cc --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs @@ -0,0 +1,18 @@ +namespace Ombi.Api.TheMovieDb.Models +{ + public class TvSearchResult + { + public string PosterPath { get; set; } + public string Overview { get; set; } + public string ReleaseDate { get; set; } + public int?[] GenreIds { get; set; } + public int Id { get; set; } + public string OriginalName { get; set; } + public string OriginalLanguage { get; set; } + public string Name { get; set; } + public string BackdropPath { get; set; } + public float Popularity { get; set; } + public int VoteCount { get; set; } + public float VoteAverage { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 08925e490..af9423f5d 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -42,7 +42,18 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } - + + public async Task> SearchTv(string searchTerm) + { + var request = new Request($"search/tv", BaseUri, HttpMethod.Get); + request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + AddRetry(request); + + var result = await Api.Request>(request); + return Mapper.Map>(result.results); + } + public async Task GetTvExternals(int theMovieDbId) { var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get); @@ -118,6 +129,15 @@ namespace Ombi.Api.TheMovieDb var result = await Api.Request>(request); return Mapper.Map>(result.results); } + + public async Task GetTVInfo(string themoviedbid) + { + var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get); + request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + AddRetry(request); + + return await Api.Request(request); + } private static void AddRetry(Request request) { request.Retry = true; diff --git a/src/Ombi.Updater/IProcessProvider.cs b/src/Ombi.Updater/IProcessProvider.cs index bc81ceb7c..68052c912 100644 --- a/src/Ombi.Updater/IProcessProvider.cs +++ b/src/Ombi.Updater/IProcessProvider.cs @@ -11,7 +11,7 @@ namespace Ombi.Updater ProcessInfo GetCurrentProcess(); int GetCurrentProcessId(); ProcessInfo GetProcessById(int id); - void Kill(StartupOptions opts); + bool Kill(StartupOptions opts); void KillAll(string processName); void SetPriority(int processId, ProcessPriorityClass priority); void WaitForExit(Process process); diff --git a/src/Ombi.Updater/Installer.cs b/src/Ombi.Updater/Installer.cs index 4827d45f9..10d574a10 100644 --- a/src/Ombi.Updater/Installer.cs +++ b/src/Ombi.Updater/Installer.cs @@ -22,29 +22,23 @@ namespace Ombi.Updater { // Kill Ombi Process var p = new ProcessProvider(); + bool killed = false; try { - p.Kill(opt); + killed = p.Kill(opt); } catch (Exception e) { Console.WriteLine(e); } - // Make sure the process has been killed - while (p.FindProcessByName(opt.ProcessName).Any()) + if (!killed) { - Thread.Sleep(500); - _log.LogDebug("Found another process called {0}, KILLING!", opt.ProcessName); - var proc = p.FindProcessByName(opt.ProcessName).FirstOrDefault(); - if (proc != null) - { - _log.LogDebug($"[{proc.Id}] - {proc.Name} - Path: {proc.StartPath}"); - opt.OmbiProcessId = proc.Id; - p.Kill(opt); - } + + _log.LogDebug("Couldn't kill the ombi process"); + return; } _log.LogDebug("Starting to move the files"); @@ -56,6 +50,7 @@ namespace Ombi.Updater private void StartOmbi(StartupOptions options) { + var startupArgsBuilder = new StringBuilder(); _log.LogDebug("Starting ombi"); var fileName = "Ombi.exe"; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -79,7 +74,6 @@ namespace Ombi.Updater } else { - var startupArgsBuilder = new StringBuilder(); if (!string.IsNullOrEmpty(options.Host)) { startupArgsBuilder.Append($"--host {options.Host} "); @@ -102,7 +96,10 @@ namespace Ombi.Updater } } - _log.LogDebug("Ombi started, now exiting"); + _log.LogDebug($"Ombi started, now exiting"); + _log.LogDebug($"Working dir: {options.ApplicationPath} (Application Path)"); + _log.LogDebug($"Filename: {Path.Combine(options.ApplicationPath, fileName)}"); + _log.LogDebug($"Startup Args: {startupArgsBuilder.ToString()}"); Environment.Exit(0); } @@ -111,21 +108,23 @@ namespace Ombi.Updater var location = System.Reflection.Assembly.GetEntryAssembly().Location; location = Path.GetDirectoryName(location); _log.LogDebug("We are currently in dir {0}", location); + var updatedLocation = Directory.GetParent(location).FullName; + _log.LogDebug("The files are in {0}", updatedLocation); // Since the updater is a folder deeper _log.LogDebug("Ombi is installed at {0}", options.ApplicationPath); //Now Create all of the directories - foreach (string dirPath in Directory.GetDirectories(location, "*", + foreach (string dirPath in Directory.GetDirectories(updatedLocation, "*", SearchOption.AllDirectories)) { - var newDir = dirPath.Replace(location, options.ApplicationPath); + var newDir = dirPath.Replace(updatedLocation, options.ApplicationPath); Directory.CreateDirectory(newDir); _log.LogDebug("Created dir {0}", newDir); } //Copy all the files & Replaces any files with the same name - foreach (string currentPath in Directory.GetFiles(location, "*.*", + foreach (string currentPath in Directory.GetFiles(updatedLocation, "*.*", SearchOption.AllDirectories)) { - var newFile = currentPath.Replace(location, options.ApplicationPath); + var newFile = currentPath.Replace(updatedLocation, options.ApplicationPath); File.Copy(currentPath, newFile, true); _log.LogDebug("Replaced file {0}", newFile); } diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index a23d8a967..07fb92d81 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp2.0 + netcoreapp2.1 3.0.0.0 3.0.0.0 @@ -12,14 +12,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi.Updater/ProcessProvider.cs b/src/Ombi.Updater/ProcessProvider.cs index 3a3f36355..f3324fc47 100644 --- a/src/Ombi.Updater/ProcessProvider.cs +++ b/src/Ombi.Updater/ProcessProvider.cs @@ -73,33 +73,32 @@ namespace Ombi.Updater process.PriorityClass = priority; } - public void Kill(StartupOptions opts) + public bool Kill(StartupOptions opts) { - if (opts.IsWindowsService) - { - Console.WriteLine("Stopping Service {0}", opts.WindowsServiceName); - var process = new Process(); - var startInfo = - new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = "cmd.exe", - Arguments = $"/C net stop \"{opts.WindowsServiceName}\"" - }; - process.StartInfo = startInfo; - process.Start(); - } - else - { + //if (opts.IsWindowsService) + //{ + // Console.WriteLine("Stopping Service {0}", opts.WindowsServiceName); + // var process = new Process(); + // var startInfo = + // new ProcessStartInfo + // { + // WindowStyle = ProcessWindowStyle.Hidden, + // FileName = "cmd.exe", + // Arguments = $"/C net stop \"{opts.WindowsServiceName}\"" + // }; + // process.StartInfo = startInfo; + // process.Start(); + //} + //else + //{ var process = Process.GetProcesses().FirstOrDefault(p => p.ProcessName == opts.ProcessName); if (process == null) { Console.WriteLine("Cannot find process with name: {0}", opts.ProcessName); - return; + return false; } - - process.Refresh(); + if (process.Id > 0) { @@ -108,8 +107,12 @@ namespace Ombi.Updater Console.WriteLine("[{0}]: Waiting for exit", process.Id); process.WaitForExit(); Console.WriteLine("[{0}]: Process terminated successfully", process.Id); + + return true; } - } + + return false; + //} } public void KillAll(string processName) diff --git a/src/Ombi.Updater/Properties/launchSettings.json b/src/Ombi.Updater/Properties/launchSettings.json index d25212166..c91e9f79f 100644 --- a/src/Ombi.Updater/Properties/launchSettings.json +++ b/src/Ombi.Updater/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Ombi.Updater": { "commandName": "Project", - "commandLineArgs": "--applicationPath \\\"C:\\\\Users\\\\Jamie\\\\Source\\\\Repos\\\\Ombi\\\\src\\\\Ombi\\\\bin\\\\Debug\\\\netcoreapp2.0\\\" --processname \\\"Ombi\\\" --startupArgs http://*:5000" + "commandLineArgs": "--applicationPath \"C:\\_git\\ombi\\src\\Ombi.Updater\\bin\\Debug\\netcoreapp2.0\" --processname \"Ombi\"" } } } \ No newline at end of file diff --git a/src/Ombi.sln b/src/Ombi.sln index ab9f8550c..1dfaa1dc6 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -92,7 +92,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Github", "Ombi.Api EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.SickRage", "Ombi.Api.SickRage\Ombi.Api.SickRage.csproj", "{94C9A366-2595-45EA-AABB-8E4A2E90EC5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -244,6 +246,10 @@ Global {10D1FE9D-9124-42B7-B1E1-CEB99B832618}.Debug|Any CPU.Build.0 = Debug|Any CPU {10D1FE9D-9124-42B7-B1E1-CEB99B832618}.Release|Any CPU.ActiveCfg = Release|Any CPU {10D1FE9D-9124-42B7-B1E1-CEB99B832618}.Release|Any CPU.Build.0 = Release|Any CPU + {4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -279,6 +285,7 @@ Global {55866DEE-46D1-4AF7-B1A2-62F6190C8EC7} = {9293CA11-360A-4C20-A674-B9E794431BF5} {94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5} {10D1FE9D-9124-42B7-B1E1-CEB99B832618} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} diff --git a/src/Ombi/.gitignore b/src/Ombi/.gitignore index b370cf723..49c402ee8 100644 --- a/src/Ombi/.gitignore +++ b/src/Ombi/.gitignore @@ -1,23 +1,10 @@ -/wwwroot/css/** -/wwwroot/fonts/** -/wwwroot/lib/** -/wwwroot/maps/** -/wwwroot/dist/** -/wwwroot/*.js.map -/wwwroot/*.js - -# dependencies -/node_modules -/bower_components - -# misc +node_modules +bin +obj +wwwroot/dist +*.log /.sass-cache /connect.lock /coverage/* -/libpeerconnection.log -npm-debug.log -testem.log -#/typings -/systemjs.config.js* /Logs/** **.db diff --git a/src/Ombi/.vscode/tasks.json b/src/Ombi/.vscode/tasks.json index 0f5fbd905..e57a5dee7 100644 --- a/src/Ombi/.vscode/tasks.json +++ b/src/Ombi/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "taskName": "restore", + "label": "restore", "command": "npm", "type": "shell", "args": [ @@ -14,7 +14,16 @@ "problemMatcher": [] }, { - "taskName": "build", + "label": "clean", + "command": "dotnet", + "type": "shell", + "args": [ + "clean" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "build", "command": "dotnet", "type": "shell", "args": [ @@ -27,7 +36,7 @@ "problemMatcher": "$msCompile" }, { - "taskName": "lint", + "label": "lint", "type": "shell", "command": "npm", "args": [ diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs new file mode 100644 index 000000000..f3c956df4 --- /dev/null +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -0,0 +1,126 @@ +using System; +using System.Linq; +using System.Net; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ombi.Core.Authentication; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; + +namespace Ombi +{ + public class ApiKeyMiddlewear + { + public ApiKeyMiddlewear(RequestDelegate next) + { + _next = next; + } + private readonly RequestDelegate _next; + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.StartsWithSegments(new PathString("/api"))) + { + //Let's check if this is an API Call + if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase)) + { + // validate the supplied API key + // Validate it + var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault(); + await ValidateApiKey(context, _next, headerKey); + } + else if (context.Request.Query.ContainsKey("apikey")) + { + if (context.Request.Query.TryGetValue("apikey", out var queryKey)) + { + await ValidateApiKey(context, _next, queryKey); + } + } + // User access token used by the mobile app + else if (context.Request.Headers["UserAccessToken"].Any()) + { + var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault(); + await ValidateUserAccessToken(context, _next, headerKey); + } + else + { + await _next.Invoke(context); + } + } + else + { + await _next.Invoke(context); + } + } + + private async Task ValidateUserAccessToken(HttpContext context, RequestDelegate next, string key) + { + if (string.IsNullOrEmpty(key)) + { + await context.Response.WriteAsync("Invalid User Access Token"); + return; + } + + var um = context.RequestServices.GetService(); + var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); + if (user == null) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid User Access Token"); + } + else + { + + var identity = new GenericIdentity(user.UserName); + var roles = await um.GetRolesAsync(user); + var principal = new GenericPrincipal(identity, roles.ToArray()); + context.User = principal; + await next.Invoke(context); + } + } + + private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key) + { + var repo = context.RequestServices.GetService>(); + var ombiSettings = await repo.GetSettingsAsync(); + var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase); + if (!valid) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid API Key"); + } + else + { + // Check if we have a UserName header if so we can impersonate that user + if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase)) + { + var username = context.Request.Headers["UserName"].FirstOrDefault(); + var um = context.RequestServices.GetService(); + var user = await um.Users.FirstOrDefaultAsync(x => + x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase)); + if (user == null) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid User"); + await next.Invoke(context); + } + var roles = await um.GetRolesAsync(user); + var identity = new GenericIdentity(user.UserName); + var principal = new GenericPrincipal(identity, roles.ToArray()); + context.User = principal; + } + else + { + var identity = new GenericIdentity("API"); + var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); + context.User = principal; + } + + await next.Invoke(context); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/animations/fadeinout.ts b/src/Ombi/ClientApp/app/animations/fadeinout.ts index 8ecf15a15..d0d5692a2 100644 --- a/src/Ombi/ClientApp/app/animations/fadeinout.ts +++ b/src/Ombi/ClientApp/app/animations/fadeinout.ts @@ -1,7 +1,7 @@ import { animate, style, transition, trigger } from "@angular/animations"; -import { AnimationEntryMetadata } from "@angular/core"; +import { AnimationTriggerMetadata } from "@angular/animations"; -export const fadeInOutAnimation: AnimationEntryMetadata = trigger("fadeInOut", [ +export const fadeInOutAnimation: AnimationTriggerMetadata = trigger("fadeInOut", [ transition(":enter", [ // :enter is alias to 'void => *' style({ opacity: 0 }), animate(1000, style({ opacity: 1 })), diff --git a/src/Ombi/ClientApp/app/app.component.html b/src/Ombi/ClientApp/app/app.component.html index 0db13fa9c..24398f088 100644 --- a/src/Ombi/ClientApp/app/app.component.html +++ b/src/Ombi/ClientApp/app/app.component.html @@ -1,4 +1,4 @@ -
+
  -
+
+
- +
+ + + @@ -167,7 +191,7 @@
- +
- +


-

{@INTRO}

+

{@INTRO}

+
{@RECENTLYADDED} -
+
"); + sb.Append(""); + sb.Append("
Powered by Ombi diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index cbbe62277..de1c55533 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,14 +1,14 @@  - netcoreapp2.0 + netcoreapp2.1 - + diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 0a5d5dabc..84e907053 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -20,8 +20,9 @@ namespace Ombi.Notifications.Agents { public DiscordNotification(IDiscordApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, - IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s) - : base(sn, r, m, t,s,log) + IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) + : base(sn, r, m, t, s, log, sub, music, userPref) { Api = api; Logger = log; @@ -130,12 +131,18 @@ namespace Ombi.Notifications.Agents title = MovieRequest.Title; image = MovieRequest.PosterPath; } - else + else if (model.RequestType == RequestType.TvShow) { user = TvRequest.RequestedUser.UserAlias; title = TvRequest.ParentRequest.Title; image = TvRequest.ParentRequest.PosterPath; } + else if (model.RequestType == RequestType.Album) + { + user = AlbumRequest.RequestedUser.UserAlias; + title = AlbumRequest.Title; + image = AlbumRequest.Cover; + } var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; var notification = new NotificationMessage { diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index d8f005e2b..cff1f3b80 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using MailKit.Net.Smtp; using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MimeKit; using Ombi.Core.Settings; @@ -21,7 +22,8 @@ namespace Ombi.Notifications.Agents public class EmailNotification : BaseNotification, IEmailNotification { public EmailNotification(ISettingsService settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService c, - ILogger log, UserManager um) : base(settings, r, m, t, c, log) + ILogger log, UserManager um, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(settings, r, m, t, c, log, sub, music, userPref) { EmailProvider = prov; Logger = log; @@ -52,7 +54,7 @@ namespace Ombi.Notifications.Agents return true; } - + private async Task LoadTemplate(NotificationType type, NotificationOptions model, EmailNotificationSettings settings) { var parsed = await LoadTemplate(NotificationAgent.Email, type, model); @@ -62,8 +64,8 @@ namespace Ombi.Notifications.Agents return null; } var email = new EmailBasicTemplate(); - var html = email.LoadTemplate(parsed.Subject, parsed.Message,parsed.Image, Customization.Logo); - + var html = email.LoadTemplate(parsed.Subject, parsed.Message, parsed.Image, Customization.Logo); + var message = new NotificationMessage { @@ -154,7 +156,7 @@ namespace Ombi.Notifications.Agents { message.To = model.Recipient; } - + await Send(message, settings); } @@ -176,7 +178,7 @@ namespace Ombi.Notifications.Agents // Issues resolved should be sent to the user message.To = model.Recipient; - + await Send(message, settings); } @@ -227,10 +229,12 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestDeclined, model, settings); message.Other.Add("PlainTextBody", plaintext); + await SendToSubscribers(settings, message); message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; await Send(message, settings); + } protected override async Task RequestApproved(NotificationOptions model, EmailNotificationSettings settings) @@ -244,12 +248,32 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestApproved, model, settings); message.Other.Add("PlainTextBody", plaintext); + await SendToSubscribers(settings, message); + message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; await Send(message, settings); } + private async Task SendToSubscribers(EmailNotificationSettings settings, NotificationMessage message) + { + if (await SubsribedUsers.AnyAsync()) + { + foreach (var user in SubsribedUsers) + { + if (user.Email.IsNullOrEmpty()) + { + continue; + } + + message.To = user.Email; + + await Send(message, settings); + } + } + } + protected override async Task AvailableRequest(NotificationOptions model, EmailNotificationSettings settings) { var message = await LoadTemplate(NotificationType.RequestAvailable, model, settings); @@ -260,7 +284,7 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestAvailable, model, settings); message.Other.Add("PlainTextBody", plaintext); - + await SendToSubscribers(settings, message); message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; diff --git a/src/Ombi.Notifications/Agents/Interfaces/IMobileNotification.cs b/src/Ombi.Notifications/Agents/Interfaces/IMobileNotification.cs new file mode 100644 index 000000000..1daf7e46a --- /dev/null +++ b/src/Ombi.Notifications/Agents/Interfaces/IMobileNotification.cs @@ -0,0 +1,6 @@ +namespace Ombi.Notifications.Agents +{ + public interface IMobileNotification : INotification + { + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index f07d62b72..37e597854 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -21,7 +21,8 @@ namespace Ombi.Notifications.Agents public class MattermostNotification : BaseNotification, IMattermostNotification { public MattermostNotification(IMattermostApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t,s,log) + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) { Api = api; Logger = log; @@ -203,13 +204,13 @@ namespace Ombi.Notifications.Agents Username = string.IsNullOrEmpty(settings.Username) ? "Ombi" : settings.Username, Channel = settings.Channel, Text = model.Message, - IconUrl = new Uri(settings.IconUrl), + IconUrl = settings.IconUrl, Attachments = new List { new MattermostAttachment { Title = model.Other.ContainsKey("title") ? model.Other["title"] : string.Empty, - ImageUrl = model.Other.ContainsKey("image") ? new Uri(model.Other["image"]) : null, + ImageUrl = model.Other.ContainsKey("image") ? model.Other["image"] : string.Empty, } } }; diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index 05c02dfef..a16785909 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -18,11 +18,12 @@ using Ombi.Store.Repository.Requests; namespace Ombi.Notifications.Agents { - public class MobileNotification : BaseNotification + public class MobileNotification : BaseNotification, IMobileNotification { public MobileNotification(IOneSignalApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository notification, - UserManager um) : base(sn, r, m, t, s,log) + UserManager um, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) { _api = api; _logger = log; @@ -39,7 +40,7 @@ namespace Ombi.Notifications.Agents protected override bool ValidateConfiguration(MobileNotificationSettings settings) { - return false; + return true; } protected override async Task NewRequest(NotificationOptions model, MobileNotificationSettings settings) @@ -57,7 +58,7 @@ namespace Ombi.Notifications.Agents // Get admin devices var playerIds = await GetAdmins(NotificationType.NewRequest); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model, true); } protected override async Task NewIssue(NotificationOptions model, MobileNotificationSettings settings) @@ -75,7 +76,7 @@ namespace Ombi.Notifications.Agents // Get admin devices var playerIds = await GetAdmins(NotificationType.Issue); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model); } protected override async Task IssueComment(NotificationOptions model, MobileNotificationSettings settings) @@ -97,13 +98,13 @@ namespace Ombi.Notifications.Agents { // Send to user var playerIds = GetUsers(model, NotificationType.IssueComment); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model); } else { // Send to admin var playerIds = await GetAdmins(NotificationType.IssueComment); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model); } } } @@ -124,7 +125,7 @@ namespace Ombi.Notifications.Agents // Send to user var playerIds = GetUsers(model, NotificationType.IssueResolved); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model); } @@ -149,7 +150,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices var playerIds = await GetAdmins(NotificationType.Test); - await Send(playerIds, notification, settings); + await Send(playerIds, notification, settings, model); } protected override async Task RequestDeclined(NotificationOptions model, MobileNotificationSettings settings) @@ -167,7 +168,8 @@ namespace Ombi.Notifications.Agents // Send to user var playerIds = GetUsers(model, NotificationType.RequestDeclined); - await Send(playerIds, notification, settings); + await AddSubscribedUsers(playerIds); + await Send(playerIds, notification, settings, model); } protected override async Task RequestApproved(NotificationOptions model, MobileNotificationSettings settings) @@ -185,7 +187,9 @@ namespace Ombi.Notifications.Agents // Send to user var playerIds = GetUsers(model, NotificationType.RequestApproved); - await Send(playerIds, notification, settings); + + await AddSubscribedUsers(playerIds); + await Send(playerIds, notification, settings, model); } protected override async Task AvailableRequest(NotificationOptions model, MobileNotificationSettings settings) @@ -202,20 +206,22 @@ namespace Ombi.Notifications.Agents }; // Send to user var playerIds = GetUsers(model, NotificationType.RequestAvailable); - await Send(playerIds, notification, settings); + + await AddSubscribedUsers(playerIds); + await Send(playerIds, notification, settings, model); } protected override Task Send(NotificationMessage model, MobileNotificationSettings settings) { throw new NotImplementedException(); } - protected async Task Send(List playerIds, NotificationMessage model, MobileNotificationSettings settings) + protected async Task Send(List playerIds, NotificationMessage model, MobileNotificationSettings settings, NotificationOptions requestModel, bool isAdminNotification = false) { - if (!playerIds.Any()) + if (playerIds == null || !playerIds.Any()) { return; } - var response = await _api.PushNotification(playerIds, model.Message); + var response = await _api.PushNotification(playerIds, model.Message, isAdminNotification, requestModel.RequestId, (int)requestModel.RequestType); _logger.LogDebug("Sent message to {0} recipients with message id {1}", response.recipients, response.id); } @@ -227,8 +233,14 @@ namespace Ombi.Notifications.Agents Message = message, }; // Send to user - var playerIds = await GetAdmins(NotificationType.RequestAvailable); - await Send(playerIds, notification, settings); + var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id.Equals(model.UserId)); + if (user == null) + { + return; + } + + var playerIds = user.NotificationUserIds.Select(x => x.PlayerId).ToList(); + await Send(playerIds, notification, settings, model); } private async Task> GetAdmins(NotificationType type) @@ -253,13 +265,13 @@ namespace Ombi.Notifications.Agents ? MovieRequest?.RequestedUser?.NotificationUserIds : TvRequest?.RequestedUser?.NotificationUserIds; } - if (model.UserId.HasValue() && !notificationIds.Any()) + if (model.UserId.HasValue() && (!notificationIds?.Any() ?? true)) { var user= _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefault(x => x.Id == model.UserId); notificationIds = user.NotificationUserIds; } - if (!notificationIds.Any()) + if (!notificationIds?.Any() ?? true) { _logger.LogInformation( $"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}"); @@ -269,6 +281,20 @@ namespace Ombi.Notifications.Agents return playerIds; } + private async Task AddSubscribedUsers(List playerIds) + { + if (await SubsribedUsers.AnyAsync()) + { + foreach (var user in SubsribedUsers.Include(x => x.NotificationUserIds)) + { + var notificationId = user.NotificationUserIds; + if (notificationId.Any()) + { + playerIds.AddRange(notificationId.Select(x => x.PlayerId)); + } + } + } + } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index 21a384db6..6c6b1f789 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -17,7 +17,8 @@ namespace Ombi.Notifications.Agents public class PushbulletNotification : BaseNotification, IPushbulletNotification { public PushbulletNotification(IPushbulletApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t,s,log) + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index 505adc44e..86f91dbaa 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents public class PushoverNotification : BaseNotification, IPushoverNotification { public PushoverNotification(IPushoverApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t, s, log) + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) { Api = api; Logger = log; @@ -177,7 +178,8 @@ namespace Ombi.Notifications.Agents { try { - await Api.PushAsync(settings.AccessToken, model.Message, settings.UserToken); + //&+' < > + await Api.PushAsync(settings.AccessToken, model.Message.StripCharacters('&','+','<','>'), settings.UserToken, settings.Priority, settings.Sound); } catch (Exception e) { diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index d723c065a..ee81e9729 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents public class SlackNotification : BaseNotification, ISlackNotification { public SlackNotification(ISlackApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t, s, log) + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index 921fd3ad2..cf463bf99 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -18,7 +18,9 @@ namespace Ombi.Notifications.Agents { public TelegramNotification(ITelegramApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, - ITvRequestRepository t, ISettingsService s) : base(sn, r, m, t,s,log) + ITvRequestRepository t, ISettingsService s + , IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t,s,log, sub, music, userPref) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Interfaces/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs similarity index 74% rename from src/Ombi.Notifications/Interfaces/BaseNotification.cs rename to src/Ombi.Notifications/BaseNotification.cs index f21d3fb12..d351c8283 100644 --- a/src/Ombi.Notifications/Interfaces/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -17,7 +19,8 @@ namespace Ombi.Notifications.Interfaces public abstract class BaseNotification : INotification where T : Settings.Settings.Models.Settings, new() { protected BaseNotification(ISettingsService settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv, - ISettingsService customization, ILogger> log) + ISettingsService customization, ILogger> log, IRepository sub, IMusicRequestRepository album, + IRepository notificationUserPreferences) { Settings = settings; TemplateRepository = templateRepo; @@ -26,21 +29,29 @@ namespace Ombi.Notifications.Interfaces CustomizationSettings = customization; Settings.ClearCache(); CustomizationSettings.ClearCache(); + RequestSubscription = sub; _log = log; + AlbumRepository = album; + UserNotificationPreferences = notificationUserPreferences; } - + protected ISettingsService Settings { get; } protected INotificationTemplatesRepository TemplateRepository { get; } protected IMovieRequestRepository MovieRepository { get; } protected ITvRequestRepository TvRepository { get; } + protected IMusicRequestRepository AlbumRepository { get; } protected CustomizationSettings Customization { get; set; } + protected IRepository RequestSubscription { get; set; } + protected IRepository UserNotificationPreferences { get; set; } private ISettingsService CustomizationSettings { get; } private readonly ILogger> _log; protected ChildRequests TvRequest { get; set; } + protected AlbumRequest AlbumRequest { get; set; } protected MovieRequests MovieRequest { get; set; } - + protected IQueryable SubsribedUsers { get; private set; } + public abstract string NotificationName { get; } public async Task NotifyAsync(NotificationOptions model) @@ -54,20 +65,21 @@ namespace Ombi.Notifications.Interfaces { Settings.ClearCache(); if (settings == null) await NotifyAsync(model); - + var notificationSettings = (T)settings; - + if (!ValidateConfiguration(notificationSettings)) { return; } - + // Is this a test? // The request id for tests is -1 // Also issues are 0 since there might not be a request associated if (model.RequestId > 0) { await LoadRequest(model.RequestId, model.RequestType); + SubsribedUsers = GetSubscriptions(model.RequestId, model.RequestType); } Customization = await CustomizationSettings.GetSettingsAsync(); @@ -124,9 +136,13 @@ namespace Ombi.Notifications.Interfaces { MovieRequest = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId); } - else + else if (type == RequestType.TvShow) + { + TvRequest = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); + } + else if (type == RequestType.Album) { - TvRequest = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); + AlbumRequest = await AlbumRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId); } } @@ -152,27 +168,45 @@ namespace Ombi.Notifications.Interfaces } if (!template.Enabled) { - return new NotificationMessageContent {Disabled = true}; + return new NotificationMessageContent { Disabled = true }; } - var parsed = Parse(model, template); + var parsed = Parse(model, template, agent); return parsed; } - private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template) + protected IQueryable GetSubscriptions(int requestId, RequestType type) + { + var subs = RequestSubscription.GetAll().Include(x => x.User).Where(x => x.RequestId == requestId && type == x.RequestType); + return subs.Select(x => x.User); + } + + protected UserNotificationPreferences GetUserPreference(string userId, NotificationAgent agent) + { + return UserNotificationPreferences.GetAll() + .FirstOrDefault(x => x.Enabled && x.Agent == agent && x.UserId == userId); + } + + private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent) { var resolver = new NotificationMessageResolver(); var curlys = new NotificationMessageCurlys(); + var preference = GetUserPreference(model.UserId, agent); if (model.RequestType == RequestType.Movie) { _log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization); - - curlys.Setup(model, MovieRequest, Customization); + + curlys.Setup(model, MovieRequest, Customization, preference); } - else + else if (model.RequestType == RequestType.TvShow) { _log.LogDebug("Notification options: {@model}, Req: {@TvRequest}, Settings: {@Customization}", model, TvRequest, Customization); - curlys.Setup(model, TvRequest, Customization); + curlys.Setup(model, TvRequest, Customization, preference); + } + else if (model.RequestType == RequestType.Album) + { + _log.LogDebug("Notification options: {@model}, Req: {@AlbumRequest}, Settings: {@Customization}", model, AlbumRequest, Customization); + curlys.Setup(model, AlbumRequest, Customization, preference); } var parsed = resolver.ParseMessage(template, curlys); diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index fb5dd976b..1b655b84d 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Humanizer; using Ombi.Helpers; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; @@ -13,10 +14,14 @@ namespace Ombi.Notifications { public class NotificationMessageCurlys { - - public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s) + public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); + if (pref != null) + { + UserPreference = pref.Enabled ? pref.Value : string.Empty; + } + string title; if (req == null) { @@ -40,12 +45,59 @@ namespace Ombi.Notifications RequestedDate = req?.RequestedDate.ToString("D"); if (Type.IsNullOrEmpty()) { - Type = req?.RequestType.ToString(); + Type = req?.RequestType.Humanize(); } Overview = req?.Overview; Year = req?.ReleaseDate.Year.ToString(); - PosterImage = req?.RequestType == RequestType.Movie ? - string.Format("https://image.tmdb.org/t/p/w300{0}", req?.PosterPath) : req?.PosterPath; + + if (req?.RequestType == RequestType.Movie) + { + PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase) + ? "https://image.tmdb.org/t/p/w300{0}" : "https://image.tmdb.org/t/p/w300/{0}", req?.PosterPath); + } + else + { + PosterImage = req?.PosterPath; + } + + AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; + } + + public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref) + { + LoadIssues(opts); + if (pref != null) + { + UserPreference = pref.Enabled ? pref.Value : string.Empty; + } + string title; + if (req == null) + { + opts.Substitutes.TryGetValue("Title", out title); + } + else + { + title = req?.Title; + } + ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; + ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; + RequestedUser = req?.RequestedUser?.UserName; + if (UserName.IsNullOrEmpty()) + { + // Can be set if it's an issue + UserName = req?.RequestedUser?.UserName; + } + + Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; + Title = title; + RequestedDate = req?.RequestedDate.ToString("D"); + if (Type.IsNullOrEmpty()) + { + Type = req?.RequestType.Humanize(); + } + Year = req?.ReleaseDate.Year.ToString(); + PosterImage = (req?.Cover.HasValue() ?? false) ? req.Cover : req?.Disk ?? string.Empty; + AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; } @@ -58,9 +110,13 @@ namespace Ombi.Notifications Alias = username.Alias.HasValue() ? username.Alias : username.UserName; } - public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s) + public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); + if (pref != null) + { + UserPreference = pref.Enabled ? pref.Value : string.Empty; + } string title; if (req == null) { @@ -83,13 +139,20 @@ namespace Ombi.Notifications RequestedDate = req?.RequestedDate.ToString("D"); if (Type.IsNullOrEmpty()) { - Type = req?.RequestType.ToString(); + Type = req?.RequestType.Humanize(); } Overview = req?.ParentRequest.Overview; Year = req?.ParentRequest.ReleaseDate.Year.ToString(); - PosterImage = req?.RequestType == RequestType.Movie ? - $"https://image.tmdb.org/t/p/w300{req?.ParentRequest.PosterPath}" : req?.ParentRequest.PosterPath; + if (req?.RequestType == RequestType.Movie) + { + PosterImage = string.Format((req?.ParentRequest.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase) + ? "https://image.tmdb.org/t/p/w300{0}" : "https://image.tmdb.org/t/p/w300/{0}", req?.ParentRequest.PosterPath); + } + else + { + PosterImage = req?.ParentRequest.PosterPath; + } AdditionalInformation = opts.AdditionalInformation; // DO Episode and Season Lists @@ -133,6 +196,8 @@ namespace Ombi.Notifications ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; RequestedUser = user.UserName; + Alias = user.UserAlias; + UserName = user.UserName; } private void LoadIssues(NotificationOptions opts) @@ -144,7 +209,7 @@ namespace Ombi.Notifications IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty; NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty; UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty; - Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val : string.Empty; + Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty; } // User Defined @@ -169,6 +234,7 @@ namespace Ombi.Notifications public string IssueStatus { get; set; } public string IssueSubject { get; set; } public string NewIssueComment { get; set; } + public string UserPreference { get; set; } // System Defined private string LongDate => DateTime.Now.ToString("D"); diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index ccdb29e65..2b5c95154 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs new file mode 100644 index 000000000..932022cd8 --- /dev/null +++ b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Ombi.Core.Settings; +using Ombi.Schedule.Jobs.Ombi; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; +using System.Threading.Tasks; + +namespace Ombi.Schedule.Tests +{ + [TestFixture] + public class IssuesPurgeTests + { + + [SetUp] + public void Setup() + { + Repo = new Mock>(); + Settings = new Mock>(); + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings()); + Job = new IssuesPurge(Repo.Object, Settings.Object); + } + + public Mock> Repo { get; set; } + public Mock> Settings { get; set; } + public IssuesPurge Job { get; set; } + + [Test] + public async Task DoesNotRun_WhenDisabled() + { + await Job.Start(); + Repo.Verify(x => x.GetAll(),Times.Never); + } + + [Test] + public async Task Deletes_Correct_Issue() + { + var issues = new List() + { + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-5).AddHours(-1) + } + }; + + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); + Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); + await Job.Start(); + + Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted)); + Repo.Verify(x => x.SaveChangesAsync(), Times.Once); + } + + [Test] + public async Task DoesNot_Delete_AnyIssues() + { + var issues = new List() + { + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-2) + }, + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-6) + } + }; + + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); + Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); + await Job.Start(); + + Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); + Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted)); + Repo.Verify(x => x.SaveChangesAsync(), Times.Once); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule.Tests/NewsletterTests.cs b/src/Ombi.Schedule.Tests/NewsletterTests.cs new file mode 100644 index 000000000..b3c2ce98a --- /dev/null +++ b/src/Ombi.Schedule.Tests/NewsletterTests.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using Ombi.Core.Settings; +using Ombi.Schedule.Jobs.Ombi; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; + +namespace Ombi.Schedule.Tests +{ + [TestFixture] + public class NewsletterTests + { + [TestCaseSource(nameof(EpisodeListData))] + public string BuildEpisodeListTest(List episodes) + { + var emailSettings = new Mock>(); + var customziation = new Mock>(); + var newsletterSettings = new Mock>(); + var newsletter = new NewsletterJob(null, null, null, null, null, null, customziation.Object, emailSettings.Object, null, null, newsletterSettings.Object, null, null, null, null); + + var ep = new List(); + foreach (var i in episodes) + { + ep.Add(i); + } + var result = newsletter.BuildEpisodeList(ep); + return result; + } + + public static IEnumerable EpisodeListData + { + get + { + yield return new TestCaseData(new List{1,2,3,4,5,6}).Returns("1-6").SetName("Simple 1-6"); + yield return new TestCaseData(new List{1,2,3,4,5,6,8,9}).Returns("1-6, 8-9").SetName("Simple 1-6, 8-9"); + yield return new TestCaseData(new List{1,99,101,555,468,469}).Returns("1, 99, 101, 555, 468-469").SetName("More Complex"); + yield return new TestCaseData(new List{1}).Returns("1").SetName("Single Episode"); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index eae150067..ea1d17f8c 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,17 +1,16 @@ - netcoreapp2.0 + netcoreapp2.1 - - + - - - - + + + + diff --git a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs index 0ebb4732c..55c9dc288 100644 --- a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs +++ b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs @@ -26,7 +26,7 @@ namespace Ombi.Schedule.Tests _tv = new Mock(); _movie = new Mock(); _notify = new Mock(); - Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object); + Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object, null); } diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 85b842d27..7ce32de04 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -1,8 +1,10 @@ -using Hangfire; +using System; +using Hangfire; using Ombi.Core.Settings; using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs.Couchpotato; using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex; using Ombi.Schedule.Jobs.Radarr; @@ -12,13 +14,14 @@ using Ombi.Settings.Settings.Models; namespace Ombi.Schedule { - public class JobSetup : IJobSetup + public class JobSetup : IJobSetup, IDisposable { public JobSetup(IPlexContentSync plexContentSync, IRadarrSync radarrSync, IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter, IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, - INewsletterJob newsletter) + INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist, + IIssuesPurge purge) { _plexContentSync = plexContentSync; _radarrSync = radarrSync; @@ -32,9 +35,13 @@ namespace Ombi.Schedule _srSync = srSync; _refreshMetadata = refresh; _newsletter = newsletter; + _plexRecentlyAddedSync = recentlyAddedPlex; + _lidarrArtistSync = artist; + _issuesPurge = purge; } private readonly IPlexContentSync _plexContentSync; + private readonly IPlexRecentlyAddedSync _plexRecentlyAddedSync; private readonly IRadarrSync _radarrSync; private readonly IOmbiAutomaticUpdater _updater; private readonly IPlexUserImporter _plexUserImporter; @@ -46,6 +53,8 @@ namespace Ombi.Schedule private readonly ISettingsService _jobSettings; private readonly IRefreshMetadata _refreshMetadata; private readonly INewsletterJob _newsletter; + private readonly ILidarrArtistSync _lidarrArtistSync; + private readonly IIssuesPurge _issuesPurge; public void Setup() { @@ -54,10 +63,13 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s)); RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s)); RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s)); - RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(), JobSettingsHelper.PlexContent(s)); + RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(false), JobSettingsHelper.PlexContent(s)); + RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s)); RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s)); RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s)); RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s)); + RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(s)); + RecurringJob.AddOrUpdate(() => _issuesPurge.Start(), JobSettingsHelper.IssuePurge(s)); RecurringJob.AddOrUpdate(() => _updater.Update(null), JobSettingsHelper.Updater(s)); @@ -65,5 +77,36 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); } + + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _plexContentSync?.Dispose(); + _radarrSync?.Dispose(); + _updater?.Dispose(); + _plexUserImporter?.Dispose(); + _embyContentSync?.Dispose(); + _embyUserImporter?.Dispose(); + _sonarrSync?.Dispose(); + _cpCache?.Dispose(); + _srSync?.Dispose(); + _jobSettings?.Dispose(); + _refreshMetadata?.Dispose(); + _newsletter?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } } } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index af5f58cb4..7007b3743 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -70,7 +70,16 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var movie in movies) { - var embyContent = await _repo.Get(movie.ImdbId); + EmbyContent embyContent = null; + if (movie.TheMovieDbId > 0) + { + embyContent = await _repo.GetByTheMovieDbId(movie.TheMovieDbId.ToString()); + } + else if(movie.ImdbId.HasValue()) + { + embyContent = await _repo.GetByImdbId(movie.ImdbId); + } + if (embyContent == null) { // We don't have this yet @@ -80,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby _log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty); movie.Available = true; + movie.MarkedAsAvailable = DateTime.Now; if (movie.Available) { var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty; @@ -112,12 +122,53 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var child in tv) { + + var useImdb = false; + var useTvDb = false; + if (child.ParentRequest.ImdbId.HasValue()) + { + useImdb = true; + } + + if (child.ParentRequest.TvDbId.ToString().HasValue()) + { + useTvDb = true; + } + var tvDbId = child.ParentRequest.TvDbId; - var seriesEpisodes = embyEpisodes.Where(x => x.Series.ProviderId == tvDbId.ToString()); + var imdbId = child.ParentRequest.ImdbId; + IQueryable seriesEpisodes = null; + if (useImdb) + { + seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); + } + + if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any())) + { + seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); + } + + if (seriesEpisodes == null) + { + continue; + } + + if (!seriesEpisodes.Any()) + { + // Let's try and match the series by name + seriesEpisodes = embyEpisodes.Where(x => + x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase)); + } + foreach (var season in child.SeasonRequests) { foreach (var episode in season.Episodes) { + if (episode.Available) + { + continue; + } + var foundEp = await seriesEpisodes.FirstOrDefaultAsync( x => x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == episode.Season.SeasonNumber); @@ -135,13 +186,14 @@ namespace Ombi.Schedule.Jobs.Emby { // We have fulfulled this request! child.Available = true; + child.MarkedAsAvailable = DateTime.Now; BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, - RequestId = child.ParentRequestId, + RequestId = child.Id, RequestType = RequestType.TvShow, - Recipient = child.RequestedUser.Email, + Recipient = child.RequestedUser.Email })); } } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 9d054ea7c..7002eec41 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -9,6 +9,7 @@ using Ombi.Api.Emby.Models.Movie; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Schedule.Jobs.Ombi; using Ombi.Store.Entities; using Ombi.Store.Repository; using Serilog; @@ -19,13 +20,14 @@ namespace Ombi.Schedule.Jobs.Emby public class EmbyContentSync : IEmbyContentSync { public EmbyContentSync(ISettingsService settings, IEmbyApi api, ILogger logger, - IEmbyContentRepository repo, IEmbyEpisodeSync epSync) + IEmbyContentRepository repo, IEmbyEpisodeSync epSync, IRefreshMetadata metadata) { _logger = logger; _settings = settings; _api = api; _repo = repo; _episodeSync = epSync; + _metadata = metadata; _settings.ClearCache(); } @@ -34,19 +36,30 @@ namespace Ombi.Schedule.Jobs.Emby private readonly IEmbyApi _api; private readonly IEmbyContentRepository _repo; private readonly IEmbyEpisodeSync _episodeSync; + private readonly IRefreshMetadata _metadata; public async Task Start() { - var embySettings = await _settings.GetSettingsAsync(); + var embySettings = await _settings.GetSettingsAsync(); if (!embySettings.Enable) return; foreach (var server in embySettings.Servers) - await StartServerCache(server); + { + try + { + await StartServerCache(server); + } + catch (Exception e) + { + _logger.LogError(e, "Exception when caching Emby for server {0}", server.Name); + } + } // Episodes BackgroundJob.Enqueue(() => _episodeSync.Start()); + BackgroundJob.Enqueue(() => _metadata.Start()); } @@ -55,78 +68,126 @@ namespace Ombi.Schedule.Jobs.Emby if (!ValidateSettings(server)) return; - var movies = await _api.GetAllMovies(server.ApiKey, server.AdministratorId, server.FullUri); - var mediaToAdd = new List(); - foreach (var movie in movies.Items) + //await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); + //await _repo.ExecuteSql("DELETE FROM EmbyContent"); + + var movies = await _api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); + var totalCount = movies.TotalRecordCount; + var processed = 1; + + var mediaToAdd = new HashSet(); + + while (processed < totalCount) { - if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase)) + foreach (var movie in movies.Items) { - var movieInfo = - await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); - foreach (var item in movieInfo.Items) + if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase)) { - var info = await _api.GetMovieInformation(item.Id, server.ApiKey, - server.AdministratorId, server.FullUri); - await ProcessMovies(info, mediaToAdd); + var movieInfo = + await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); + foreach (var item in movieInfo.Items) + { + await ProcessMovies(item, mediaToAdd); + } + + processed++; + } + else + { + processed++; + // Regular movie + await ProcessMovies(movie, mediaToAdd); } } - else - { - // Regular movie - var movieInfo = await _api.GetMovieInformation(movie.Id, server.ApiKey, - server.AdministratorId, server.FullUri); - await ProcessMovies(movieInfo, mediaToAdd); - } + // Get the next batch + movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); + } - // TV Time - var tv = await _api.GetAllShows(server.ApiKey, server.AdministratorId, server.FullUri); - foreach (var tvShow in tv.Items) + + // TV Time + var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); + var totalTv = tv.TotalRecordCount; + processed = 1; + while (processed < totalTv) { - var tvInfo = await _api.GetSeriesInformation(tvShow.Id, server.ApiKey, server.AdministratorId, - server.FullUri); - if (string.IsNullOrEmpty(tvInfo.ProviderIds?.Tvdb)) + foreach (var tvShow in tv.Items) { - Log.Error("Provider Id on tv {0} is null", tvShow.Name); - continue; - } + try + { - var existingTv = await _repo.GetByEmbyId(tvShow.Id); - if (existingTv == null) - mediaToAdd.Add(new EmbyContent + processed++; + if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + { + _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); + continue; + } + + var existingTv = await _repo.GetByEmbyId(tvShow.Id); + if (existingTv == null) + { + _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); + mediaToAdd.Add(new EmbyContent + { + TvDbId = tvShow.ProviderIds?.Tvdb, + ImdbId = tvShow.ProviderIds?.Imdb, + TheMovieDbId = tvShow.ProviderIds?.Tmdb, + Title = tvShow.Name, + Type = EmbyMediaType.Series, + EmbyId = tvShow.Id, + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), + AddedAt = DateTime.UtcNow + }); + } + else + { + _logger.LogDebug("We already have TV Show {0}", tvShow.Name); + } + + } + catch (Exception) { - ProviderId = tvInfo.ProviderIds.Tvdb, - Title = tvInfo.Name, - Type = EmbyMediaType.Series, - EmbyId = tvShow.Id, - AddedAt = DateTime.UtcNow - }); + + throw; + } + } + // Get the next batch + tv = await _api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); } if (mediaToAdd.Any()) await _repo.AddRange(mediaToAdd); } - private async Task ProcessMovies(MovieInformation movieInfo, ICollection content) + private async Task ProcessMovies(EmbyMovie movieInfo, ICollection content) { - if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb)) - { - Log.Error("Provider Id on movie {0} is null", movieInfo.Name); - return; - } // Check if it exists var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); - - if (existingMovie == null) + var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id); + if (existingMovie == null && !alreadyGoingToAdd) + { + _logger.LogDebug("Adding new movie {0}", movieInfo.Name); content.Add(new EmbyContent { - ProviderId = movieInfo.ProviderIds.Imdb, + ImdbId = movieInfo.ProviderIds.Imdb, + TheMovieDbId = movieInfo.ProviderIds?.Tmdb, Title = movieInfo.Name, Type = EmbyMediaType.Movie, EmbyId = movieInfo.Id, + Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), AddedAt = DateTime.UtcNow, }); + } + else + { + // we have this + _logger.LogDebug("We already have movie {0}", movieInfo.Name); + } } private bool ValidateSettings(EmbyServers server) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index 749abd761..f66ff89ab 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -73,47 +73,51 @@ namespace Ombi.Schedule.Jobs.Emby private async Task CacheEpisodes(EmbyServers server) { - var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, server.AdministratorId, server.FullUri); - var epToAdd = new List(); - - foreach (var ep in allEpisodes.Items) + var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); + var total = allEpisodes.TotalRecordCount; + var processed = 1; + var epToAdd = new HashSet(); + while (processed < total) { - if (ep.LocationType.Equals("Virtual", StringComparison.CurrentCultureIgnoreCase)) + foreach (var ep in allEpisodes.Items) { - // This means that we don't actully have the file, it's just Emby being nice and showing future stuff - continue; - } + processed++; + // Let's make sure we have the parent request, stop those pesky forign key errors, + // Damn me having data integrity + var parent = await _repo.GetByEmbyId(ep.SeriesId); + if (parent == null) + { + _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", + ep.Name); + continue; + } - var epInfo = await _api.GetEpisodeInformation(ep.Id, server.ApiKey, server.AdministratorId, server.FullUri); - if (epInfo?.ProviderIds?.Tvdb == null) - { - continue; - } + var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); + // Make sure it's not in the hashset too + var existingInList = epToAdd.Any(x => x.EmbyId == ep.Id); - // Let's make sure we have the parent request, stop those pesky forign key errors, - // Damn me having data integrity - var parent = await _repo.GetByEmbyId(epInfo.SeriesId); - if (parent == null) - { - _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", ep.Name); - continue; - } - - var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); - if (existingEpisode == null) - { - // add it - epToAdd.Add(new EmbyEpisode + if (existingEpisode == null && !existingInList) { - EmbyId = ep.Id, - EpisodeNumber = ep.IndexNumber, - SeasonNumber = ep.ParentIndexNumber, - ParentId = ep.SeriesId, - ProviderId = epInfo.ProviderIds.Tvdb, - Title = ep.Name, - AddedAt = DateTime.UtcNow - }); + _logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName); + // add it + epToAdd.Add(new EmbyEpisode + { + EmbyId = ep.Id, + EpisodeNumber = ep.IndexNumber, + SeasonNumber = ep.ParentIndexNumber, + ParentId = ep.SeriesId, + TvDbId = ep.ProviderIds.Tvdb, + TheMovieDbId = ep.ProviderIds.Tmdb, + ImdbId = ep.ProviderIds.Imdb, + Title = ep.Name, + AddedAt = DateTime.UtcNow + }); + } } + + await _repo.AddRange(epToAdd); + epToAdd.Clear(); + allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); } if (epToAdd.Any()) diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs new file mode 100644 index 000000000..56444b105 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Store.Entities; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public interface ILidarrAlbumSync + { + Task CacheContent(); + void Dispose(); + Task> GetCachedContent(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs new file mode 100644 index 000000000..1d3424756 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Store.Entities; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public interface ILidarrArtistSync + { + Task CacheContent(); + void Dispose(); + Task> GetCachedContent(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs new file mode 100644 index 000000000..f0c679229 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public interface ILidarrAvailabilityChecker + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs new file mode 100644 index 000000000..2e32b6478 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr; +using Ombi.Api.Radarr; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public class LidarrAlbumSync : ILidarrAlbumSync + { + public LidarrAlbumSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IOmbiContext ctx, + IBackgroundJobClient job, ILidarrAvailabilityChecker availability) + { + _lidarrSettings = lidarr; + _lidarrApi = lidarrApi; + _logger = log; + _ctx = ctx; + _job = job; + _availability = availability; + _lidarrSettings.ClearCache(); + } + + private readonly ISettingsService _lidarrSettings; + private readonly ILidarrApi _lidarrApi; + private readonly ILogger _logger; + private readonly IOmbiContext _ctx; + private readonly IBackgroundJobClient _job; + private readonly ILidarrAvailabilityChecker _availability; + + public async Task CacheContent() + { + try + { + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + try + { + var albums = await _lidarrApi.GetAllAlbums(settings.ApiKey, settings.FullUri); + if (albums != null && albums.Any()) + { + // Let's remove the old cached data + await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrAlbumCache"); + + var albumCache = new List(); + foreach (var a in albums) + { + if (a.id > 0) + { + albumCache.Add(new LidarrAlbumCache + { + ArtistId = a.artistId, + ForeignAlbumId = a.foreignAlbumId, + ReleaseDate = a.releaseDate, + TrackCount = a.currentRelease.trackCount, + Monitored = a.monitored, + Title = a.title, + PercentOfTracks = a.statistics?.percentOfEpisodes ?? 0m, + AddedAt = DateTime.Now, + }); + } + } + await _ctx.LidarrAlbumCache.AddRangeAsync(albumCache); + + await _ctx.SaveChangesAsync(); + } + } + catch (System.Exception ex) + { + _logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr Album"); + } + + _job.Enqueue(() => _availability.Start()); + } + } + catch (Exception) + { + _logger.LogInformation(LoggingEvents.LidarrArtistCache, "Lidarr is not setup, cannot cache Album"); + } + } + + public async Task> GetCachedContent() + { + return await _ctx.LidarrAlbumCache.ToListAsync(); + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _ctx?.Dispose(); + _lidarrSettings?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs new file mode 100644 index 000000000..ac6264e3d --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr; +using Ombi.Api.Radarr; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public class LidarrArtistSync : ILidarrArtistSync + { + public LidarrArtistSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IOmbiContext ctx, + IBackgroundJobClient background, ILidarrAlbumSync album) + { + _lidarrSettings = lidarr; + _lidarrApi = lidarrApi; + _logger = log; + _ctx = ctx; + _job = background; + _albumSync = album; + _lidarrSettings.ClearCache(); + } + + private readonly ISettingsService _lidarrSettings; + private readonly ILidarrApi _lidarrApi; + private readonly ILogger _logger; + private readonly IOmbiContext _ctx; + private readonly IBackgroundJobClient _job; + private readonly ILidarrAlbumSync _albumSync; + + public async Task CacheContent() + { + try + { + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + try + { + var artists = await _lidarrApi.GetArtists(settings.ApiKey, settings.FullUri); + if (artists != null && artists.Any()) + { + // Let's remove the old cached data + await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrArtistCache"); + + var artistCache = new List(); + foreach (var a in artists) + { + if (a.id > 0) + { + artistCache.Add(new LidarrArtistCache + { + ArtistId = a.id, + ArtistName = a.artistName, + ForeignArtistId = a.foreignArtistId, + Monitored = a.monitored + }); + } + } + await _ctx.LidarrArtistCache.AddRangeAsync(artistCache); + + await _ctx.SaveChangesAsync(); + } + } + catch (Exception ex) + { + _logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr"); + } + + _job.Enqueue(() => _albumSync.CacheContent()); + } + } + catch (Exception) + { + _logger.LogInformation(LoggingEvents.LidarrArtistCache, "Lidarr is not setup, cannot cache Artist"); + } + } + + public async Task> GetCachedContent() + { + return await _ctx.LidarrArtistCache.ToListAsync(); + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _ctx?.Dispose(); + _lidarrSettings?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs new file mode 100644 index 000000000..9db24784d --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Core.Notifications; +using Ombi.Helpers; +using Ombi.Notifications.Models; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Schedule.Jobs.Lidarr +{ + public class LidarrAvailabilityChecker : ILidarrAvailabilityChecker + { + public LidarrAvailabilityChecker(IMusicRequestRepository requests, IRepository albums, ILogger log, + IBackgroundJobClient job, INotificationService notification) + { + _cachedAlbums = albums; + _requestRepository = requests; + _logger = log; + _job = job; + _notificationService = notification; + } + + private readonly IMusicRequestRepository _requestRepository; + private readonly IRepository _cachedAlbums; + private readonly ILogger _logger; + private readonly IBackgroundJobClient _job; + private readonly INotificationService _notificationService; + + public async Task Start() + { + var allAlbumRequests = _requestRepository.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available); + var albumsToUpdate = new List(); + foreach (var request in allAlbumRequests) + { + // Check if we have it cached + var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId)); + if (cachedAlbum != null) + { + if (cachedAlbum.FullyAvailable) + { + request.Available = true; + request.MarkedAsAvailable = DateTime.Now; + albumsToUpdate.Add(request); + } + } + } + + foreach (var albumRequest in albumsToUpdate) + { + await _requestRepository.Update(albumRequest); + var recipient = albumRequest.RequestedUser.Email.HasValue() ? albumRequest.RequestedUser.Email : string.Empty; + + _logger.LogDebug("AlbumId: {0}, RequestUser: {1}", albumRequest.Id, recipient); + + _job.Enqueue(() => _notificationService.Publish(new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.RequestAvailable, + RequestId = albumRequest.Id, + RequestType = RequestType.Album, + Recipient = recipient, + })); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs index 61f4bd7c8..09b7d9858 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs @@ -1,46 +1,90 @@ using System.Text; +using Ombi.Helpers; namespace Ombi.Schedule.Jobs.Ombi { public abstract class HtmlTemplateGenerator { - protected virtual void AddParagraph(StringBuilder stringBuilder, string text, int fontSize = 14, string fontWeight = "normal") + protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url) { - stringBuilder.AppendFormat("

{0}

", text, fontSize, fontWeight); + sb.Append("
"); + sb.AppendFormat("", url); + sb.Append(""); + sb.Append(""); sb.Append(""); + sb.Append("
"); + sb.Append(""); } - protected virtual void AddImageInsideTable(StringBuilder sb, string url, int size = 400) + protected virtual void AddPosterInsideTable(StringBuilder sb, string url) { sb.Append(""); - sb.Append(""); - sb.Append(""); + sb.Append(""); } - protected virtual void TableData(StringBuilder sb) + protected virtual void AddInfoTable(StringBuilder sb) { sb.Append( - "
"); - sb.Append($""); - sb.Append("
"); + sb.AppendFormat("", url); } - protected virtual void Href(StringBuilder sb, string url) + protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url) { - sb.AppendFormat("", url); + if (url.HasValue()) + { + sb.Append(""); + sb.Append( + ""); + sb.Append(""); + } + + sb.Append("
"); + sb.AppendFormat("", mediaurl); + sb.AppendFormat( + "", + url); + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + ""); + sb.Append(""); } - protected virtual void EndTag(StringBuilder sb, string tag) + protected virtual void AddTitle(StringBuilder sb, string url, string title) { - sb.AppendFormat("", tag); + sb.Append(""); + sb.Append(""); + sb.Append(""); } - protected virtual void Header(StringBuilder sb, int size, string text, string fontWeight = "normal") + protected virtual void AddParagraph(StringBuilder sb, string text) { - sb.AppendFormat( - "{1}", - size, text, fontWeight); + sb.Append(""); + sb.Append(""); + sb.Append(""); } + protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary) + { + sb.Append(""); + sb.Append(""); + sb.Append(""); + } + protected virtual void AddGenres(StringBuilder sb, string text) + { + sb.Append(""); + sb.Append(""); + sb.Append(""); + } } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs new file mode 100644 index 000000000..fbd1e3aaf --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public interface IIssuesPurge : IBaseJob + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs index a08db74d0..ed13280b0 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs @@ -1,9 +1,11 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Ombi.Schedule.Jobs.Ombi { public interface IRefreshMetadata : IBaseJob { Task Start(); + Task ProcessPlexServerContent(IEnumerable contentIds); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs new file mode 100644 index 000000000..92ca31071 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class IssuesPurge : IIssuesPurge + { + public IssuesPurge(IRepository issuesRepo, ISettingsService issueSettings) + { + _issuesRepository = issuesRepo; + _settings = issueSettings; + _settings.ClearCache(); + } + + private readonly IRepository _issuesRepository; + private readonly ISettingsService _settings; + + public async Task Start() + { + var settings = await _settings.GetSettingsAsync(); + if (!settings.DeleteIssues) + { + return; + } + + var now = DateTime.Now.AddDays(-settings.DaysAfterResolvedToDelete).Date; + var resolved = _issuesRepository.GetAll().Where(x => x.Status == IssueStatus.Resolved); + var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date <= now); + + foreach (var d in toDelete) + { + d.Status = IssueStatus.Deleted; + } + + await _issuesRepository.SaveChangesAsync(); + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _issuesRepository?.Dispose(); + _settings?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index d8629e410..f152f6b4b 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -1,10 +1,16 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using MailKit; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr; +using Ombi.Api.Lidarr.Models; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze; @@ -14,6 +20,7 @@ using Ombi.Notifications; using Ombi.Notifications.Models; using Ombi.Notifications.Templates; using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.Notifications; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -25,7 +32,8 @@ namespace Ombi.Schedule.Jobs.Ombi public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository addedLog, IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService custom, ISettingsService emailSettings, INotificationTemplatesRepository templateRepo, - UserManager um, ISettingsService newsletter) + UserManager um, ISettingsService newsletter, ILogger log, + ILidarrApi lidarrApi, IRepository albumCache, ISettingsService lidarrSettings) { _plex = plex; _emby = emby; @@ -41,6 +49,11 @@ namespace Ombi.Schedule.Jobs.Ombi _emailSettings.ClearCache(); _customizationSettings.ClearCache(); _newsletterSettings.ClearCache(); + _log = log; + _lidarrApi = lidarrApi; + _lidarrAlbumRepository = albumCache; + _lidarrSettings = lidarrSettings; + _lidarrSettings.ClearCache(); } private readonly IPlexContentRepository _plex; @@ -54,6 +67,10 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ISettingsService _emailSettings; private readonly ISettingsService _newsletterSettings; private readonly UserManager _userManager; + private readonly ILogger _log; + private readonly ILidarrApi _lidarrApi; + private readonly IRepository _lidarrAlbumRepository; + private readonly ISettingsService _lidarrSettings; public async Task Start(NewsletterSettings settings, bool test) { @@ -73,143 +90,178 @@ namespace Ombi.Schedule.Jobs.Ombi return; } - var customization = await _customizationSettings.GetSettingsAsync(); - - // Get the Content - var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); - var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - - var addedLog = _recentlyAddedLog.GetAll(); - var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - - var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode).Select(x => x.ContentId); - var addedEmbyEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode).Select(x => x.ContentId); - - // Filter out the ones that we haven't sent yet - var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(x.Id)); - var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(x.Id)); + try + { - var plexEpisodesToSend = _plex.GetAllEpisodes().Include(x => x.Series).Where(x => !addedPlexEpisodesLogIds.Contains(x.Id)).AsNoTracking(); - var embyEpisodesToSend = _emby.GetAllEpisodes().Include(x => x.Series).Where(x => !addedEmbyEpisodesLogIds.Contains(x.Id)).AsNoTracking(); - var body = string.Empty; - if (test) - { - var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10); - var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10); - body = await BuildHtml(plexm, embym, plext, embyt); - } - else - { - body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend); - if (body.IsNullOrEmpty()) + var customization = await _customizationSettings.GetSettingsAsync(); + // Get the Content + var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); + var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); + var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking(); + + var addedLog = _recentlyAddedLog.GetAll(); + var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId); + + var addedPlexEpisodesLogIds = + addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode); + var addedEmbyEpisodesLogIds = + addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode); + + + // Filter out the ones that we haven't sent yet + var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && x.HasTheMovieDb && !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); + var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && x.HasTheMovieDb && !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); + var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet(); + _log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count()); + _log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count()); + _log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count()); + + var plexEpisodesToSend = + FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), addedPlexEpisodesLogIds); + var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), + addedEmbyEpisodesLogIds); + + _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); + _log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count()); + var body = string.Empty; + if (test) { - return; + var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); + var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie ).OrderByDescending(x => x.AddedAt).Take(10); + var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); + var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); + var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); + body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings); } - - } - - if (!test) - { - // Get the users to send it to - var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); - if (!users.Any()) + else { - return; + body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings); + if (body.IsNullOrEmpty()) + { + return; + } } - var emailTasks = new List(); - foreach (var user in users) + + if (!test) { - if (user.Email.IsNullOrEmpty()) + // Get the users to send it to + var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.ReceivesNewsletter); + if (!users.Any()) { - continue; + return; } - var messageContent = ParseTemplate(template, customization, user); - var email = new NewsletterTemplate(); + foreach (var emails in settings.ExternalEmails) + { + users.Add(new OmbiUser + { + UserName = emails, + Email = emails + }); + } + var emailTasks = new List(); + foreach (var user in users) + { + // Get the users to send it to + if (user.Email.IsNullOrEmpty()) + { + continue; + } - var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); + var messageContent = ParseTemplate(template, customization, user); + var email = new NewsletterTemplate(); - emailTasks.Add(_email.Send( - new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email }, - emailSettings)); - } + var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); - // Now add all of this to the Recently Added log - var recentlyAddedLog = new HashSet(); - foreach (var p in plexContentMoviesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog + emailTasks.Add(_email.Send( + new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email }, + emailSettings)); + } + + // Now add all of this to the Recently Added log + var recentlyAddedLog = new HashSet(); + foreach (var p in plexContentMoviesToSend) { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Parent, - ContentId = p.Id - }); + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Parent, + ContentId = StringHelper.IntParseLinq(p.TheMovieDbId), + }); - } + } - foreach (var p in plexEpisodesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog + foreach (var p in plexEpisodesToSend) { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Episode, - ContentId = p.Id - }); - } + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Episode, + ContentId = StringHelper.IntParseLinq(p.Series.TvDbId), + EpisodeNumber = p.EpisodeNumber, + SeasonNumber = p.SeasonNumber + }); + } + foreach (var e in embyContentMoviesToSend) + { + if (e.Type == EmbyMediaType.Movie) + { + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Emby, + ContentType = ContentType.Parent, + ContentId = StringHelper.IntParseLinq(e.TheMovieDbId), + }); + } + } - foreach (var e in embyContentMoviesToSend) - { - if (e.Type == EmbyMediaType.Movie) + foreach (var p in embyEpisodesToSend) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentType = ContentType.Parent, - ContentId = e.Id + ContentType = ContentType.Episode, + ContentId = StringHelper.IntParseLinq(p.Series.TvDbId), + EpisodeNumber = p.EpisodeNumber, + SeasonNumber = p.SeasonNumber }); } + await _recentlyAddedLog.AddRange(recentlyAddedLog); + await Task.WhenAll(emailTasks.ToArray()); } - - foreach (var p in embyEpisodesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog - { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Emby, - ContentType = ContentType.Episode, - ContentId = p.Id - }); - } - await _recentlyAddedLog.AddRange(recentlyAddedLog); - - await Task.WhenAll(emailTasks.ToArray()); - } - else - { - var admins = await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin); - foreach (var a in admins) + else { - if (a.Email.IsNullOrEmpty()) + var admins = await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin); + foreach (var a in admins) { - continue; - } - var messageContent = ParseTemplate(template, customization, a); + if (a.Email.IsNullOrEmpty()) + { + continue; + } + var messageContent = ParseTemplate(template, customization, a); - var email = new NewsletterTemplate(); + var email = new NewsletterTemplate(); - var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); + var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); - await _email.Send( - new NotificationMessage { Message = html, Subject = messageContent.Subject, To = a.Email }, - emailSettings); + await _email.Send( + new NotificationMessage { Message = html, Subject = messageContent.Subject, To = a.Email }, + emailSettings); + } } + + } + catch (Exception e) + { + _log.LogError(e, "Error when attempting to create newsletter"); + throw; } } @@ -219,6 +271,40 @@ namespace Ombi.Schedule.Jobs.Ombi await Start(newsletterSettings, false); } + private HashSet FilterPlexEpisodes(IEnumerable source, IQueryable recentlyAdded) + { + var itemsToReturn = new HashSet(); + foreach (var ep in source) + { + var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); + if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) + { + continue; + } + + itemsToReturn.Add(ep); + } + + return itemsToReturn; + } + + private HashSet FilterEmbyEpisodes(IEnumerable source, IQueryable recentlyAdded) + { + var itemsToReturn = new HashSet(); + foreach (var ep in source) + { + var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); + if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) + { + continue; + } + + itemsToReturn.Add(ep); + } + + return itemsToReturn; + } + private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings, OmbiUser username) { var resolver = new NotificationMessageResolver(); @@ -229,24 +315,65 @@ namespace Ombi.Schedule.Jobs.Ombi return resolver.ParseMessage(template, curlys); } - private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend, IQueryable plexEpisodes, IQueryable embyEp) + private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend, + HashSet plexEpisodes, HashSet embyEp, HashSet albums, NewsletterSettings settings) { var sb = new StringBuilder(); var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie); var embyMovies = embyContentToSend.Where(x => x.Type == EmbyMediaType.Movie); - if (plexMovies.Any() || embyMovies.Any()) + if ((plexMovies.Any() || embyMovies.Any()) && !settings.DisableMovies) { - sb.Append("

New Movies:



"); + sb.Append("

New Movies



"); + sb.Append( + "
"); + if(url.HasValue()) sb.AppendFormat("", url); + sb.AppendFormat("

{0}

", title); + if (url.HasValue()) sb.Append("
"); + sb.Append("
"); + sb.AppendFormat("

{0}

", text); + sb.Append("
"); + sb.AppendFormat("

{0}

", episodes); + sb.AppendFormat("
{0}
", summary); + sb.Append("
"); + sb.AppendFormat("{0}", text); + sb.Append("
"); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append("
"); + sb.Append(""); + sb.Append(""); await ProcessPlexMovies(plexMovies, sb); await ProcessEmbyMovies(embyMovies, sb); + sb.Append(""); + sb.Append("
"); + sb.Append("
"); } - if (plexEpisodes.Any() || embyEp.Any()) + if ((plexEpisodes.Any() || embyEp.Any()) && !settings.DisableTv) { - sb.Append("

New Episodes:



"); + sb.Append("

New TV



"); + sb.Append( + ""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append("
"); + sb.Append(""); + sb.Append(""); await ProcessPlexTv(plexEpisodes, sb); await ProcessEmbyTv(embyEp, sb); + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + } + + + if (albums.Any() && !settings.DisableMusic) + { + sb.Append("

New Albums



"); + sb.Append( + ""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append("
"); + sb.Append(""); + sb.Append(""); + await ProcessAlbums(albums, sb); + sb.Append(""); + sb.Append("
"); + sb.Append("
"); } return sb.ToString(); @@ -254,8 +381,7 @@ namespace Ombi.Schedule.Jobs.Ombi private async Task ProcessPlexMovies(IQueryable plexContentToSend, StringBuilder sb) { - sb.Append( - ""); + int count = 0; var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt); foreach (var content in ordered) { @@ -265,69 +391,125 @@ namespace Ombi.Schedule.Jobs.Ombi continue; } var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId); + var mediaurl = content.Url; if (info == null) { continue; } try { - CreateMovieHtmlContent(sb, info); + CreateMovieHtmlContent(sb, info, mediaurl); + count += 1; } catch (Exception e) { - Console.WriteLine(e); - throw; + _log.LogError(e, "Error when Processing Plex Movies {0}", info.Title); } finally { EndLoopHtml(sb); } + + if (count == 2) + { + count = 0; + sb.Append(""); + sb.Append(""); + } + } + } + private async Task ProcessAlbums(HashSet albumsToSend, StringBuilder sb) + { + var settings = await _lidarrSettings.GetSettingsAsync(); + int count = 0; + var ordered = albumsToSend.OrderByDescending(x => x.AddedAt); + foreach (var content in ordered) + { + var info = await _lidarrApi.GetAlbumByForeignId(content.ForeignAlbumId, settings.ApiKey, settings.FullUri); + if (info == null) + { + continue; + } + try + { + CreateAlbumHtmlContent(sb, info); + count += 1; + } + catch (Exception e) + { + _log.LogError(e, "Error when Processing Lidarr Album {0}", info.title); + } + finally + { + EndLoopHtml(sb); + } + + if (count == 2) + { + count = 0; + sb.Append(""); + sb.Append(""); + } } } private async Task ProcessEmbyMovies(IQueryable embyContent, StringBuilder sb) { - sb.Append( - "
"); + int count = 0; var ordered = embyContent.OrderByDescending(x => x.AddedAt); foreach (var content in ordered) { - var imdbId = content.ProviderId; - var findResult = await _movieApi.Find(imdbId, ExternalSource.imdb_id); - var result = findResult.movie_results?.FirstOrDefault(); - if(result == null) + var theMovieDbId = content.TheMovieDbId; + if (!content.TheMovieDbId.HasValue()) { - continue; + var imdbId = content.ImdbId; + var findResult = await _movieApi.Find(imdbId, ExternalSource.imdb_id); + var result = findResult.movie_results?.FirstOrDefault(); + if (result == null) + { + continue; + } + + theMovieDbId = result.id.ToString(); } - var info = await _movieApi.GetMovieInformationWithExtraInfo(result.id); + + var mediaurl = content.Url; + var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId)); if (info == null) { continue; } try { - CreateMovieHtmlContent(sb, info); + CreateMovieHtmlContent(sb, info, mediaurl); + count += 1; } catch (Exception e) { - Console.WriteLine(e); - throw; + _log.LogError(e, "Error when processing Emby Movies {0}", info.Title); } finally { EndLoopHtml(sb); } + + if (count == 2) + { + count = 0; + sb.Append(""); + sb.Append(""); + } } } - private void CreateMovieHtmlContent(StringBuilder sb, MovieResponseDto info) + private void CreateMovieHtmlContent(StringBuilder sb, MovieResponseDto info, string mediaurl) { - AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); + AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}"); + AddPosterInsideTable(sb, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); - sb.Append(""); - TableData(sb); + AddMediaServerUrl(sb, mediaurl, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); + AddInfoTable(sb); - Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); var releaseDate = string.Empty; try { @@ -337,19 +519,60 @@ namespace Ombi.Schedule.Jobs.Ombi { // Swallow, couldn't parse the date } - Header(sb, 3, $"{info.Title} {releaseDate}"); - EndTag(sb, "a"); + + AddTitle(sb, $"https://www.imdb.com/title/{info.ImdbId}/", $"{info.Title} {releaseDate}"); + + var summary = info.Overview; + if (summary.Length > 280) + { + summary = summary.Remove(280); + summary = summary + "...

"; + } + AddParagraph(sb, summary); if (info.Genres.Any()) { - AddParagraph(sb, - $"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + AddGenres(sb, + $"Genres: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + } + } + + private void CreateAlbumHtmlContent(StringBuilder sb, AlbumLookup info) + { + var cover = info.images + .FirstOrDefault(x => x.coverType.Equals("cover", StringComparison.InvariantCultureIgnoreCase))?.url; + if (cover.IsNullOrEmpty()) + { + cover = info.remoteCover; + } + AddBackgroundInsideTable(sb, cover); + var disk = info.images + .FirstOrDefault(x => x.coverType.Equals("disc", StringComparison.InvariantCultureIgnoreCase))?.url; + if (disk.IsNullOrEmpty()) + { + disk = info.remoteCover; } + AddPosterInsideTable(sb, disk); + + AddMediaServerUrl(sb, string.Empty, string.Empty); + AddInfoTable(sb); - AddParagraph(sb, info.Overview); + var releaseDate = $"({info.releaseDate.Year})"; + + AddTitle(sb, string.Empty, $"{info.title} {releaseDate}"); + + var summary = info.artist?.artistName ?? string.Empty; + if (summary.Length > 280) + { + summary = summary.Remove(280); + summary = summary + "...

"; + } + AddParagraph(sb, summary); + + AddGenres(sb, $"Type: {info.albumType}"); } - private async Task ProcessPlexTv(IQueryable plexContent, StringBuilder sb) + private async Task ProcessPlexTv(HashSet plexContent, StringBuilder sb) { var series = new List(); foreach (var plexEpisode in plexContent) @@ -370,62 +593,75 @@ namespace Ombi.Schedule.Jobs.Ombi } } + int count = 0; var orderedTv = series.OrderByDescending(x => x.AddedAt); - sb.Append( - "
"); foreach (var t in orderedTv) { - try + if (!t.HasTvDb) { - if (!t.HasTvDb) + // We may need to use themoviedb for the imdbid or their own id to get info + if (t.HasTheMovieDb) { - // We may need to use themoviedb for the imdbid or their own id to get info - if (t.HasTheMovieDb) + int.TryParse(t.TheMovieDbId, out var movieId); + var externals = await _movieApi.GetTvExternals(movieId); + if (externals == null || externals.tvdb_id <= 0) { - int.TryParse(t.TheMovieDbId, out var movieId); - var externals = await _movieApi.GetTvExternals(movieId); - if (externals == null || externals.tvdb_id <= 0) - { - continue; - } - t.TvDbId = externals.tvdb_id.ToString(); + continue; } - // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. - //else if(t.HasImdb) - //{ - // // Check the imdbid - // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); - // if (externals?.tv_results == null || externals.tv_results.Length <= 0) - // { - // continue; - // } - // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); - //} - + t.TvDbId = externals.tvdb_id.ToString(); } + // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. + //else if(t.HasImdb) + //{ + // // Check the imdbid + // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); + // if (externals?.tv_results == null || externals.tv_results.Length <= 0) + // { + // continue; + // } + // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); + //} - int.TryParse(t.TvDbId, out var tvdbId); - var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); - if (info == null) - { - continue; - } + } + + int.TryParse(t.TvDbId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + + try + { var banner = info.image?.original; if (!string.IsNullOrEmpty(banner)) { banner = banner.Replace("http", "https"); // Always use the Https banners } - AddImageInsideTable(sb, banner); - - sb.Append(""); - sb.Append( - ""); + sb.Append(""); + } + } + } + + public string BuildEpisodeList(IEnumerable orderedEpisodes) + { + var epSb = new StringBuilder(); + var previousEpisodes = new List(); + var previousEpisode = -1; + foreach (var ep in orderedEpisodes) + { + if (ep - 1 == previousEpisode) + { + // This is the next one + previousEpisodes.Add(ep); + } + else + { + if (previousEpisodes.Count > 1) + { + // End it + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, "); + } + else if (previousEpisodes.Count == 1) + { + epSb.Append($"{previousEpisodes.FirstOrDefault()}, "); + } + // New one + previousEpisodes.Clear(); + previousEpisodes.Add(ep); } + previousEpisode = ep; + } + + if (previousEpisodes.Count > 1) + { + // Got some left over + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}"); + } + else if(previousEpisodes.Count == 1) + { + epSb.Append(previousEpisodes.FirstOrDefault()); } - sb.Append("
"); - var title = $"{t.Title} ({t.ReleaseYear})"; + var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId); + if (tvInfo != null && tvInfo.backdrop_path.HasValue()) + { - Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); - Header(sb, 3, title); - EndTag(sb, "a"); + AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}"); + } + else + { + AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/"); + } + AddPosterInsideTable(sb, banner); + AddMediaServerUrl(sb, t.Url, banner); + AddInfoTable(sb); + + var title = ""; + if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) + { + title = $"{t.Title} ({info.premiered.Remove(4)})"; + } else + { + title = $"{t.Title}"; + } + AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); // Group by the season number var results = t.Episodes.GroupBy(p => p.SeasonNumber, @@ -437,47 +673,92 @@ namespace Ombi.Schedule.Jobs.Ombi ); // Group the episodes + var finalsb = new StringBuilder(); foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var epSb = new StringBuilder(); - for (var i = 0; i < orderedEpisodes.Count; i++) - { - var ep = orderedEpisodes[i]; - if (i < orderedEpisodes.Count - 1) - { - epSb.Append($"{ep.EpisodeNumber},"); - } - else - { - epSb.Append($"{ep.EpisodeNumber}"); - } + var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); + finalsb.Append("
"); + } - } - AddParagraph(sb, $"Season: {epInformation.SeasonNumber}, Episode: {epSb}"); + var summary = info.summary; + if (summary.Length > 280) + { + summary = summary.Remove(280); + summary = summary + "...

"; } + AddTvParagraph(sb, finalsb.ToString(), summary); if (info.genres.Any()) { - AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); } - - AddParagraph(sb, info.summary); + } catch (Exception e) { - //Log.Error(e); + _log.LogError(e, "Error when processing Plex TV {0}", t.Title); } finally { EndLoopHtml(sb); + count += 1; + } + + if (count == 2) + { + count = 0; + sb.Append("


"); + return epSb.ToString(); } - private async Task ProcessEmbyTv(IQueryable embyContent, StringBuilder sb) + private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb) { var series = new List(); foreach (var episode in embyContent) @@ -496,33 +777,55 @@ namespace Ombi.Schedule.Jobs.Ombi series.Add(episode.Series); } } + + int count = 0; var orderedTv = series.OrderByDescending(x => x.AddedAt); - sb.Append( - ""); foreach (var t in orderedTv) { + if (!t.TvDbId.HasValue()) + { + continue; + } + + int.TryParse(t.TvDbId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + try { - int.TryParse(t.ProviderId, out var tvdbId); - var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); - if (info == null) - { - continue; - } var banner = info.image?.original; if (!string.IsNullOrEmpty(banner)) { banner = banner.Replace("http", "https"); // Always use the Https banners } - AddImageInsideTable(sb, banner); - sb.Append(""); - sb.Append( - ""); + sb.Append(""); } } - sb.Append("
"); + var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId); + if (tvInfo != null && tvInfo.backdrop_path.HasValue()) + { - Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); - Header(sb, 3, t.Title); - EndTag(sb, "a"); + AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}"); + } + else + { + AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/"); + } + AddPosterInsideTable(sb, banner); + AddMediaServerUrl(sb, t.Url, banner); + AddInfoTable(sb); + + var title = ""; + if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) + { + title = $"{t.Title} ({info.premiered.Remove(4)})"; + } + else + { + title = $"{t.Title}"; + } + AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); // Group by the season number var results = t.Episodes?.GroupBy(p => p.SeasonNumber, @@ -534,54 +837,60 @@ namespace Ombi.Schedule.Jobs.Ombi ); // Group the episodes + var finalsb = new StringBuilder(); foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var epSb = new StringBuilder(); - for (var i = 0; i < orderedEpisodes.Count; i++) - { - var ep = orderedEpisodes[i]; - if (i < orderedEpisodes.Count - 1) - { - epSb.Append($"{ep.EpisodeNumber},"); - } - else - { - epSb.Append($"{ep.EpisodeNumber}"); - } + var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); + finalsb.Append("
"); + } - } - AddParagraph(sb, $"Season: {epInformation.SeasonNumber}, Episode: {epSb}"); + var summary = info.summary; + if (summary.Length > 280) + { + summary = summary.Remove(280); + summary = summary + "...

"; } + AddTvParagraph(sb, finalsb.ToString(), summary); if (info.genres.Any()) { - AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); } - - AddParagraph(sb, info.summary); + } catch (Exception e) { - //Log.Error(e); + _log.LogError(e, "Error when processing Emby TV {0}", t.Title); } finally { EndLoopHtml(sb); + count += 1; + } + + if (count == 2) + { + count = 0; + sb.Append("


"); } private void EndLoopHtml(StringBuilder sb) { //NOTE: BR have to be in TD's as per html spec or it will be put outside of the table... //Source: http://stackoverflow.com/questions/6588638/phantom-br-tag-rendered-by-browsers-prior-to-table-tag - sb.Append("
"); - sb.Append("
"); - sb.Append("
"); + sb.Append("
"); sb.Append("
"); + sb.Append("
"); + sb.Append("
- - - - diff --git a/src/Ombi/ClientApp/app/settings/authentication/authentication.component.html b/src/Ombi/ClientApp/app/settings/authentication/authentication.component.html index 394716028..a270d513c 100644 --- a/src/Ombi/ClientApp/app/settings/authentication/authentication.component.html +++ b/src/Ombi/ClientApp/app/settings/authentication/authentication.component.html @@ -14,6 +14,13 @@ +
+
+ + +
+
+
@@ -26,13 +26,6 @@
-
-
- - -
-
-
diff --git a/src/Ombi/ClientApp/app/settings/update/update.component.ts b/src/Ombi/ClientApp/app/settings/update/update.component.ts index b0ba2f57d..df0e8b32e 100644 --- a/src/Ombi/ClientApp/app/settings/update/update.component.ts +++ b/src/Ombi/ClientApp/app/settings/update/update.component.ts @@ -35,6 +35,7 @@ export class UpdateComponent implements OnInit { scriptLocation: [x.scriptLocation], windowsService: [x.windowsService], windowsServiceName: [x.windowsServiceName], + testMode: [x.testMode], }); this.isWindows = x.isWindows; this.enableUpdateButton = x.autoUpdateEnabled; diff --git a/src/Ombi/ClientApp/app/shared/issues-report.component.ts b/src/Ombi/ClientApp/app/shared/issues-report.component.ts index de171d5c2..498ff1757 100644 --- a/src/Ombi/ClientApp/app/shared/issues-report.component.ts +++ b/src/Ombi/ClientApp/app/shared/issues-report.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { IIssueCategory, IIssues, IssueStatus, RequestType } from "./../interfaces"; -import { IssuesService, NotificationService } from "./../services"; +import { IIssueCategory, IIssues, IssueStatus, RequestType } from "../interfaces"; +import { IssuesService, NotificationService } from "../services"; @Component({ selector: "issue-report", diff --git a/src/Ombi/ClientApp/app/shared/shared.module.ts b/src/Ombi/ClientApp/app/shared/shared.module.ts index d20b5bcbb..b87c3f42d 100644 --- a/src/Ombi/ClientApp/app/shared/shared.module.ts +++ b/src/Ombi/ClientApp/app/shared/shared.module.ts @@ -2,10 +2,11 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { TranslateModule } from "@ngx-translate/core"; +import { TruncateModule } from "@yellowspot/ng-truncate"; import { IssuesReportComponent } from "./issues-report.component"; -import { SidebarModule } from "primeng/primeng"; +import { InputSwitchModule, SidebarModule } from "primeng/primeng"; @NgModule({ declarations: [ @@ -15,13 +16,17 @@ import { SidebarModule } from "primeng/primeng"; SidebarModule, FormsModule, CommonModule, + InputSwitchModule, + TruncateModule, ], exports: [ - TranslateModule, - CommonModule, + TranslateModule, + CommonModule, FormsModule, SidebarModule, IssuesReportComponent, + TruncateModule, + InputSwitchModule, ], }) export class SharedModule {} diff --git a/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.html b/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.html new file mode 100644 index 000000000..5f56ee710 --- /dev/null +++ b/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.html @@ -0,0 +1,66 @@ + + + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.ts b/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.ts new file mode 100644 index 000000000..1c77a0181 --- /dev/null +++ b/src/Ombi/ClientApp/app/usermanagement/addplexuser.component.ts @@ -0,0 +1,84 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap"; + +import { NotificationService, PlexService } from "../services"; + +import { IPlexSection, IPlexServersAdd } from "../interfaces"; + +@Component({ + selector: "ngbd-modal-content", + templateUrl: "./addplexuser.component.html", +}) +export class AddPlexUserComponent implements OnInit { + + @Input() public name: string; + + public plexServers: IPlexServersAdd[]; + public plexLibs: IPlexSection[]; + + public libsSelected: number[] = []; + + public form: FormGroup; + + constructor(public activeModal: NgbActiveModal, + private plexService: PlexService, + private notificationService: NotificationService, + private fb: FormBuilder) { + } + + public ngOnInit(): void { + this.form = this.fb.group({ + selectedServer: [null, Validators.required], + allLibsSelected: [true], + username:[null, Validators.required], + }); + this.getServers(); + } + + public getServers() { + this.plexService.getServersFromSettings().subscribe(x => { + if (x.success) { + this.plexServers = x.servers; + } + }); + } + + public getPlexLibs(machineId: string) { + this.plexService.getLibrariesFromSettings(machineId).subscribe(x => { + if (x.successful) { + this.plexLibs = x.data; + } + }); + } + + public selected() { + this.getPlexLibs(this.form.value.selectedServer); + } + + public checkedLib(checked: boolean, value: number) { + if(checked) { + this.libsSelected.push(value); + } else { + this.libsSelected = this.libsSelected.filter(v => v !== value); + } + } + + public onSubmit(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + const libs = form.value.allLibsSelected ? [] : this.libsSelected; + + this.plexService.addUserToServer({ username: form.value.username, machineIdentifier: form.value.selectedServer, libsSelected: libs }).subscribe(x => { + if (x.success) { + this.notificationService.success("User added to Plex"); + } else { + this.notificationService.error(x.error); + } + this.activeModal.close(); + }); + + } +} diff --git a/src/Ombi/ClientApp/app/usermanagement/updatedetails.component.ts b/src/Ombi/ClientApp/app/usermanagement/updatedetails.component.ts index 26cb3f16d..e44313ad7 100644 --- a/src/Ombi/ClientApp/app/usermanagement/updatedetails.component.ts +++ b/src/Ombi/ClientApp/app/usermanagement/updatedetails.component.ts @@ -19,7 +19,7 @@ export class UpdateDetailsComponent implements OnInit { this.identityService.getUser().subscribe(x => { const localUser = x as IUpdateLocalUser; this.form = this.fb.group({ - id:[localUser.id], + id: [localUser.id], username: [localUser.userName], emailAddress: [localUser.emailAddress, [Validators.email]], confirmNewPassword: [localUser.confirmNewPassword], diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html deleted file mode 100644 index 683bd5620..000000000 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html +++ /dev/null @@ -1,79 +0,0 @@ - -

Create User

- - - -
-
- - -
-
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts deleted file mode 100644 index 36b187e79..000000000 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; - -import { ICheckbox, IUser, UserType } from "../interfaces"; -import { IdentityService, NotificationService } from "../services"; - -@Component({ - templateUrl: "./usermanagement-add.component.html", -}) -export class UserManagementAddComponent implements OnInit { - public user: IUser; - public availableClaims: ICheckbox[]; - public confirmPass: ""; - - constructor(private identityService: IdentityService, - private notificationSerivce: NotificationService, - private router: Router) { } - - public ngOnInit() { - this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x); - this.user = { - alias: "", - claims: [], - emailAddress: "", - id: "", - password: "", - userName: "", - userType: UserType.LocalUser, - checked: false, - hasLoggedIn: false, - lastLoggedIn: new Date(), - episodeRequestLimit: 0, - movieRequestLimit: 0, - userAccessToken: "", - }; - } - - public create() { - this.user.claims = this.availableClaims; - - if (this.user.password) { - if (this.user.password !== this.confirmPass) { - this.notificationSerivce.error("Passwords do not match"); - return; - } - } - const hasClaims = this.availableClaims.some((item) => { - if (item.enabled) { return true; } - - return false; - }); - - if (!hasClaims) { - this.notificationSerivce.error("Please assign a role"); - return; - } - - this.identityService.createUser(this.user).subscribe(x => { - if (x.successful) { - this.notificationSerivce.success(`The user ${this.user.userName} has been created successfully`); - this.router.navigate(["usermanagement"]); - } else { - x.errors.forEach((val) => { - this.notificationSerivce.error(val); - }); - } - }); - } - -} diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html deleted file mode 100644 index 95a70cab0..000000000 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html +++ /dev/null @@ -1,70 +0,0 @@ -
-
-

User: {{user.userName}}

- - - - - - -
- - - - -
-
-
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.ts deleted file mode 100644 index e82ed2eba..000000000 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Component } from "@angular/core"; -import { Router } from "@angular/router"; -import { ConfirmationService } from "primeng/primeng"; - -import { ActivatedRoute } from "@angular/router"; -import { IUser } from "../interfaces"; -import { IdentityService } from "../services"; -import { NotificationService } from "../services"; - -@Component({ - templateUrl: "./usermanagement-edit.component.html", -}) -export class UserManagementEditComponent { - public user: IUser; - public userId: string; - - constructor(private identityService: IdentityService, - private route: ActivatedRoute, - private notificationService: NotificationService, - private router: Router, - private confirmationService: ConfirmationService) { - this.route.params - .subscribe((params: any) => { - this.userId = params.id; - - this.identityService.getUserById(this.userId).subscribe(x => { - this.user = x; - }); - }); - } - - public delete() { - - this.confirmationService.confirm({ - message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.", - header: "Are you sure?", - icon: "fa fa-trash", - accept: () => { - this.identityService.deleteUser(this.user).subscribe(x => { - if (x.successful) { - this.notificationService.success(`The user ${this.user.userName} was deleted`); - this.router.navigate(["usermanagement"]); - } else { - x.errors.forEach((val) => { - this.notificationService.error(val); - }); - } - - }); - }, - reject: () => { - return; - }, - }); - } - - public resetPassword() { - this.identityService.submitResetPassword(this.user.emailAddress).subscribe(x => { - if (x.successful) { - this.notificationService.success(`Sent reset password email to ${this.user.emailAddress}`); - this.router.navigate(["usermanagement"]); - } else { - x.errors.forEach((val) => { - this.notificationService.error(val); - }); - } - - }); - } - - public update() { - const hasClaims = this.user.claims.some((item) => { - if (item.enabled) { return true; } - - return false; - }); - - if (!hasClaims) { - this.notificationService.error("Please assign a role"); - return; - } - - this.identityService.updateUser(this.user).subscribe(x => { - if (x.successful) { - this.notificationService.success(`The user ${this.user.userName} has been updated successfully`); - this.router.navigate(["usermanagement"]); - } else { - x.errors.forEach((val) => { - this.notificationService.error(val); - }); - } - }); - } - -} diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html new file mode 100644 index 000000000..6f3ae63ee --- /dev/null +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -0,0 +1,221 @@ +

Create User

+

User: {{user.userName}}

+ +
+ + +
+
+ +
+ + + + +
+
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.ts new file mode 100644 index 000000000..5be0a4509 --- /dev/null +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.ts @@ -0,0 +1,186 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { ICheckbox, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces"; +import { IdentityService, NotificationService, RadarrService, SonarrService } from "../services"; + +import { ConfirmationService } from "primeng/primeng"; + +@Component({ + templateUrl: "./usermanagement-user.component.html", +}) +export class UserManagementUserComponent implements OnInit { + + public user: IUser; + public userId: string; + public availableClaims: ICheckbox[]; + public confirmPass: ""; + public notificationPreferences: INotificationPreferences[]; + + public sonarrQualities: ISonarrProfile[]; + public sonarrRootFolders: ISonarrRootFolder[]; + public radarrQualities: IRadarrProfile[]; + public radarrRootFolders: IRadarrRootFolder[]; + + public NotificationAgent = INotificationAgent; + public edit: boolean; + + constructor(private identityService: IdentityService, + private notificationService: NotificationService, + private router: Router, + private route: ActivatedRoute, + private confirmationService: ConfirmationService, + private sonarrService: SonarrService, + private radarrService: RadarrService) { + + this.route.params.subscribe((params: any) => { + if(params.id) { + this.userId = params.id; + this.edit = true; + this.identityService.getUserById(this.userId).subscribe(x => { + this.user = x; + }); + } + }); + } + + public ngOnInit() { + this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x); + if(this.edit) { + this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x); + } else { + this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x); + } + this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => this.sonarrQualities = x); + this.sonarrService.getRootFoldersWithoutSettings().subscribe(x => this.sonarrRootFolders = x); + this.radarrService.getQualityProfilesFromSettings().subscribe(x => this.radarrQualities = x); + this.radarrService.getRootFoldersFromSettings().subscribe(x => this.radarrRootFolders = x); + + if(!this.edit) { + this.user = { + alias: "", + claims: [], + emailAddress: "", + id: "", + password: "", + userName: "", + userType: UserType.LocalUser, + checked: false, + hasLoggedIn: false, + lastLoggedIn: new Date(), + episodeRequestLimit: 0, + movieRequestLimit: 0, + userAccessToken: "", + musicRequestLimit: 0, + episodeRequestQuota: null, + movieRequestQuota: null, + userQualityProfiles: { + radarrQualityProfile: 0, + radarrRootPath: 0, + sonarrQualityProfile: 0, + sonarrQualityProfileAnime: 0, + sonarrRootPath: 0, + sonarrRootPathAnime: 0, + }, + musicRequestQuota: null, + }; + } + } + + public create() { + this.user.claims = this.availableClaims; + + if (this.user.password) { + if (this.user.password !== this.confirmPass) { + this.notificationService.error("Passwords do not match"); + return; + } + } + const hasClaims = this.availableClaims.some((item) => { + if (item.enabled) { return true; } + + return false; + }); + + if (!hasClaims) { + this.notificationService.error("Please assign a role"); + return; + } + + this.identityService.createUser(this.user).subscribe(x => { + if (x.successful) { + this.notificationService.success(`The user ${this.user.userName} has been created successfully`); + this.router.navigate(["usermanagement"]); + } else { + x.errors.forEach((val) => { + this.notificationService.error(val); + }); + } + }); + } + + public delete() { + + this.confirmationService.confirm({ + message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.", + header: "Are you sure?", + icon: "fa fa-trash", + accept: () => { + this.identityService.deleteUser(this.user).subscribe(x => { + if (x.successful) { + this.notificationService.success(`The user ${this.user.userName} was deleted`); + this.router.navigate(["usermanagement"]); + } else { + x.errors.forEach((val) => { + this.notificationService.error(val); + }); + } + + }); + }, + reject: () => { + return; + }, + }); + } + + public resetPassword() { + this.identityService.submitResetPassword(this.user.emailAddress).subscribe(x => { + if (x.successful) { + this.notificationService.success(`Sent reset password email to ${this.user.emailAddress}`); + this.router.navigate(["usermanagement"]); + } else { + x.errors.forEach((val) => { + this.notificationService.error(val); + }); + } + + }); + } + + public update() { + const hasClaims = this.user.claims.some((item) => { + if (item.enabled) { return true; } + + return false; + }); + + if (!hasClaims) { + this.notificationService.error("Please assign a role"); + return; + } + + this.identityService.updateUser(this.user).subscribe(x => { + if (x.successful) { + this.identityService.updateNotificationPreferences(this.notificationPreferences).subscribe(); + this.notificationService.success(`The user ${this.user.userName} has been updated successfully`); + this.router.navigate(["usermanagement"]); + } else { + x.errors.forEach((val) => { + this.notificationService.error(val); + }); + } + }); + } + +} diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html index 37ea2eea5..179709e5d 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html @@ -2,8 +2,12 @@ - + + +
+ +
+ + + + diff --git a/src/Ombi/ClientApp/app/issues/issuestable.component.ts b/src/Ombi/ClientApp/app/issues/issuestable.component.ts index ee93e689d..aadfd546a 100644 --- a/src/Ombi/ClientApp/app/issues/issuestable.component.ts +++ b/src/Ombi/ClientApp/app/issues/issuestable.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { IIssues, IPagenator, IssueStatus } from "./../interfaces"; +import { IIssues, IPagenator, IssueStatus } from "../interfaces"; @Component({ selector: "issues-table", @@ -11,7 +11,7 @@ export class IssuesTableComponent { @Input() public issues: IIssues[]; @Input() public totalRecords: number; - @Output() public changePage = new EventEmitter(); + @Output() public changePage = new EventEmitter(); public IssueStatus = IssueStatus; @@ -20,11 +20,25 @@ export class IssuesTableComponent { public rowCount = 10; - public setOrder(value: string) { + public setOrder(value: string, el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + if (el.nodeName === "A") { + el = el.parentElement; + } + + const parent = el.parentElement; + const previousFilter = parent.querySelector(".active"); + if (this.order === value) { - this.reverse = !this.reverse; + this.reverse = !this.reverse; + } else { + if (previousFilter) { + previousFilter.className = ""; + } + el.className = "active"; } - + this.order = value; } @@ -33,7 +47,7 @@ export class IssuesTableComponent { //event.rows = Number of rows to display in new page //event.page = Index of the new page //event.pageCount = Total number of pages - + this.changePage.emit(event); } diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index 5fc0150ff..bb6afc1dd 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -2,34 +2,58 @@ you can substitue the span of reauth email for a input with the email and include the remember me checkbox --> -
+
+ -
-
+
+ +
+
+ +

- + + + + + + +
+ +
+ + +
+ +
+ +
+
diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 661850e86..0c04edbad 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -6,7 +6,7 @@ import { TranslateService } from "@ngx-translate/core"; import { PlatformLocation } from "@angular/common"; import { AuthService } from "../auth/auth.service"; import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces"; -import { NotificationService } from "../services"; +import { NotificationService, PlexTvService } from "../services"; import { SettingsService } from "../services"; import { StatusService } from "../services"; @@ -25,17 +25,30 @@ export class LoginComponent implements OnDestroy, OnInit { public form: FormGroup; public customizationSettings: ICustomizationSettings; public authenticationSettings: IAuthenticationSettings; + public plexEnabled: boolean; public background: any; public landingFlag: boolean; public baseUrl: string; + public loginWithOmbi: boolean; + public pinTimer: any; + + public get appName(): string { + if (this.customizationSettings.applicationName) { + return this.customizationSettings.applicationName; + } else { + return "Ombi"; + } + } + private timer: any; - + private clientId: string; + private errorBody: string; private errorValidation: string; constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService, private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer, - private route: ActivatedRoute, private location: PlatformLocation, private readonly translate: TranslateService) { + private route: ActivatedRoute, private location: PlatformLocation, private translate: TranslateService, private plexTv: PlexTvService) { this.route.params .subscribe((params: any) => { this.landingFlag = params.landing; @@ -60,20 +73,21 @@ export class LoginComponent implements OnDestroy, OnInit { } }); - if(authService.loggedIn()) { + if (authService.loggedIn()) { this.router.navigate(["search"]); } } public ngOnInit() { this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x); + this.settingsService.getClientId().subscribe(x => this.clientId = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); this.images.getRandomBackground().subscribe(x => { this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); }); this.timer = setInterval(() => { this.cycleBackground(); - }, 10000); + }, 7000); const base = this.location.getBaseHrefFromDOM(); if (base.length > 1) { @@ -90,9 +104,9 @@ export class LoginComponent implements OnDestroy, OnInit { return; } const value = form.value; - const user = { password: value.password, username: value.username, rememberMe:value.rememberMe }; + const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: "" } }; this.authService.requiresPassword(user).subscribe(x => { - if(x && this.authenticationSettings.allowNoPassword) { + if (x && this.authenticationSettings.allowNoPassword) { // Looks like this user requires a password this.authenticationSettings.allowNoPassword = false; return; @@ -102,6 +116,7 @@ export class LoginComponent implements OnDestroy, OnInit { localStorage.setItem("id_token", x.access_token); if (this.authService.loggedIn()) { + this.ngOnDestroy(); this.router.navigate(["search"]); } else { this.notify.error(this.errorBody); @@ -111,18 +126,59 @@ export class LoginComponent implements OnDestroy, OnInit { }); } + public oauth() { + const oAuthWindow = window.open(window.location.toString(), "_blank", `toolbar=0, + location=0, + status=0, + menubar=0, + scrollbars=1, + resizable=1, + width=500, + height=500`); + this.plexTv.GetPin(this.clientId, this.appName).subscribe((pin: any) => { + + this.authService.login({ usePlexOAuth: true, password: "", rememberMe: true, username: "", plexTvPin: pin }).subscribe(x => { + oAuthWindow!.location.replace(x.url); + + this.pinTimer = setInterval(() => { + this.notify.info("Authenticating", "Loading... Please Wait"); + this.getPinResult(x.pinId); + }, 10000); + }); + }); + } + + public getPinResult(pinId: number) { + this.authService.oAuth(pinId).subscribe(x => { + if(x.access_token) { + localStorage.setItem("id_token", x.access_token); + + if (this.authService.loggedIn()) { + this.ngOnDestroy(); + this.router.navigate(["search"]); + return; + } + } + + }, err => { + this.notify.error(err.statusText); + + this.router.navigate(["login"]); + }); + } + public ngOnDestroy() { clearInterval(this.timer); + clearInterval(this.pinTimer); } private cycleBackground() { - this.images.getRandomBackground().subscribe(x => { - this.background = ""; - }); - this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer - .bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); - }); + this.images.getRandomBackground().subscribe(x => { + this.background = ""; + }); + this.images.getRandomBackground().subscribe(x => { + this.background = this.sanitizer + .bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); + }); } - } diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.html b/src/Ombi/ClientApp/app/login/loginoauth.component.html new file mode 100644 index 000000000..e2118c7aa --- /dev/null +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.html @@ -0,0 +1,16 @@ + +
+ +
+
+ + +
+ + + + Back +
+
+
+
diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.ts b/src/Ombi/ClientApp/app/login/loginoauth.component.ts new file mode 100644 index 000000000..18f90be5f --- /dev/null +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { AuthService } from "../auth/auth.service"; +import { NotificationService } from "../services"; + +@Component({ + templateUrl: "./loginoauth.component.html", +}) +export class LoginOAuthComponent implements OnInit { + public pin: number; + public error: string; + + constructor(private authService: AuthService, private router: Router, + private route: ActivatedRoute, private notify: NotificationService) { + this.route.params + .subscribe((params: any) => { + this.pin = params.pin; + }); + } + + public ngOnInit(): void { + this.auth(); + } + + public auth() { + this.authService.oAuth(this.pin).subscribe(x => { + if (x.access_token) { + localStorage.setItem("id_token", x.access_token); + + if (this.authService.loggedIn()) { + this.router.navigate(["search"]); + return; + } + } + if (x.errorMessage) { + this.error = x.errorMessage; + } + + }, err => { + this.notify.error(err.statusText); + this.router.navigate(["login"]); + }); + } +} diff --git a/src/Ombi/ClientApp/app/login/resetpassword.component.ts b/src/Ombi/ClientApp/app/login/resetpassword.component.ts index 3a2f7d94a..2203b95b9 100644 --- a/src/Ombi/ClientApp/app/login/resetpassword.component.ts +++ b/src/Ombi/ClientApp/app/login/resetpassword.component.ts @@ -4,7 +4,7 @@ import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { DomSanitizer } from "@angular/platform-browser"; import { ICustomizationSettings } from "../interfaces"; -import { IdentityService, ImageService,NotificationService, SettingsService } from "../services"; +import { IdentityService, ImageService, NotificationService, SettingsService } from "../services"; @Component({ templateUrl: "./resetpassword.component.html", diff --git a/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts b/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts index 53d652070..f426a39d1 100644 --- a/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts +++ b/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from "@angular/core"; -import { NguCarousel } from "@ngu/carousel"; +import { NguCarouselConfig } from "@ngu/carousel"; +import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "../interfaces"; import { ImageService, RecentlyAddedService } from "../services"; -import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces"; @Component({ templateUrl: "recentlyAdded.component.html", @@ -41,13 +41,13 @@ export class RecentlyAddedComponent implements OnInit { public range: Date[]; public groupTv: boolean = false; - + // https://github.com/sheikalthaf/ngu-carousel - public carouselTile: NguCarousel; - + public carouselTile: NguCarouselConfig; + constructor(private recentlyAddedService: RecentlyAddedService, private imageService: ImageService) {} - + public ngOnInit() { this.getMovies(); this.getShows(); @@ -67,10 +67,10 @@ export class RecentlyAddedComponent implements OnInit { } public close() { - if(this.range.length < 2) { + if (this.range.length < 2) { return; } - if(!this.range[1]) { + if (!this.range[1]) { // If we do not have a second date then just set it to now this.range[1] = new Date(); } @@ -82,23 +82,27 @@ export class RecentlyAddedComponent implements OnInit { } private getShows() { - if(this.groupTv) { + if (this.groupTv) { this.recentlyAddedService.getRecentlyAddedTvGrouped().subscribe(x => { this.tv = x; - + this.tv.forEach((t) => { this.imageService.getTvPoster(t.tvDbId).subscribe(p => { - t.posterPath = p; + if (p) { + t.posterPath = p; + } }); }); }); } else { this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => { this.tv = x; - + this.tv.forEach((t) => { this.imageService.getTvPoster(t.tvDbId).subscribe(p => { - t.posterPath = p; + if (p) { + t.posterPath = p; + } }); }); }); @@ -110,11 +114,11 @@ export class RecentlyAddedComponent implements OnInit { this.movies = x; this.movies.forEach((movie) => { - if(movie.theMovieDbId) { + if (movie.theMovieDbId) { this.imageService.getMoviePoster(movie.theMovieDbId).subscribe(p => { movie.posterPath = p; }); - } else if(movie.imdbId) { + } else if (movie.imdbId) { this.imageService.getMoviePoster(movie.imdbId).subscribe(p => { movie.posterPath = p; }); diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 6b2300f38..a35ede06c 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -1,73 +1,57 @@ - -
+
+ {{ 'Requests.SortRequestDateDesc' | translate }} + + {{ 'Requests.SortTitleAsc' | translate}} + + + {{ 'Requests.SortTitleDesc' | translate}} + + + {{ 'Requests.SortStatusAsc' | translate}} + + + {{ 'Requests.SortStatusDesc' | translate}} + + -
+ + + +
-
+ +
+
+
+
- poster + poster
@@ -99,7 +83,7 @@ + [translate]="'Common.PendingApproval'">
@@ -115,16 +99,27 @@
-
{{ 'Requests.QualityOverride' | translate }} +
{{ 'Requests.QualityOverride' | translate }} {{request.qualityOverrideTitle}}
-
{{ 'Requests.RootFolderOverride' | translate }} +
{{ 'Requests.RootFolderOverride' | translate }} {{request.rootPathOverrideTitle}}
+
+
+ + + + + + + +
+
@@ -178,10 +173,12 @@
- -
@@ -189,10 +186,9 @@
- + +
@@ -222,42 +220,43 @@

{{ 'Requests.Filter' | translate }}


- -

{{ 'Filter.FilterHeaderAvailability' | translate }}

-
-
- - +
+

{{ 'Filter.FilterHeaderAvailability' | translate }}

+
+
+ + +
-
-
-
- - +
+
+ + +
- -

{{ 'Filter.FilterHeaderRequestStatus' | translate }}

-
-
- - +
+

{{ 'Filter.FilterHeaderRequestStatus' | translate }}

+
+
+ + +
-
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
- - \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index 0fc5f6651..311adeb92 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -1,21 +1,20 @@ -import { Component, Input, OnInit } from "@angular/core"; +import { PlatformLocation } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; import { DomSanitizer } from "@angular/platform-browser"; -import "rxjs/add/operator/debounceTime"; -import "rxjs/add/operator/distinctUntilChanged"; -import "rxjs/add/operator/map"; -import { Subject } from "rxjs/Subject"; +import { Subject } from "rxjs"; +import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { AuthService } from "../auth/auth.service"; +import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces"; import { NotificationService, RadarrService, RequestService } from "../services"; -import { FilterType, IFilter, IIssueCategory, IMovieRequests, IRadarrProfile, IRadarrRootFolder } from "../interfaces"; - @Component({ selector: "movie-requests", templateUrl: "./movierequests.component.html", }) export class MovieRequestsComponent implements OnInit { public movieRequests: IMovieRequests[]; + public defaultPoster: string; public searchChanged: Subject = new Subject(); public searchText: string; @@ -36,46 +35,57 @@ export class MovieRequestsComponent implements OnInit { public filter: IFilter; public filterType = FilterType; - public order: string = "requestedDate"; - public reverse = false; + public orderType: OrderType = OrderType.RequestedDateDesc; + public OrderType = OrderType; + public totalMovies: number = 100; private currentlyLoaded: number; private amountToLoad: number; - constructor(private requestService: RequestService, - private auth: AuthService, - private notificationService: NotificationService, - private radarrService: RadarrService, - private sanitizer: DomSanitizer) { - this.searchChanged - .debounceTime(600) // Wait Xms after the last event before emitting last event - .distinctUntilChanged() // only emit if value is different from previous value - .subscribe(x => { - this.searchText = x as string; - if (this.searchText === "") { - this.resetSearch(); - return; - } - this.requestService.searchMovieRequests(this.searchText) - .subscribe(m => { - this.setOverrides(m); - this.movieRequests = m; - }); - }); + constructor( + private requestService: RequestService, + private auth: AuthService, + private notificationService: NotificationService, + private radarrService: RadarrService, + private sanitizer: DomSanitizer, + private readonly platformLocation: PlatformLocation) { + this.searchChanged.pipe( + debounceTime(600), // Wait Xms after the last event before emitting last event + distinctUntilChanged(), // only emit if value is different from previous value + ).subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.resetSearch(); + return; + } + this.requestService.searchMovieRequests(this.searchText) + .subscribe(m => { + this.setOverrides(m); + this.movieRequests = m; + }); + }); + this.defaultPoster = "../../../images/default_movie_poster.png"; + const base = this.platformLocation.getBaseHrefFromDOM(); + if (base) { + this.defaultPoster = "../../.." + base + "/images/default_movie_poster.png"; + } } public ngOnInit() { - this.amountToLoad = 100; - this.currentlyLoaded = 100; - this.loadInit(); - this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.amountToLoad = 10; + this.currentlyLoaded = 10; this.filter = { availabilityFilter: FilterType.None, - statusFilter: FilterType.None}; + statusFilter: FilterType.None, + }; + this.loadInit(); + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + } - public loadMore() { - this.loadRequests(this.amountToLoad, this.currentlyLoaded); + public paginate(event: IPagenator) { + const skipAmount = event.first; + this.loadRequests(this.amountToLoad, skipAmount); } public search(text: any) { @@ -85,13 +95,13 @@ export class MovieRequestsComponent implements OnInit { public removeRequest(request: IMovieRequests) { this.requestService.removeMovieRequest(request); this.removeRequestFromUi(request); - this.loadRequests(1, this.currentlyLoaded); + this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); } public changeAvailability(request: IMovieRequests, available: boolean) { request.available = available; - if(available) { + if (available) { this.requestService.markMovieAvailable({ id: request.id }).subscribe(x => { if (x.result) { this.notificationService.success( @@ -149,55 +159,92 @@ export class MovieRequestsComponent implements OnInit { event.preventDefault(); } - public clearFilter() { + public clearFilter(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; + el = el.querySelectorAll("INPUT"); + for (el of el) { + el.checked = false; + el.parentElement.classList.remove("active"); + } + this.filterDisplay = false; this.filter.availabilityFilter = FilterType.None; this.filter.statusFilter = FilterType.None; - + this.resetSearch(); } - public filterAvailability(filter: FilterType) { + public filterAvailability(filter: FilterType, el: any) { + this.filterActiveStyle(el); this.filter.availabilityFilter = filter; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.setOverrides(x); - this.movieRequests = x; - }); + this.loadInit(); } - public filterStatus(filter: FilterType) { + public filterStatus(filter: FilterType, el: any) { + this.filterActiveStyle(el); this.filter.statusFilter = filter; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.setOverrides(x); - this.movieRequests = x; - }); + this.loadInit(); } - public setOrder(value: string, el: any) { + public setOrder(value: OrderType, el: any) { el = el.toElement || el.relatedTarget || el.target || el.srcElement; const parent = el.parentElement; const previousFilter = parent.querySelector(".active"); - if (this.order === value) { - this.reverse = !this.reverse; - } else { - previousFilter.className = ""; - el.className = "active"; + previousFilter.className = ""; + el.className = "active"; + + this.orderType = value; + + this.loadInit(); + } + + public subscribe(request: IMovieRequests) { + request.subscribed = true; + this.requestService.subscribeToMovie(request.id) + .subscribe(x => { + this.notificationService.success("Subscribed To Movie!"); + }); + } + + public unSubscribe(request: IMovieRequests) { + request.subscribed = false; + this.requestService.unSubscribeToMovie(request.id) + .subscribe(x => { + this.notificationService.success("Unsubscribed Movie!"); + }); + } + + private filterActiveStyle(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; //gets radio div + el = el.parentElement; //gets form group div + el = el.parentElement; //gets status filter div + el = el.querySelectorAll("INPUT"); + for (el of el) { + if (el.checked) { + if (!el.parentElement.classList.contains("active")) { + el.parentElement.className += " active"; + } + } else { + el.parentElement.classList.remove("active"); + } } - this.order = value; - } + } private loadRequests(amountToLoad: number, currentlyLoaded: number) { - this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1) + this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) .subscribe(x => { - this.setOverrides(x); - if(!this.movieRequests) { + this.setOverrides(x.collection); + if (!this.movieRequests) { this.movieRequests = []; } - this.movieRequests.push.apply(this.movieRequests, x); + this.movieRequests = x.collection; + this.totalMovies = x.total; this.currentlyLoaded = currentlyLoaded + amountToLoad; }); } @@ -238,21 +285,25 @@ export class MovieRequestsComponent implements OnInit { } private loadInit() { - this.requestService.getMovieRequests(this.amountToLoad, 0) + this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter) .subscribe(x => { - this.movieRequests = x; + this.movieRequests = x.collection; + this.totalMovies = x.total; this.movieRequests.forEach((req) => { - this.movieRequests.forEach((req) => this.setBackground(req)); - }); - this.radarrService.getQualityProfilesFromSettings().subscribe(c => { - this.radarrProfiles = c; - this.movieRequests.forEach((req) => this.setQualityOverrides(req)); - }); - this.radarrService.getRootFoldersFromSettings().subscribe(c => { - this.radarrRootFolders = c; - this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + this.setBackground(req); + this.setPoster(req); }); + if (this.isAdmin) { + this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + this.radarrProfiles = c; + this.movieRequests.forEach((req) => this.setQualityOverrides(req)); + }); + this.radarrService.getRootFoldersFromSettings().subscribe(c => { + this.radarrRootFolders = c; + this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + }); + } }); } @@ -296,13 +347,22 @@ export class MovieRequestsComponent implements OnInit { } private setOverride(req: IMovieRequests): void { + this.setPoster(req); this.setBackground(req); this.setQualityOverrides(req); this.setRootFolderOverrides(req); } + private setPoster(req: IMovieRequests): void { + if (req.posterPath === null) { + req.posterPath = this.defaultPoster; + } else { + req.posterPath = "https://image.tmdb.org/t/p/w300/" + req.posterPath; + } + } + private setBackground(req: IMovieRequests): void { req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle - ("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")"); + ("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")"); } } diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html new file mode 100644 index 000000000..f6ad4bb2a --- /dev/null +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html @@ -0,0 +1,265 @@ + +
+ + +
+
+
+
+
+ +
+ poster +
+ + +
+ + +
+
+ {{ 'Requests.RequestedBy' | translate }} + {{request.requestedUser.userName}} + {{request.requestedUser.alias}} + {{request.requestedUser.userName}} +
+ +
+ {{ 'Requests.RequestStatus' | translate }} + + + + + + + + +
+
+ {{ 'Requests.Denied' | translate }} + + +
+ + +
{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | date: 'mediumDate'} }}
+
{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | date: 'mediumDate'}}
+
+
+ + +
+
+ +
+
+
+ +
+ + + + + + + +
+ +
+
+
+ +
+ +
+ + +
+ + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + + + +

{{ 'Requests.Filter' | translate }}

+
+
+

{{ 'Filter.FilterHeaderAvailability' | translate }}

+
+
+ + +
+
+
+
+ + +
+
+
+
+

{{ 'Filter.FilterHeaderRequestStatus' | translate }}

+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts new file mode 100644 index 000000000..8f8096acd --- /dev/null +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts @@ -0,0 +1,351 @@ +import { PlatformLocation } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; +import { DomSanitizer } from "@angular/platform-browser"; +import { Subject } from "rxjs"; +import { debounceTime, distinctUntilChanged } from "rxjs/operators"; + +import { AuthService } from "../../auth/auth.service"; +import { FilterType, IAlbumRequest, IFilter, IIssueCategory, IPagenator, OrderType } from "../../interfaces"; +import { NotificationService, RequestService } from "../../services"; + +@Component({ + selector: "music-requests", + templateUrl: "./musicrequests.component.html", +}) +export class MusicRequestsComponent implements OnInit { + public albumRequests: IAlbumRequest[]; + public defaultPoster: string; + + public searchChanged: Subject = new Subject(); + public searchText: string; + + public isAdmin: boolean; // Also PowerUser + + @Input() public issueCategories: IIssueCategory[]; + @Input() public issuesEnabled: boolean; + public issuesBarVisible = false; + public issueRequest: IAlbumRequest; + public issueProviderId: string; + public issueCategorySelected: IIssueCategory; + + public filterDisplay: boolean; + public filter: IFilter; + public filterType = FilterType; + + public orderType: OrderType = OrderType.RequestedDateDesc; + public OrderType = OrderType; + + public totalAlbums: number = 100; + private currentlyLoaded: number; + private amountToLoad: number; + + constructor( + private requestService: RequestService, + private auth: AuthService, + private notificationService: NotificationService, + private sanitizer: DomSanitizer, + private readonly platformLocation: PlatformLocation) { + this.searchChanged.pipe( + debounceTime(600), // Wait Xms after the last event before emitting last event + distinctUntilChanged(), // only emit if value is different from previous value + ).subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.resetSearch(); + return; + } + this.requestService.searchAlbumRequests(this.searchText) + .subscribe(m => { + this.setOverrides(m); + this.albumRequests = m; + }); + }); + this.defaultPoster = "../../../images/default-music-placeholder.png"; + const base = this.platformLocation.getBaseHrefFromDOM(); + if (base) { + this.defaultPoster = "../../.." + base + "/images/default-music-placeholder.png"; + } + } + + public ngOnInit() { + this.amountToLoad = 10; + this.currentlyLoaded = 10; + this.filter = { + availabilityFilter: FilterType.None, + statusFilter: FilterType.None, + }; + this.loadInit(); + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + } + + public paginate(event: IPagenator) { + const skipAmount = event.first; + this.loadRequests(this.amountToLoad, skipAmount); + } + + public search(text: any) { + this.searchChanged.next(text.target.value); + } + + public removeRequest(request: IAlbumRequest) { + this.requestService.removeAlbumRequest(request).subscribe(x => { + this.removeRequestFromUi(request); + this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); + }); + } + + public changeAvailability(request: IAlbumRequest, available: boolean) { + request.available = available; + + if (available) { + this.requestService.markAlbumAvailable({ id: request.id }).subscribe(x => { + if (x.result) { + this.notificationService.success( + `${request.title} Is now available`); + } else { + this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); + request.approved = false; + } + }); + } else { + this.requestService.markAlbumUnavailable({ id: request.id }).subscribe(x => { + if (x.result) { + this.notificationService.success( + `${request.title} Is now unavailable`); + } else { + this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); + request.approved = false; + } + }); + } + } + + public approve(request: IAlbumRequest) { + request.approved = true; + this.approveRequest(request); + } + + public deny(request: IAlbumRequest) { + request.denied = true; + this.denyRequest(request); + } + + // public selectRootFolder(searchResult: IAlbumRequest, rootFolderSelected: IRadarrRootFolder, event: any) { + // event.preventDefault(); + // // searchResult.rootPathOverride = rootFolderSelected.id; + // this.setOverride(searchResult); + // this.updateRequest(searchResult); + // } + + // public selectQualityProfile(searchResult: IMovieRequests, profileSelected: IRadarrProfile, event: any) { + // event.preventDefault(); + // searchResult.qualityOverride = profileSelected.id; + // this.setOverride(searchResult); + // this.updateRequest(searchResult); + // } + + public reportIssue(catId: IIssueCategory, req: IAlbumRequest) { + this.issueRequest = req; + this.issueCategorySelected = catId; + this.issuesBarVisible = true; + this.issueProviderId = req.foreignAlbumId; + } + + public ignore(event: any): void { + event.preventDefault(); + } + + public clearFilter(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; + el = el.querySelectorAll("INPUT"); + for (el of el) { + el.checked = false; + el.parentElement.classList.remove("active"); + } + + this.filterDisplay = false; + this.filter.availabilityFilter = FilterType.None; + this.filter.statusFilter = FilterType.None; + + this.resetSearch(); + } + + public filterAvailability(filter: FilterType, el: any) { + this.filterActiveStyle(el); + this.filter.availabilityFilter = filter; + this.loadInit(); + } + + public filterStatus(filter: FilterType, el: any) { + this.filterActiveStyle(el); + this.filter.statusFilter = filter; + this.loadInit(); + } + + public setOrder(value: OrderType, el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + const parent = el.parentElement; + const previousFilter = parent.querySelector(".active"); + + previousFilter.className = ""; + el.className = "active"; + + this.orderType = value; + + this.loadInit(); + } + + // public subscribe(request: IAlbumRequest) { + // request.subscribed = true; + // this.requestService.subscribeToMovie(request.id) + // .subscribe(x => { + // this.notificationService.success("Subscribed To Movie!"); + // }); + // } + + // public unSubscribe(request: IMovieRequests) { + // request.subscribed = false; + // this.requestService.unSubscribeToMovie(request.id) + // .subscribe(x => { + // this.notificationService.success("Unsubscribed Movie!"); + // }); + // } + + private filterActiveStyle(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; //gets radio div + el = el.parentElement; //gets form group div + el = el.parentElement; //gets status filter div + el = el.querySelectorAll("INPUT"); + for (el of el) { + if (el.checked) { + if (!el.parentElement.classList.contains("active")) { + el.parentElement.className += " active"; + } + } else { + el.parentElement.classList.remove("active"); + } + } + } + + private loadRequests(amountToLoad: number, currentlyLoaded: number) { + this.requestService.getAlbumRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) + .subscribe(x => { + this.setOverrides(x.collection); + if (!this.albumRequests) { + this.albumRequests = []; + } + this.albumRequests = x.collection; + this.totalAlbums = x.total; + this.currentlyLoaded = currentlyLoaded + amountToLoad; + }); + } + + private approveRequest(request: IAlbumRequest) { + this.requestService.approveAlbum({ id: request.id }) + .subscribe(x => { + request.approved = true; + if (x.result) { + this.notificationService.success( + `Request for ${request.title} has been approved successfully`); + } else { + this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage); + request.approved = false; + } + }); + } + + private denyRequest(request: IAlbumRequest) { + this.requestService.denyAlbum({ id: request.id }) + .subscribe(x => { + if (x.result) { + this.notificationService.success( + `Request for ${request.title} has been denied successfully`); + } else { + this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage); + request.denied = false; + } + }); + } + + private loadInit() { + this.requestService.getAlbumRequests(this.amountToLoad, 0, this.orderType, this.filter) + .subscribe(x => { + this.albumRequests = x.collection; + this.totalAlbums = x.total; + + this.setOverrides(this.albumRequests); + + if (this.isAdmin) { + // this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + // this.radarrProfiles = c; + // this.albumRequests.forEach((req) => this.setQualityOverrides(req)); + // }); + // this.radarrService.getRootFoldersFromSettings().subscribe(c => { + // this.radarrRootFolders = c; + // this.albumRequests.forEach((req) => this.setRootFolderOverrides(req)); + // }); + } + }); + } + + private resetSearch() { + this.currentlyLoaded = 5; + this.loadInit(); + } + + private removeRequestFromUi(key: IAlbumRequest) { + const index = this.albumRequests.indexOf(key, 0); + if (index > -1) { + this.albumRequests.splice(index, 1); + } + } + + private setOverrides(requests: IAlbumRequest[]): void { + requests.forEach((req) => { + this.setOverride(req); + }); + } + + // private setQualityOverrides(req: IMovieRequests): void { + // if (this.radarrProfiles) { + // const profile = this.radarrProfiles.filter((p) => { + // return p.id === req.qualityOverride; + // }); + // if (profile.length > 0) { + // req.qualityOverrideTitle = profile[0].name; + // } + // } + // } + // private setRootFolderOverrides(req: IMovieRequests): void { + // if (this.radarrRootFolders) { + // const path = this.radarrRootFolders.filter((folder) => { + // return folder.id === req.rootPathOverride; + // }); + // if (path.length > 0) { + // req.rootPathOverrideTitle = path[0].path; + // } + // } + // } + + private setOverride(req: IAlbumRequest): void { + this.setAlbumBackground(req); + // this.setQualityOverrides(req); + // this.setRootFolderOverrides(req); + } + private setAlbumBackground(req: IAlbumRequest) { + if (req.disk === null) { + if(req.cover === null) { + req.disk = this.defaultPoster; + } else { + req.disk = req.cover; + } + } + req.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + req.cover + ")"); + } +} diff --git a/src/Ombi/ClientApp/app/requests/remainingrequests.component.html b/src/Ombi/ClientApp/app/requests/remainingrequests.component.html new file mode 100644 index 000000000..11cd28b34 --- /dev/null +++ b/src/Ombi/ClientApp/app/requests/remainingrequests.component.html @@ -0,0 +1,18 @@ +
+

+ {{'Requests.Remaining.Quota' | translate: {remaining: remaining.remaining, total: remaining.limit} }} +

+

+ {{'Requests.Remaining.NextDays' | translate: {time: daysUntil} }} +

+

+ {{'Requests.Remaining.NextHours' | translate: {time: hoursUntil} }} +

+

+ {{(minutesUntil == 1 ? 'Requests.Remaining.NextMinute' : 'Requests.Remaining.NextMinutes') | translate: {time: minutesUntil} }} +

+
+ +
+
+ diff --git a/src/Ombi/ClientApp/app/requests/remainingrequests.component.ts b/src/Ombi/ClientApp/app/requests/remainingrequests.component.ts new file mode 100644 index 000000000..c88e63009 --- /dev/null +++ b/src/Ombi/ClientApp/app/requests/remainingrequests.component.ts @@ -0,0 +1,73 @@ +import { IRemainingRequests } from "../interfaces/IRemainingRequests"; +import { RequestService } from "../services"; + +import { Component, Input, OnInit } from "@angular/core"; +import { Observable } from "rxjs"; + +@Component({ + selector: "remaining-requests", + templateUrl: "./remainingrequests.component.html", +}) + +export class RemainingRequestsComponent implements OnInit { + public remaining: IRemainingRequests; + @Input() public movie: boolean; + @Input() public tv: boolean; + @Input() public music: boolean; + public daysUntil: number; + public hoursUntil: number; + public minutesUntil: number; + @Input() public quotaRefreshEvents: Observable; + + constructor(private requestService: RequestService) { + } + + public ngOnInit() { + const self = this; + + this.update(); + + this.quotaRefreshEvents.subscribe(() => { + this.update(); + }); + + setInterval(() => { + self.update(); + }, 60000); + } + + public update(): void { + const callback = (remaining => { + this.remaining = remaining; + this.calculateTime(); + }); + + if (this.movie) { + this.requestService.getRemainingMovieRequests().subscribe(callback); + } + if(this.tv) { + this.requestService.getRemainingTvRequests().subscribe(callback); + } + if(this.music) { + this.requestService.getRemainingMusicRequests().subscribe(callback); + } + } + + private calculateTime(): void { + this.daysUntil = Math.ceil(this.daysUntilNextRequest()); + this.hoursUntil = Math.ceil(this.hoursUntilNextRequest()); + this.minutesUntil = Math.ceil(this.minutesUntilNextRequest()); + } + + private daysUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24; + } + + private hoursUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60; + } + + private minutesUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60; + } +} diff --git a/src/Ombi/ClientApp/app/requests/remainingrequests.module.ts b/src/Ombi/ClientApp/app/requests/remainingrequests.module.ts new file mode 100644 index 000000000..411a94dfd --- /dev/null +++ b/src/Ombi/ClientApp/app/requests/remainingrequests.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from "@angular/core"; +import { FormsModule } from "@angular/forms"; +import { RouterModule } from "@angular/router"; + +import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; + +import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng"; +import { RequestService } from "../services"; + +@NgModule({ + imports: [ + FormsModule, + NgbModule.forRoot(), + TreeTableModule, + SidebarModule, + TooltipModule, + ], + declarations: [ + ], + exports: [ + RouterModule, + ], + providers: [ + RequestService, + ], +}) +export class SearchModule { } diff --git a/src/Ombi/ClientApp/app/requests/request.component.html b/src/Ombi/ClientApp/app/requests/request.component.html index 916d3b774..d2df3c97a 100644 --- a/src/Ombi/ClientApp/app/requests/request.component.html +++ b/src/Ombi/ClientApp/app/requests/request.component.html @@ -3,10 +3,14 @@ @@ -19,5 +23,8 @@
+
+ +
diff --git a/src/Ombi/ClientApp/app/requests/request.component.ts b/src/Ombi/ClientApp/app/requests/request.component.ts index 08df3f2be..d0fa9d0b1 100644 --- a/src/Ombi/ClientApp/app/requests/request.component.ts +++ b/src/Ombi/ClientApp/app/requests/request.component.ts @@ -1,8 +1,8 @@  import { Component, OnInit } from "@angular/core"; -import { IIssueCategory } from "./../interfaces"; -import { IssuesService, SettingsService } from "./../services"; +import { IIssueCategory } from "../interfaces"; +import { IssuesService, SettingsService } from "../services"; @Component({ templateUrl: "./request.component.html", @@ -11,6 +11,7 @@ export class RequestComponent implements OnInit { public showMovie = true; public showTv = false; + public showAlbums = false; public issueCategories: IIssueCategory[]; public issuesEnabled = false; @@ -28,10 +29,18 @@ export class RequestComponent implements OnInit { public selectMovieTab() { this.showMovie = true; this.showTv = false; + this.showAlbums = false; } public selectTvTab() { this.showMovie = false; this.showTv = true; + this.showAlbums = false; + } + + public selectMusicTab() { + this.showMovie = false; + this.showTv = false; + this.showAlbums = true; } } diff --git a/src/Ombi/ClientApp/app/requests/requests.module.ts b/src/Ombi/ClientApp/app/requests/requests.module.ts index 7bddee71c..63d7117f5 100644 --- a/src/Ombi/ClientApp/app/requests/requests.module.ts +++ b/src/Ombi/ClientApp/app/requests/requests.module.ts @@ -6,14 +6,15 @@ import { OrderModule } from "ngx-order-pipe"; import { InfiniteScrollModule } from "ngx-infinite-scroll"; -import { ButtonModule, DialogModule } from "primeng/primeng"; +import { ButtonModule, DialogModule, PaginatorModule } from "primeng/primeng"; import { MovieRequestsComponent } from "./movierequests.component"; +import { MusicRequestsComponent } from "./music/musicrequests.component"; // Request import { RequestComponent } from "./request.component"; import { TvRequestChildrenComponent } from "./tvrequest-children.component"; import { TvRequestsComponent } from "./tvrequests.component"; -import { SidebarModule, TreeTableModule } from "primeng/primeng"; +import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng"; import { IdentityService, RadarrService, RequestService, SonarrService } from "../services"; @@ -23,7 +24,6 @@ import { SharedModule } from "../shared/shared.module"; const routes: Routes = [ { path: "", component: RequestComponent, canActivate: [AuthGuard] }, - { path: ":id", component: TvRequestChildrenComponent, canActivate: [AuthGuard] }, ]; @NgModule({ imports: [ @@ -36,12 +36,15 @@ const routes: Routes = [ SharedModule, SidebarModule, OrderModule, + PaginatorModule, + TooltipModule, ], declarations: [ RequestComponent, MovieRequestsComponent, TvRequestsComponent, TvRequestChildrenComponent, + MusicRequestsComponent, ], exports: [ RouterModule, diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html index 30e1398eb..f41b17f51 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html @@ -1,6 +1,6 @@ 

-
+
@@ -11,8 +11,11 @@ {{child.requestedUser.userName}}
-
+ + + +
@@ -22,15 +25,7 @@
- +
@@ -101,8 +96,3 @@
- - - - \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index 300599063..19e941e9d 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { IChildRequests, IIssueCategory } from "../interfaces"; +import { IChildRequests } from "../interfaces"; import { NotificationService, RequestService } from "../services"; @Component({ - selector:"tvrequests-children", + selector: "tvrequests-children", templateUrl: "./tvrequest-children.component.html", }) export class TvRequestChildrenComponent { @@ -13,13 +13,6 @@ export class TvRequestChildrenComponent { @Output() public requestDeleted = new EventEmitter(); - @Input() public issueCategories: IIssueCategory[]; - @Input() public issuesEnabled: boolean; - @Input() public issueProviderId: string; - public issuesBarVisible = false; - public issueRequest: IChildRequests; - public issueCategorySelected: IIssueCategory; - constructor(private requestService: RequestService, private notificationService: NotificationService) { } @@ -28,17 +21,17 @@ export class TvRequestChildrenComponent { .subscribe(x => { this.removeRequestFromUi(request); this.requestDeleted.emit(request.id); - }); + }); } public changeAvailability(request: IChildRequests, available: boolean) { request.available = available; - request.seasonRequests.forEach((season)=> { - season.episodes.forEach((ep)=> { + request.seasonRequests.forEach((season) => { + season.episodes.forEach((ep) => { ep.available = available; }); }); - if(available) { + if (available) { this.requestService.markTvAvailable({ id: request.id }).subscribe(x => { if (x.result) { this.notificationService.success( @@ -101,11 +94,20 @@ export class TvRequestChildrenComponent { }); } - public reportIssue(catId: IIssueCategory, req: IChildRequests) { - this.issueRequest = req; - this.issueCategorySelected = catId; - this.issuesBarVisible = true; - this.issueProviderId = req.id.toString(); + public subscribe(request: IChildRequests) { + request.subscribed = true; + this.requestService.subscribeToTv(request.id) + .subscribe(x => { + this.notificationService.success("Subscribed To TV Show!"); + }); + } + + public unSubscribe(request: IChildRequests) { + request.subscribed = false; + this.requestService.unSubscribeToTv(request.id) + .subscribe(x => { + this.notificationService.success("Unsubscribed TV Show!"); + }); } private removeRequestFromUi(key: IChildRequests) { diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index a602d27e1..d1f9b36da 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -4,111 +4,111 @@

- - -
- +
+
+ +
+
+
+
- - - Results - - - -
-
-
-
+
-
+ poster - poster +
+
+ +
+
+ Status: + {{node.status}}
-
- -
-
- Status: - {{node.data.status}} -
- -
Release Date: {{node.data.releaseDate | date}}
-
-
{{ 'Requests.QualityOverride' | translate }} - {{node.data.qualityOverrideTitle}} -
-
{{ 'Requests.RootFolderOverride' | translate }} - {{node.data.rootPathOverrideTitle}} -
+
Release Date: {{node.releaseDate | date}}
+
+
{{ 'Requests.QualityOverride' | translate }} + {{node.qualityOverrideTitle}} +
+
{{ 'Requests.RootFolderOverride' | translate }} + {{node.rootPathOverrideTitle}}
- -
-
- -
- -
+
+
+
+ + +
+ +
+ {{ 'Requests.ChangeRootFolder' | translate }} +
- +
+ {{ 'Requests.ChangeQualityProfile' | translate }} +
- -
+ +
+
- -
- -
- - - +
+ +
+ +
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts index ad8b4ca50..144de0206 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts @@ -1,20 +1,13 @@ -import { Component, Input, OnInit } from "@angular/core"; +import { PlatformLocation } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; import { DomSanitizer } from "@angular/platform-browser"; -import "rxjs/add/operator/debounceTime"; -import "rxjs/add/operator/distinctUntilChanged"; -import "rxjs/add/operator/map"; -import { Subject } from "rxjs/Subject"; -import { ImageService } from "./../services/image.service"; - -import "rxjs/add/operator/debounceTime"; -import "rxjs/add/operator/distinctUntilChanged"; -import "rxjs/add/operator/map"; +import { Subject } from "rxjs"; +import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { AuthService } from "../auth/auth.service"; +import { FilterType, IIssueCategory, IPagenator, IRequestsViewModel, ISonarrProfile, ISonarrRootFolder, ITvRequests, OrderType } from "../interfaces"; import { NotificationService, RequestService, SonarrService } from "../services"; - -import { TreeNode } from "primeng/primeng"; -import { IIssueCategory, ISonarrProfile, ISonarrRootFolder, ITvRequests } from "../interfaces"; +import { ImageService } from "../services/image.service"; @Component({ selector: "tv-requests", @@ -23,100 +16,86 @@ import { IIssueCategory, ISonarrProfile, ISonarrRootFolder, ITvRequests } from }) export class TvRequestsComponent implements OnInit { - public tvRequests: TreeNode[]; + public tvRequests: IRequestsViewModel; public searchChanged = new Subject(); public searchText: string; public isAdmin: boolean; public showChildDialogue = false; // This is for the child modal popup public selectedSeason: ITvRequests; + public defaultPoster: string; @Input() public issueCategories: IIssueCategory[]; @Input() public issuesEnabled: boolean; public issueProviderId: string; + public issuesBarVisible = false; + public issueRequest: ITvRequests; + public issueCategorySelected: IIssueCategory; public sonarrProfiles: ISonarrProfile[] = []; public sonarrRootFolders: ISonarrRootFolder[] = []; + public totalTv: number = 100; private currentlyLoaded: number; private amountToLoad: number; - constructor(private requestService: RequestService, - private auth: AuthService, - private sanitizer: DomSanitizer, - private imageService: ImageService, - private sonarrService: SonarrService, - private notificationService: NotificationService) { - this.searchChanged - .debounceTime(600) // Wait Xms after the last event before emitting last event - .distinctUntilChanged() // only emit if value is different from previous value - .subscribe(x => { - this.searchText = x as string; - if (this.searchText === "") { - this.resetSearch(); - return; - } - this.requestService.searchTvRequestsTree(this.searchText) - .subscribe(m => { - this.tvRequests = m; - this.tvRequests.forEach((val) => this.loadBackdrop(val)); - this.tvRequests.forEach((val) => this.setOverride(val.data)); - }); - }); + constructor( + private requestService: RequestService, + private auth: AuthService, + private sanitizer: DomSanitizer, + private imageService: ImageService, + private sonarrService: SonarrService, + private notificationService: NotificationService, + private readonly platformLocation: PlatformLocation) { + + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + if (this.isAdmin) { + this.sonarrService.getQualityProfilesWithoutSettings() + .subscribe(x => this.sonarrProfiles = x); + + this.sonarrService.getRootFoldersWithoutSettings() + .subscribe(x => this.sonarrRootFolders = x); + } } - public openClosestTab(el: any) { - const rowclass = "undefined ng-star-inserted"; - el = el.toElement || el.relatedTarget || el.target || el.srcElement; - - if (el.nodeName === "BUTTON") { - - const isButtonAlreadyActive = el.parentElement.querySelector(".active"); - // if a Button already has Class: .active - if (isButtonAlreadyActive) { - isButtonAlreadyActive.classList.remove("active"); - } else { - el.className += " active"; - } - } + public openClosestTab(node: ITvRequests,el: any) { + el.preventDefault(); + node.open = !node.open; + } - while (el.className !== rowclass) { - // Increment the loop to the parent node until we find the row we need - el = el.parentNode; - } - // At this point, the while loop has stopped and `el` represents the element that has - // the class you specified - - // Then we loop through the children to find the caret which we want to click - const caretright = "fa-caret-right"; - const caretdown = "fa-caret-down"; - for (const value of el.children) { - // the caret from the ui has 2 class selectors depending on if expanded or not - // we search for both since we want to still toggle the clicking - if (value.className.includes(caretright) || value.className.includes(caretdown)) { - // Then we tell JS to click the element even though we hid it from the UI - value.click(); - //Break from loop since we no longer need to continue looking - break; + public ngOnInit() { + this.amountToLoad = 10; + this.currentlyLoaded = 10; + this.tvRequests = {collection:[], total:0}; + + this.searchChanged.pipe( + debounceTime(600), // Wait Xms after the last event before emitting last event + distinctUntilChanged(), // only emit if value is different from previous value + ).subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.resetSearch(); + return; } + this.requestService.searchTvRequests(this.searchText) + .subscribe(m => { + this.tvRequests.collection = m; + this.tvRequests.collection.forEach((val) => this.loadBackdrop(val)); + this.tvRequests.collection.forEach((val) => this.setOverride(val)); + }); + }); + this.defaultPoster = "../../../images/default_tv_poster.png"; + const base = this.platformLocation.getBaseHrefFromDOM(); + if (base) { + this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png"; } - } - public ngOnInit() { - this.amountToLoad = 1000; - this.currentlyLoaded = 1000; - this.tvRequests = []; - this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); - this.loadInit(); } - public loadMore() { - //TODO: I believe this +1 is causing off by one error skipping loading of tv shows - //When removed and scrolling very slowly everything works as expected, however - //if you scroll really quickly then you start getting duplicates of movies - //since it's async and some subsequent results return first and then incrementer - //is increased so you see movies which had already been gotten show up... - this.requestService.getTvRequestsTree(this.amountToLoad, this.currentlyLoaded + 1) + public paginate(event: IPagenator) { + const skipAmount = event.first; + + this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None) .subscribe(x => { this.tvRequests = x; this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; @@ -141,14 +120,21 @@ export class TvRequestsComponent implements OnInit { event.preventDefault(); searchResult.rootFolder = rootFolderSelected.id; this.setOverride(searchResult); - this.updateRequest(searchResult); + this.setRootFolder(searchResult); } public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) { event.preventDefault(); searchResult.qualityOverride = profileSelected.id; this.setOverride(searchResult); - this.updateRequest(searchResult); + this.setQualityProfile(searchResult); + } + + public reportIssue(catId: IIssueCategory, req: ITvRequests) { + this.issueRequest = req; + this.issueCategorySelected = catId; + this.issuesBarVisible = true; + this.issueProviderId = req.id.toString(); } private setOverride(req: ITvRequests): void { @@ -156,13 +142,24 @@ export class TvRequestsComponent implements OnInit { this.setRootFolderOverrides(req); } - private updateRequest(request: ITvRequests) { - this.requestService.updateTvRequest(request) - .subscribe(x => { - this.notificationService.success("Request Updated"); - this.setOverride(x); - request = x; - }); + private setQualityProfile(req: ITvRequests) { + this.requestService.setQualityProfile(req.id, req.qualityOverride).subscribe(x => { + if(x) { + this.notificationService.success("Quality profile updated"); + } else { + this.notificationService.error("Could not update the quality profile"); + } + }); + } + + private setRootFolder(req: ITvRequests) { + this.requestService.setRootFolder(req.id, req.rootFolder).subscribe(x => { + if(x) { + this.notificationService.success("Quality profile updated"); + } else { + this.notificationService.error("Could not update the quality profile"); + } + }); } private setQualityOverrides(req: ITvRequests): void { @@ -187,32 +184,40 @@ export class TvRequestsComponent implements OnInit { } private loadInit() { - this.requestService.getTvRequestsTree(this.amountToLoad, 0) + this.requestService.getTotalTv().subscribe(x => this.totalTv = x); + this.requestService.getTvRequests(this.amountToLoad, 0, OrderType.RequestedDateDesc, FilterType.None, FilterType.None) .subscribe(x => { this.tvRequests = x; - this.tvRequests.forEach((val, index) => { + this.tvRequests.collection.forEach((val, index) => { + this.setDefaults(val); this.loadBackdrop(val); - this.setOverride(val.data); - }); - }); - - if(this.isAdmin) { - this.sonarrService.getQualityProfilesWithoutSettings() - .subscribe(x => this.sonarrProfiles = x); - - this.sonarrService.getRootFoldersWithoutSettings() - .subscribe(x => this.sonarrRootFolders = x); - } + this.setOverride(val); + }); + }); } private resetSearch() { this.currentlyLoaded = 5; this.loadInit(); } - private loadBackdrop(val: TreeNode): void { - this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => { - val.data.background = this.sanitizer.bypassSecurityTrustStyle - ("url(" + x + ")"); + + private setDefaults(val: ITvRequests) { + if (val.posterPath === null) { + val.posterPath = this.defaultPoster; + } + } + + private loadBackdrop(val: ITvRequests): void { + if (val.background != null) { + val.background = this.sanitizer.bypassSecurityTrustStyle + ("url(https://image.tmdb.org/t/p/w1280" + val.background + ")"); + } else { + this.imageService.getTvBanner(val.tvDbId).subscribe(x => { + if (x) { + val.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + x + ")"); + } }); + } } } diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index 58d8415fb..916e2e8de 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -1,5 +1,6 @@ 
+
@@ -18,8 +19,9 @@
-
-
+ + +
@@ -33,7 +35,7 @@
- poster + poster
@@ -65,7 +67,13 @@
- +
+
+ + + +
+
@@ -77,7 +85,7 @@ + {{ 'Common.Request' | translate }}
@@ -85,6 +93,7 @@
+
+
+ + + + + + +
+ + + + +
+ +
+ + + + diff --git a/src/Ombi/ClientApp/app/search/music/albumsearch.component.ts b/src/Ombi/ClientApp/app/search/music/albumsearch.component.ts new file mode 100644 index 000000000..1a44e120e --- /dev/null +++ b/src/Ombi/ClientApp/app/search/music/albumsearch.component.ts @@ -0,0 +1,91 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { TranslateService } from "@ngx-translate/core"; + +import { Subject } from "rxjs"; +import { AuthService } from "../../auth/auth.service"; +import { IIssueCategory, IRequestEngineResult } from "../../interfaces"; +import { ISearchAlbumResult } from "../../interfaces/ISearchMusicResult"; +import { NotificationService, RequestService } from "../../services"; + +@Component({ + selector: "album-search", + templateUrl: "./albumsearch.component.html", +}) +export class AlbumSearchComponent { + + @Input() public result: ISearchAlbumResult; + public engineResult: IRequestEngineResult; + @Input() public defaultPoster: string; + + @Input() public issueCategories: IIssueCategory[]; + @Input() public issuesEnabled: boolean; + + @Input() public musicRequested: Subject; + public issuesBarVisible = false; + public issueRequestTitle: string; + public issueRequestId: number; + public issueProviderId: string; + public issueCategorySelected: IIssueCategory; + + @Output() public setSearch = new EventEmitter(); + + constructor( + private requestService: RequestService, + private notificationService: NotificationService, private authService: AuthService, + private readonly translate: TranslateService) { + } + + public selectArtist(event: Event, artistId: string) { + event.preventDefault(); + this.setSearch.emit(artistId); + } + + public reportIssue(catId: IIssueCategory, req: ISearchAlbumResult) { + this.issueRequestId = req.id; + this.issueRequestTitle = req.title + `(${req.releaseDate.getFullYear})`; + this.issueCategorySelected = catId; + this.issuesBarVisible = true; + this.issueProviderId = req.id.toString(); + } + + public request(searchResult: ISearchAlbumResult) { + searchResult.requested = true; + searchResult.requestProcessing = true; + searchResult.showSubscribe = false; + if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMusic")) { + searchResult.approved = true; + } + + try { + this.requestService.requestAlbum({ foreignAlbumId: searchResult.foreignAlbumId }) + .subscribe(x => { + + this.engineResult = x; + + if (this.engineResult.result) { + this.musicRequested.next(); + this.translate.get("Search.RequestAdded", { title: searchResult.title }).subscribe(x => { + this.notificationService.success(x); + searchResult.processed = true; + }); + } else { + if (this.engineResult.errorMessage && this.engineResult.message) { + this.notificationService.warning("Request Added", `${this.engineResult.message} - ${this.engineResult.errorMessage}`); + } else { + this.notificationService.warning("Request Added", this.engineResult.message ? this.engineResult.message : this.engineResult.errorMessage); + } + searchResult.requested = false; + searchResult.approved = false; + searchResult.processed = false; + searchResult.requestProcessing = false; + + } + }); + } catch (e) { + + searchResult.processed = false; + searchResult.requestProcessing = false; + this.notificationService.error(e); + } + } +} diff --git a/src/Ombi/ClientApp/app/search/music/artistsearch.component.html b/src/Ombi/ClientApp/app/search/music/artistsearch.component.html new file mode 100644 index 000000000..77a68a841 --- /dev/null +++ b/src/Ombi/ClientApp/app/search/music/artistsearch.component.html @@ -0,0 +1,65 @@ +
+ +
+
+
+ poster + +
+
+
+ +

{{result.artistName}}

+
+ + + + + {{result.artistType}} + + + {{result.disambiguation}} + + + Monitored + + + +
+
+

{{result.overview | truncate: 350 }}

+ + +
+ +
+
+
+ + +
+
+ + +
+ + +
+ + + + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/music/artistsearch.component.ts b/src/Ombi/ClientApp/app/search/music/artistsearch.component.ts new file mode 100644 index 000000000..852e294e3 --- /dev/null +++ b/src/Ombi/ClientApp/app/search/music/artistsearch.component.ts @@ -0,0 +1,27 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; + +import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult"; +import { SearchService } from "../../services"; + +@Component({ + selector: "artist-search", + templateUrl: "./artistsearch.component.html", +}) +export class ArtistSearchComponent { + + @Input() public result: ISearchArtistResult; + @Input() public defaultPoster: string; + public searchingAlbums: boolean; + + @Output() public viewAlbumsResult = new EventEmitter(); + + constructor(private searchService: SearchService) { + } + + public viewAllAlbums() { + this.searchingAlbums = true; + this.searchService.getAlbumsForArtist(this.result.forignArtistId).subscribe(x => { + this.viewAlbumsResult.emit(x); + }); + } +} diff --git a/src/Ombi/ClientApp/app/search/music/musicsearch.component.html b/src/Ombi/ClientApp/app/search/music/musicsearch.component.html new file mode 100644 index 000000000..e0d95203b --- /dev/null +++ b/src/Ombi/ClientApp/app/search/music/musicsearch.component.html @@ -0,0 +1,52 @@ + +
+
+ +
+ +
+
+
+
+ + + + +
+
+ + +
+
+
+
+ +
+
+
+ +
+
+ + + + +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts new file mode 100644 index 000000000..54d941da8 --- /dev/null +++ b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts @@ -0,0 +1,192 @@ +import { PlatformLocation } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; +import { DomSanitizer } from "@angular/platform-browser"; +import { TranslateService } from "@ngx-translate/core"; +import { Subject } from "rxjs"; +import { debounceTime, distinctUntilChanged } from "rxjs/operators"; + +import { AuthService } from "../../auth/auth.service"; +import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../../interfaces"; +import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult"; +import { NotificationService, RequestService, SearchService } from "../../services"; + +@Component({ + selector: "music-search", + templateUrl: "./musicsearch.component.html", +}) +export class MusicSearchComponent implements OnInit { + + public searchText: string; + public searchChanged: Subject = new Subject(); + public artistResult: ISearchArtistResult[]; + public albumResult: ISearchAlbumResult[]; + public result: IRequestEngineResult; + public searchApplied = false; + public searchAlbum: boolean = true; + + public musicRequested: Subject = new Subject(); + @Input() public issueCategories: IIssueCategory[]; + @Input() public issuesEnabled: boolean; + public issuesBarVisible = false; + public issueRequestTitle: string; + public issueRequestId: number; + public issueProviderId: string; + public issueCategorySelected: IIssueCategory; + public defaultPoster: string; + + constructor( + private searchService: SearchService, private requestService: RequestService, + private notificationService: NotificationService, private authService: AuthService, + private readonly translate: TranslateService, private sanitizer: DomSanitizer, + private readonly platformLocation: PlatformLocation) { + + this.searchChanged.pipe( + debounceTime(600), // Wait Xms after the last event before emitting last event + distinctUntilChanged(), // only emit if value is different from previous value + ).subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + if(this.searchAlbum) { + this.clearAlbumResults(); + } else { + this.clearArtistResults(); + } + + return; + } + if(this.searchAlbum) { + if(!this.searchText) { + this.searchText = "iowa"; // REMOVE + } + this.searchService.searchAlbum(this.searchText) + .subscribe(x => { + this.albumResult = x; + this.searchApplied = true; + this.setAlbumBackground(); + }); + } else { + this.searchService.searchArtist(this.searchText) + .subscribe(x => { + this.artistResult = x; + this.searchApplied = true; + this.setArtistBackground(); + }); + } + }); + this.defaultPoster = "../../../images/default-music-placeholder.png"; + const base = this.platformLocation.getBaseHrefFromDOM(); + if (base) { + this.defaultPoster = "../../.." + base + "/images/default-music-placeholder.png"; + } + } + + public ngOnInit() { + this.searchText = ""; + this.artistResult = []; + this.result = { + message: "", + result: false, + errorMessage: "", + }; + } + + public search(text: any) { + this.searchChanged.next(text.target.value); + } + + public searchMode(val: boolean) { + this.searchAlbum = val; + if(val) { + // Album + this.clearArtistResults(); + } else { + this.clearAlbumResults(); + } + } + + public setArtistSearch(artistId: string) { + this.searchAlbum = false; + this.clearAlbumResults(); + this.searchChanged.next(`lidarr:${artistId}`); + } + + public request(searchResult: ISearchMovieResult) { + searchResult.requested = true; + searchResult.requestProcessing = true; + searchResult.showSubscribe = false; + if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) { + searchResult.approved = true; + } + + try { + this.requestService.requestMovie({ theMovieDbId: searchResult.id }) + .subscribe(x => { + this.result = x; + + if (this.result.result) { + this.translate.get("Search.RequestAdded", { title: searchResult.title }).subscribe(x => { + this.notificationService.success(x); + searchResult.processed = true; + }); + } else { + if (this.result.errorMessage && this.result.message) { + this.notificationService.warning("Request Added", `${this.result.message} - ${this.result.errorMessage}`); + } else { + this.notificationService.warning("Request Added", this.result.message ? this.result.message : this.result.errorMessage); + } + searchResult.requested = false; + searchResult.approved = false; + searchResult.processed = false; + searchResult.requestProcessing = false; + + } + }); + } catch (e) { + + searchResult.processed = false; + searchResult.requestProcessing = false; + this.notificationService.error(e); + } + } + + public viewAlbumsForArtist(albums: ISearchAlbumResult[]) { + this.clearArtistResults(); + this.searchAlbum = true; + this.albumResult = albums; + this.setAlbumBackground(); + } + + private clearArtistResults() { + this.artistResult = []; + this.searchApplied = false; + } + + private clearAlbumResults() { + this.albumResult = []; + this.searchApplied = false; + } + + private setArtistBackground() { + this.artistResult.forEach((val, index) => { + if (val.poster === null) { + val.poster = this.defaultPoster; + } + val.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + val.banner + ")"); + }); + } + + private setAlbumBackground() { + this.albumResult.forEach((val, index) => { + if (val.disk === null) { + if(val.cover === null) { + val.disk = this.defaultPoster; + } else { + val.disk = val.cover; + } + } + val.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + val.cover + ")"); + }); + } +} diff --git a/src/Ombi/ClientApp/app/search/search.component.html b/src/Ombi/ClientApp/app/search/search.component.html index 398bfd311..046635812 100644 --- a/src/Ombi/ClientApp/app/search/search.component.html +++ b/src/Ombi/ClientApp/app/search/search.component.html @@ -13,6 +13,9 @@
  • {{ 'Search.TvTab' | translate }}
  • +
  • + {{ 'Search.MusicTab' | translate }} +
  • @@ -25,6 +28,9 @@
    +
    + +
    diff --git a/src/Ombi/ClientApp/app/search/search.component.ts b/src/Ombi/ClientApp/app/search/search.component.ts index f583266ee..43d926970 100644 --- a/src/Ombi/ClientApp/app/search/search.component.ts +++ b/src/Ombi/ClientApp/app/search/search.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; -import { IIssueCategory } from "./../interfaces"; -import { IssuesService, SettingsService } from "./../services"; +import { IIssueCategory } from "../interfaces"; +import { IssuesService, SettingsService } from "../services"; @Component({ templateUrl: "./search.component.html", @@ -9,8 +9,10 @@ import { IssuesService, SettingsService } from "./../services"; export class SearchComponent implements OnInit { public showTv: boolean; public showMovie: boolean; + public showMusic: boolean; public issueCategories: IIssueCategory[]; public issuesEnabled = false; + public musicEnabled: boolean; constructor(private issuesService: IssuesService, private settingsService: SettingsService) { @@ -18,8 +20,10 @@ export class SearchComponent implements OnInit { } public ngOnInit() { + this.settingsService.lidarrEnabled().subscribe(x => this.musicEnabled = x); this.showMovie = true; this.showTv = false; + this.showMusic = false; this.issuesService.getCategories().subscribe(x => this.issueCategories = x); this.settingsService.getIssueSettings().subscribe(x => this.issuesEnabled = x.enabled); } @@ -27,10 +31,17 @@ export class SearchComponent implements OnInit { public selectMovieTab() { this.showMovie = true; this.showTv = false; + this.showMusic = false; } public selectTvTab() { this.showMovie = false; this.showTv = true; + this.showMusic = false; + } + public selectMusicTab() { + this.showMovie = false; + this.showTv = false; + this.showMusic = true; } } diff --git a/src/Ombi/ClientApp/app/search/search.module.ts b/src/Ombi/ClientApp/app/search/search.module.ts index 1b7f1310f..4e87940f5 100644 --- a/src/Ombi/ClientApp/app/search/search.module.ts +++ b/src/Ombi/ClientApp/app/search/search.module.ts @@ -7,17 +7,21 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { MovieSearchComponent } from "./moviesearch.component"; import { MovieSearchGridComponent } from "./moviesearchgrid.component"; +import { AlbumSearchComponent } from "./music/albumsearch.component"; +import { ArtistSearchComponent } from "./music/artistsearch.component"; +import { MusicSearchComponent } from "./music/musicsearch.component"; import { SearchComponent } from "./search.component"; import { SeriesInformationComponent } from "./seriesinformation.component"; import { TvSearchComponent } from "./tvsearch.component"; -import { SidebarModule, TreeTableModule } from "primeng/primeng"; +import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng"; import { RequestService } from "../services"; import { SearchService } from "../services"; import { AuthGuard } from "../auth/auth.guard"; +import { RemainingRequestsComponent } from "../requests/remainingrequests.component"; import { SharedModule } from "../shared/shared.module"; const routes: Routes = [ @@ -25,7 +29,7 @@ const routes: Routes = [ { path: "show/:id", component: SeriesInformationComponent, canActivate: [AuthGuard] }, ]; @NgModule({ - imports: [ + imports: [ CommonModule, FormsModule, RouterModule.forChild(routes), @@ -33,6 +37,7 @@ const routes: Routes = [ TreeTableModule, SharedModule, SidebarModule, + TooltipModule, ], declarations: [ SearchComponent, @@ -40,6 +45,10 @@ const routes: Routes = [ TvSearchComponent, SeriesInformationComponent, MovieSearchGridComponent, + RemainingRequestsComponent, + MusicSearchComponent, + ArtistSearchComponent, + AlbumSearchComponent, ], exports: [ RouterModule, diff --git a/src/Ombi/ClientApp/app/search/seriesinformation.component.html b/src/Ombi/ClientApp/app/search/seriesinformation.component.html index e5eb15d69..6cc214c8b 100644 --- a/src/Ombi/ClientApp/app/search/seriesinformation.component.html +++ b/src/Ombi/ClientApp/app/search/seriesinformation.component.html @@ -1,13 +1,4 @@ - -
    +
    diff --git a/src/Ombi/ClientApp/app/search/seriesinformation.component.scss b/src/Ombi/ClientApp/app/search/seriesinformation.component.scss index 1a1ac35b3..c7dca5e86 100644 --- a/src/Ombi/ClientApp/app/search/seriesinformation.component.scss +++ b/src/Ombi/ClientApp/app/search/seriesinformation.component.scss @@ -10,4 +10,16 @@ #requestFloatingBtn:hover { background-color: #555; /* Add a dark-grey background on hover */ + } + + #bannerimage { + width: 758px; + height: 140px; + background-color: black; + background-position: center; + padding-bottom:30px; + } + + .content-space { + padding-top: 10px; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/seriesinformation.component.ts b/src/Ombi/ClientApp/app/search/seriesinformation.component.ts index c4a50ae38..6a918a69a 100644 --- a/src/Ombi/ClientApp/app/search/seriesinformation.component.ts +++ b/src/Ombi/ClientApp/app/search/seriesinformation.component.ts @@ -1,5 +1,4 @@ -import { Component, Input, OnInit} from "@angular/core"; -import "rxjs/add/operator/takeUntil"; +import { Component, Input, OnInit } from "@angular/core"; import { NotificationService } from "../services"; import { RequestService } from "../services"; @@ -9,6 +8,8 @@ import { INewSeasonRequests, IRequestEngineResult, ISeasonsViewModel, ITvRequest import { IEpisodesRequests } from "../interfaces"; import { ISearchTvResult } from "../interfaces"; +import { Subject } from "rxjs"; + @Component({ selector: "seriesinformation", templateUrl: "./seriesinformation.component.html", @@ -19,7 +20,7 @@ export class SeriesInformationComponent implements OnInit { public result: IRequestEngineResult; public series: ISearchTvResult; public requestedEpisodes: IEpisodesRequests[] = []; - + @Input() public tvRequested: Subject; @Input() private seriesId: number; constructor(private searchService: SearchService, private requestService: RequestService, private notificationService: NotificationService) { } @@ -39,30 +40,31 @@ export class SeriesInformationComponent implements OnInit { }); }); - if(!selected) { + if (!selected) { this.notificationService.error("You need to select some episodes!"); return; } this.series.requested = true; - const viewModel = { firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id}; + const viewModel = { firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id}; viewModel.seasons = []; this.series.seasonRequests.forEach((season) => { - const seasonsViewModel = {seasonNumber: season.seasonNumber, episodes: []}; + const seasonsViewModel = {seasonNumber: season.seasonNumber, episodes: []}; season.episodes.forEach(ep => { - if(!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) { - if(ep.requested) { + if (!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) { + if (ep.selected) { seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber}); } } }); - + viewModel.seasons.push(seasonsViewModel); }); this.requestService.requestTv(viewModel) .subscribe(x => { + this.tvRequested.next(); this.result = x as IRequestEngineResult; if (this.result.result) { this.notificationService.success( diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.html b/src/Ombi/ClientApp/app/search/tvsearch.component.html index fb1729e50..44570687a 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.html +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.html @@ -5,7 +5,7 @@ -
    -
    + + +
    - -
    -
    @@ -42,121 +40,125 @@
    {{ 'Search.NoResults' | translate }}
    - - - - {{ 'Search.TvShows.Results' | translate }} - - - -
    -
    - -
    -
    -
    - - poster +
    +
    + +
    +
    -
    -
    -
    +
    +
    +
    + + poster + +
    +
    +
    - -

    {{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})

    +
    +

    {{node.title}} ({{node.firstAired | date: 'yyyy'}})

    +
    + + + {{ 'Search.Movies.HomePage' | translate }} - - {{ 'Search.Movies.HomePage' | translate }} - - {{ 'Search.Movies.Trailer' | translate }} - {{node.data.status}} + + {{ 'Search.Movies.Trailer' | translate }} + + + {{node.status}} + + + {{ 'Search.TvShows.AirDate' | translate }} {{node.firstAired | date: 'dd/MM/yyyy'}} + {{node.network}} - {{ 'Search.TvShows.AirDate' | translate }} {{node.data.firstAired | date: 'dd/MM/yyyy'}} - {{node.data.network}} + {{ 'Common.Available' | translate }} + + {{ 'Common.PartlyAvailable' | translate }} + - - {{ 'Common.Available' | translate }} - - {{ 'Common.PartlyAvailable' | translate }} - - -
    -
    -
    -

    {{node.data.overview}}

    +
    +
    +

    {{node.overview}}

    +
    -
    - - -
    - -
    -
    - - -
    -
    -
    -
    +
    + +
    + +
    +
    + + + +
    +
    +
    -
    - -
    - -
    -
    -
    - - - +
    +
    + +
    + +
    + +
    +
    +
    diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.ts b/src/Ombi/ClientApp/app/search/tvsearch.component.ts index 961e85f63..92d0d549a 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.ts +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.ts @@ -1,13 +1,12 @@ -import { Component, Input, OnInit } from "@angular/core"; +import { PlatformLocation } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; import { DomSanitizer } from "@angular/platform-browser"; -import { Subject } from "rxjs/Subject"; +import { Subject } from "rxjs"; +import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { AuthService } from "../auth/auth.service"; -import { ImageService, NotificationService, RequestService, SearchService} from "../services"; - -import { TreeNode } from "primeng/primeng"; -import { IRequestEngineResult } from "../interfaces"; -import { IIssueCategory, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces"; +import { IIssueCategory, IRequestEngineResult, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces"; +import { ImageService, NotificationService, RequestService, SearchService } from "../services"; @Component({ selector: "tv-search", @@ -18,9 +17,11 @@ export class TvSearchComponent implements OnInit { public searchText: string; public searchChanged = new Subject(); - public tvResults: TreeNode[]; + public tvResults: ISearchTvResult[]; + public tvRequested: Subject = new Subject(); public result: IRequestEngineResult; public searchApplied = false; + public defaultPoster: string; @Input() public issueCategories: IIssueCategory[]; @Input() public issuesEnabled: boolean; @@ -30,52 +31,38 @@ export class TvSearchComponent implements OnInit { public issueProviderId: string; public issueCategorySelected: IIssueCategory; - constructor(private searchService: SearchService, private requestService: RequestService, - private notificationService: NotificationService, private authService: AuthService, - private imageService: ImageService, private sanitizer: DomSanitizer) { - - this.searchChanged - .debounceTime(600) // Wait Xms after the last event before emitting last event - .distinctUntilChanged() // only emit if value is different from previous value - .subscribe(x => { - this.searchText = x as string; - if (this.searchText === "") { - this.clearResults(); - return; - } - this.searchService.searchTvTreeNode(this.searchText) - .subscribe(x => { - this.tvResults = x; - this.searchApplied = true; - this.getExtraInfo(); - }); - }); - } - public openClosestTab(el: any) { - el.preventDefault(); - const rowclass = "undefined ng-star-inserted"; - el = el.toElement || el.relatedTarget || el.target; - while (el.className !== rowclass) { - // Increment the loop to the parent node until we find the row we need - el = el.parentNode; - } - // At this point, the while loop has stopped and `el` represents the element that has - // the class you specified - - // Then we loop through the children to find the caret which we want to click - const caretright = "fa-caret-right"; - const caretdown = "fa-caret-down"; - for (const value of el.children) { - // the caret from the ui has 2 class selectors depending on if expanded or not - // we search for both since we want to still toggle the clicking - if (value.className.includes(caretright) || value.className.includes(caretdown)) { - // Then we tell JS to click the element even though we hid it from the UI - value.click(); - //Break from loop since we no longer need to continue looking - break; + constructor( + private searchService: SearchService, private requestService: RequestService, + private notificationService: NotificationService, private authService: AuthService, + private imageService: ImageService, private sanitizer: DomSanitizer, + private readonly platformLocation: PlatformLocation) { + + this.searchChanged.pipe( + debounceTime(600), // Wait Xms after the last event before emitting last event + distinctUntilChanged(), // only emit if value is different from previous value + ).subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.clearResults(); + return; } + this.searchService.searchTv(this.searchText) + .subscribe(x => { + this.tvResults = x; + this.searchApplied = true; + this.getExtraInfo(); + }); + }); + this.defaultPoster = "../../../images/default_tv_poster.png"; + const base = this.platformLocation.getBaseHrefFromDOM(); + if (base) { + this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png"; } } + public openClosestTab(node: ISearchTvResult,el: any) { + el.preventDefault(); + node.open = !node.open; + } public ngOnInit() { this.searchText = ""; @@ -83,8 +70,9 @@ export class TvSearchComponent implements OnInit { this.result = { message: "", result: false, - errorMessage:"", + errorMessage: "", }; + this.popularShows(); } public search(text: any) { @@ -129,15 +117,17 @@ export class TvSearchComponent implements OnInit { public getExtraInfo() { this.tvResults.forEach((val, index) => { - this.imageService.getTvBanner(val.data.id).subscribe(x => { - - val.data.background = this.sanitizer. - bypassSecurityTrustStyle - ("url(" + x + ")"); + this.imageService.getTvBanner(val.id).subscribe(x => { + if (x) { + val.background = this.sanitizer. + bypassSecurityTrustStyle + ("url(" + x + ")"); + } }); - this.searchService.getShowInformationTreeNode(val.data.id) + this.searchService.getShowInformation(val.id) .subscribe(x => { - if (x.data) { + if (x) { + this.setDefaults(x); this.updateItem(val, x); } else { const index = this.tvResults.indexOf(val, 0); @@ -154,24 +144,25 @@ export class TvSearchComponent implements OnInit { if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) { searchResult.approved = true; } - - const viewModel = { firstSeason: searchResult.firstSeason, latestSeason: searchResult.latestSeason, requestAll: searchResult.requestAll, tvDbId: searchResult.id}; + + const viewModel = { firstSeason: searchResult.firstSeason, latestSeason: searchResult.latestSeason, requestAll: searchResult.requestAll, tvDbId: searchResult.id }; viewModel.seasons = []; searchResult.seasonRequests.forEach((season) => { - const seasonsViewModel = {seasonNumber: season.seasonNumber, episodes: []}; + const seasonsViewModel = { seasonNumber: season.seasonNumber, episodes: [] }; season.episodes.forEach(ep => { - if(!searchResult.latestSeason || !searchResult.requestAll || !searchResult.firstSeason) { - if(ep.requested) { - seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber}); + if (!searchResult.latestSeason || !searchResult.requestAll || !searchResult.firstSeason) { + if (ep.requested) { + seasonsViewModel.episodes.push({ episodeNumber: ep.episodeNumber }); } } }); - + viewModel.seasons.push(seasonsViewModel); }); this.requestService.requestTv(viewModel) .subscribe(x => { + this.tvRequested.next(); this.result = x; if (this.result.result) { this.notificationService.success( @@ -206,25 +197,39 @@ export class TvSearchComponent implements OnInit { public reportIssue(catId: IIssueCategory, req: ISearchTvResult) { this.issueRequestId = req.id; - this.issueRequestTitle = req.title; + const firstAiredDate = new Date(req.firstAired); + this.issueRequestTitle = req.title + ` (${firstAiredDate.getFullYear()})`; this.issueCategorySelected = catId; this.issuesBarVisible = true; this.issueProviderId = req.id.toString(); } - private updateItem(key: TreeNode, updated: TreeNode) { + private updateItem(key: ISearchTvResult, updated: ISearchTvResult) { const index = this.tvResults.indexOf(key, 0); if (index > -1) { // Update certain properties, otherwise we will loose some data - this.tvResults[index].data.banner = updated.data.banner; - this.tvResults[index].data.imdbId = updated.data.imdbId; - this.tvResults[index].data.seasonRequests = updated.data.seasonRequests; - this.tvResults[index].data.seriesId = updated.data.seriesId; - this.tvResults[index].data.fullyAvailable = updated.data.fullyAvailable; - this.tvResults[index].data.backdrop = updated.data.backdrop; + this.tvResults[index].title = updated.title; + this.tvResults[index].banner = updated.banner; + this.tvResults[index].imdbId = updated.imdbId; + this.tvResults[index].seasonRequests = updated.seasonRequests; + this.tvResults[index].seriesId = updated.seriesId; + this.tvResults[index].fullyAvailable = updated.fullyAvailable; + this.tvResults[index].background = updated.banner; } } + private setDefaults(x: ISearchTvResult) { + if (x.banner === null) { + x.banner = this.defaultPoster; + } + + if (x.imdbId === null) { + x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId; + } else { + x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/"; + } + } + private clearResults() { this.tvResults = []; this.searchApplied = false; diff --git a/src/Ombi/ClientApp/app/services/applications/couchpotato.service.ts b/src/Ombi/ClientApp/app/services/applications/couchpotato.service.ts index 667cd1b98..44fa409a4 100644 --- a/src/Ombi/ClientApp/app/services/applications/couchpotato.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/couchpotato.service.ts @@ -1,7 +1,7 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "../service.helpers"; diff --git a/src/Ombi/ClientApp/app/services/applications/emby.service.ts b/src/Ombi/ClientApp/app/services/applications/emby.service.ts index a8897043c..d4e52a630 100644 --- a/src/Ombi/ClientApp/app/services/applications/emby.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/emby.service.ts @@ -1,7 +1,7 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "../service.helpers"; diff --git a/src/Ombi/ClientApp/app/services/applications/index.ts b/src/Ombi/ClientApp/app/services/applications/index.ts index 9433dfce0..295a53415 100644 --- a/src/Ombi/ClientApp/app/services/applications/index.ts +++ b/src/Ombi/ClientApp/app/services/applications/index.ts @@ -4,3 +4,6 @@ export * from "./plex.service"; export * from "./radarr.service"; export * from "./sonarr.service"; export * from "./tester.service"; +export * from "./plexoauth.service"; +export * from "./plextv.service"; +export * from "./lidarr.service"; diff --git a/src/Ombi/ClientApp/app/services/applications/lidarr.service.ts b/src/Ombi/ClientApp/app/services/applications/lidarr.service.ts new file mode 100644 index 000000000..9ef36357a --- /dev/null +++ b/src/Ombi/ClientApp/app/services/applications/lidarr.service.ts @@ -0,0 +1,36 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; + +import { ILidarrProfile, ILidarrRootFolder, IProfiles } from "../../interfaces"; +import { ILidarrSettings } from "../../interfaces"; +import { ServiceHelpers } from "../service.helpers"; + +@Injectable() +export class LidarrService extends ServiceHelpers { + constructor(http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/Lidarr", platformLocation); + } + + public getRootFolders(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}/RootFolders/`, JSON.stringify(settings), {headers: this.headers}); + } + public getQualityProfiles(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}/Profiles/`, JSON.stringify(settings), {headers: this.headers}); + } + + public getRootFoldersFromSettings(): Observable { + return this.http.get(`${this.url}/RootFolders/`, {headers: this.headers}); + } + public getQualityProfilesFromSettings(): Observable { + return this.http.get(`${this.url}/Profiles/`, {headers: this.headers}); + } + + public getMetadataProfiles(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}/Metadata/`, JSON.stringify(settings), {headers: this.headers}); + } + public getLanguages(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}/Langauges/`,JSON.stringify(settings), {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/app/services/applications/plex.service.ts b/src/Ombi/ClientApp/app/services/applications/plex.service.ts index c04a990e1..9f53d0e34 100644 --- a/src/Ombi/ClientApp/app/services/applications/plex.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/plex.service.ts @@ -1,12 +1,12 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "../service.helpers"; -import { IPlexAuthentication, IPlexLibResponse, IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces"; +import { IPlexAuthentication, IPlexLibResponse, IPlexLibSimpleResponse, IPlexOAuthViewModel, IPlexServer, IPlexServerAddViewModel, IPlexServerViewModel, IPlexUserAddResponse, IPlexUserViewModel, IUsersModel } from "../../interfaces"; @Injectable() export class PlexService extends ServiceHelpers { @@ -22,11 +22,27 @@ export class PlexService extends ServiceHelpers { return this.http.post(`${this.url}servers`, JSON.stringify({ login, password }), {headers: this.headers}); } + public getServersFromSettings(): Observable { + return this.http.get(`${this.url}servers`, {headers: this.headers}); + } + public getLibraries(plexSettings: IPlexServer): Observable { return this.http.post(`${this.url}Libraries`, JSON.stringify(plexSettings), {headers: this.headers}); } + public getLibrariesFromSettings(machineId: string): Observable { + return this.http.get(`${this.url}Libraries/${machineId}`, {headers: this.headers}); + } + + public addUserToServer(user: IPlexUserViewModel): Observable { + return this.http.post(`${this.url}user`,JSON.stringify(user), {headers: this.headers}); + } + public getFriends(): Observable { return this.http.get(`${this.url}Friends`, {headers: this.headers}); } + + public oAuth(wizard: IPlexOAuthViewModel): Observable { + return this.http.post(`${this.url}oauth`, JSON.stringify(wizard), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts b/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts new file mode 100644 index 000000000..7a1b495b5 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts @@ -0,0 +1,20 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { Observable } from "rxjs"; + +import { ServiceHelpers } from "../service.helpers"; + +import { IPlexOAuthAccessToken } from "../../interfaces"; + +@Injectable() +export class PlexOAuthService extends ServiceHelpers { + constructor(http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/PlexOAuth/", platformLocation); + } + + public oAuth(pin: number): Observable { + return this.http.get(`${this.url}${pin}`, {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/app/services/applications/plextv.service.ts b/src/Ombi/ClientApp/app/services/applications/plextv.service.ts new file mode 100644 index 000000000..03a49e90c --- /dev/null +++ b/src/Ombi/ClientApp/app/services/applications/plextv.service.ts @@ -0,0 +1,27 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient, HttpHeaders } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { Observable } from "rxjs"; + +import { IPlexPin } from "../../interfaces"; + +@Injectable() +export class PlexTvService { + + constructor(private http: HttpClient, public platformLocation: PlatformLocation) { + } + + public GetPin(clientId: string, applicationName: string): Observable { + const headers = new HttpHeaders({"Content-Type": "application/json", + "X-Plex-Client-Identifier": clientId, + "X-Plex-Product": applicationName, + "X-Plex-Version": "3", + "X-Plex-Device": "Ombi (Web)", + "X-Plex-Platform": "Web", + "Accept": "application/json", + }); + return this.http.post("https://plex.tv/api/v2/pins?strong=true", null, {headers}); + } + +} diff --git a/src/Ombi/ClientApp/app/services/applications/radarr.service.ts b/src/Ombi/ClientApp/app/services/applications/radarr.service.ts index 2403b3c8e..140a59d28 100644 --- a/src/Ombi/ClientApp/app/services/applications/radarr.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/radarr.service.ts @@ -1,7 +1,7 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { IRadarrProfile, IRadarrRootFolder } from "../../interfaces"; import { IRadarrSettings } from "../../interfaces"; diff --git a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts index b6328ad83..756c6ea6c 100644 --- a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts @@ -1,8 +1,8 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ISonarrSettings } from "../../interfaces"; import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; diff --git a/src/Ombi/ClientApp/app/services/applications/tester.service.ts b/src/Ombi/ClientApp/app/services/applications/tester.service.ts index bab94d32f..e692b9196 100644 --- a/src/Ombi/ClientApp/app/services/applications/tester.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/tester.service.ts @@ -1,8 +1,8 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "../service.helpers"; @@ -11,7 +11,9 @@ import { IDiscordNotifcationSettings, IEmailNotificationSettings, IEmbyServer, + ILidarrSettings, IMattermostNotifcationSettings, + IMobileNotificationTestSettings, INewsletterNotificationSettings, IPlexServer, IPushbulletNotificationSettings, @@ -36,6 +38,7 @@ export class TesterService extends ServiceHelpers { public pushbulletTest(settings: IPushbulletNotificationSettings): Observable { return this.http.post(`${this.url}pushbullet`, JSON.stringify(settings), {headers: this.headers}); } + public pushoverTest(settings: IPushoverNotificationSettings): Observable { return this.http.post(`${this.url}pushover`, JSON.stringify(settings), {headers: this.headers}); } @@ -51,7 +54,7 @@ export class TesterService extends ServiceHelpers { public emailTest(settings: IEmailNotificationSettings): Observable { return this.http.post(`${this.url}email`, JSON.stringify(settings), {headers: this.headers}); } - + public plexTest(settings: IPlexServer): Observable { return this.http.post(`${this.url}plex`, JSON.stringify(settings), {headers: this.headers}); } @@ -64,13 +67,17 @@ export class TesterService extends ServiceHelpers { return this.http.post(`${this.url}radarr`, JSON.stringify(settings), {headers: this.headers}); } + public lidarrTest(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}lidarr`, JSON.stringify(settings), {headers: this.headers}); + } + public sonarrTest(settings: ISonarrSettings): Observable { return this.http.post(`${this.url}sonarr`, JSON.stringify(settings), {headers: this.headers}); - } + } public couchPotatoTest(settings: ICouchPotatoSettings): Observable { return this.http.post(`${this.url}couchpotato`, JSON.stringify(settings), {headers: this.headers}); - } + } public telegramTest(settings: ITelegramNotifcationSettings): Observable { return this.http.post(`${this.url}telegram`, JSON.stringify(settings), {headers: this.headers}); @@ -78,8 +85,13 @@ export class TesterService extends ServiceHelpers { public sickrageTest(settings: ISickRageSettings): Observable { return this.http.post(`${this.url}sickrage`, JSON.stringify(settings), {headers: this.headers}); - } + } + public newsletterTest(settings: INewsletterNotificationSettings): Observable { return this.http.post(`${this.url}newsletter`, JSON.stringify(settings), {headers: this.headers}); } + + public mobileNotificationTest(settings: IMobileNotificationTestSettings): Observable { + return this.http.post(`${this.url}mobile`, JSON.stringify(settings), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/identity.service.ts b/src/Ombi/ClientApp/app/services/identity.service.ts index 812885caf..bce159ebe 100644 --- a/src/Ombi/ClientApp/app/services/identity.service.ts +++ b/src/Ombi/ClientApp/app/services/identity.service.ts @@ -1,10 +1,10 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; -import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser } from "../interfaces"; +import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -12,14 +12,14 @@ export class IdentityService extends ServiceHelpers { constructor(http: HttpClient, public platformLocation: PlatformLocation) { super(http, "/api/v1/Identity/", platformLocation); } - public createWizardUser(user: ICreateWizardUser): Observable { - return this.http.post(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers}); + public createWizardUser(user: ICreateWizardUser): Observable { + return this.http.post(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers}); } public getUser(): Observable { return this.http.get(this.url, {headers: this.headers}); } - + public getAccessToken(): Observable { return this.http.get(`${this.url}accesstoken`, {headers: this.headers}); } @@ -43,6 +43,11 @@ export class IdentityService extends ServiceHelpers { public updateUser(user: IUser): Observable { return this.http.put(this.url, JSON.stringify(user), {headers: this.headers}); } + + public updateNotificationPreferences(pref: INotificationPreferences[]): Observable { + return this.http.post(`${this.url}NotificationPreferences`, JSON.stringify(pref), {headers: this.headers}); + } + public updateLocalUser(user: IUpdateLocalUser): Observable { return this.http.put(this.url + "local", JSON.stringify(user), {headers: this.headers}); } @@ -67,6 +72,13 @@ export class IdentityService extends ServiceHelpers { return this.http.post(`${this.url}welcomeEmail`, JSON.stringify(user), {headers: this.headers}); } + public getNotificationPreferences(): Observable { + return this.http.get(`${this.url}notificationpreferences`, {headers: this.headers}); + } + public getNotificationPreferencesForUser(userId: string): Observable { + return this.http.get(`${this.url}notificationpreferences/${userId}`, {headers: this.headers}); + } + public hasRole(role: string): boolean { const roles = localStorage.getItem("roles") as string[] | null; if (roles) { diff --git a/src/Ombi/ClientApp/app/services/image.service.ts b/src/Ombi/ClientApp/app/services/image.service.ts index a640ac4de..6307e7e3d 100644 --- a/src/Ombi/ClientApp/app/services/image.service.ts +++ b/src/Ombi/ClientApp/app/services/image.service.ts @@ -1,6 +1,6 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { HttpClient } from "@angular/common/http"; @@ -20,21 +20,21 @@ export class ImageService extends ServiceHelpers { public getTvBanner(tvdbid: number): Observable { return this.http.get(`${this.url}tv/${tvdbid}`, {headers: this.headers}); } - + public getMoviePoster(movieDbId: string): Observable { return this.http.get(`${this.url}poster/movie/${movieDbId}`, { headers: this.headers }); } - + public getTvPoster(tvdbid: number): Observable { return this.http.get(`${this.url}poster/tv/${tvdbid}`, { headers: this.headers }); } - + public getMovieBackground(movieDbId: string): Observable { return this.http.get(`${this.url}background/movie/${movieDbId}`, { headers: this.headers }); } - + public getTvBackground(tvdbid: number): Observable { return this.http.get(`${this.url}background/tv/${tvdbid}`, { headers: this.headers }); } - + } diff --git a/src/Ombi/ClientApp/app/services/issues.service.ts b/src/Ombi/ClientApp/app/services/issues.service.ts index d72f5a842..41cbb4df1 100644 --- a/src/Ombi/ClientApp/app/services/issues.service.ts +++ b/src/Ombi/ClientApp/app/services/issues.service.ts @@ -1,10 +1,10 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; -import { IIssueCategory, IIssueComments,IIssueCount, IIssues, IIssuesChat, INewIssueComments, IssueStatus, IUpdateStatus } from "../interfaces"; +import { IIssueCategory, IIssueComments, IIssueCount, IIssues, IIssuesChat, INewIssueComments, IssueStatus, IUpdateStatus } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -40,10 +40,10 @@ export class IssuesService extends ServiceHelpers { public createIssue(issue: IIssues): Observable { return this.http.post(this.url, JSON.stringify(issue), {headers: this.headers}); } - + public getIssue(id: number): Observable { return this.http.get(`${this.url}${id}`, {headers: this.headers}); - } + } public getComments(id: number): Observable { return this.http.get(`${this.url}${id}/comments`, {headers: this.headers}); @@ -56,4 +56,8 @@ export class IssuesService extends ServiceHelpers { public updateStatus(model: IUpdateStatus): Observable { return this.http.post(`${this.url}status`, JSON.stringify(model), { headers: this.headers }); } + + public deleteComment(id: number): Observable { + return this.http.delete(`${this.url}comments/${id}`, { headers: this.headers }); + } } diff --git a/src/Ombi/ClientApp/app/services/job.service.ts b/src/Ombi/ClientApp/app/services/job.service.ts index 79957af97..05425683f 100644 --- a/src/Ombi/ClientApp/app/services/job.service.ts +++ b/src/Ombi/ClientApp/app/services/job.service.ts @@ -1,8 +1,8 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "./service.helpers"; @@ -35,6 +35,10 @@ export class JobService extends ServiceHelpers { return this.http.post(`${this.url}plexcontentcacher/`, {headers: this.headers}); } + public runPlexRecentlyAddedCacher(): Observable { + return this.http.post(`${this.url}plexrecentlyadded/`, {headers: this.headers}); + } + public runEmbyCacher(): Observable { return this.http.post(`${this.url}embycontentcacher/`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/services/landingpage.service.ts b/src/Ombi/ClientApp/app/services/landingpage.service.ts index 6cac16037..22964ff71 100644 --- a/src/Ombi/ClientApp/app/services/landingpage.service.ts +++ b/src/Ombi/ClientApp/app/services/landingpage.service.ts @@ -1,6 +1,6 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { HttpClient } from "@angular/common/http"; diff --git a/src/Ombi/ClientApp/app/services/mobile.service.ts b/src/Ombi/ClientApp/app/services/mobile.service.ts index 04d2a6b36..1f9e3fb24 100644 --- a/src/Ombi/ClientApp/app/services/mobile.service.ts +++ b/src/Ombi/ClientApp/app/services/mobile.service.ts @@ -1,10 +1,10 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; -import { IMobileUsersViewModel } from "./../interfaces"; +import { IMobileUsersViewModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() diff --git a/src/Ombi/ClientApp/app/services/notificationMessage.service.ts b/src/Ombi/ClientApp/app/services/notificationMessage.service.ts index 239bcf17c..93727c5d2 100644 --- a/src/Ombi/ClientApp/app/services/notificationMessage.service.ts +++ b/src/Ombi/ClientApp/app/services/notificationMessage.service.ts @@ -1,10 +1,10 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; -import { IMassEmailModel } from "./../interfaces"; +import { IMassEmailModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; diff --git a/src/Ombi/ClientApp/app/services/recentlyAdded.service.ts b/src/Ombi/ClientApp/app/services/recentlyAdded.service.ts index 366c6e583..c062f973b 100644 --- a/src/Ombi/ClientApp/app/services/recentlyAdded.service.ts +++ b/src/Ombi/ClientApp/app/services/recentlyAdded.service.ts @@ -1,10 +1,10 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; -import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces"; +import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -18,7 +18,8 @@ export class RecentlyAddedService extends ServiceHelpers { public getRecentlyAddedTv(): Observable { return this.http.get(`${this. url}tv/`, {headers: this.headers}); - } + } + public getRecentlyAddedTvGrouped(): Observable { return this.http.get(`${this.url}tv/grouped`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index a2757427d..0021dc208 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -1,25 +1,47 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { TreeNode } from "primeng/primeng"; -import { IRequestEngineResult } from "../interfaces"; -import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, ITvRequests, ITvUpdateModel } from "../interfaces"; +import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, + IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; +import { IRemainingRequests } from "../interfaces/IRemainingRequests"; + @Injectable() export class RequestService extends ServiceHelpers { constructor(http: HttpClient, public platformLocation: PlatformLocation) { super(http, "/api/v1/Request/", platformLocation); } + public getRemainingMovieRequests(): Observable { + return this.http.get(`${this.url}movie/remaining`, {headers: this.headers}); + } + + public getRemainingTvRequests(): Observable { + return this.http.get(`${this.url}tv/remaining`, {headers: this.headers}); + } + + public getRemainingMusicRequests(): Observable { + return this.http.get(`${this.url}music/remaining`, {headers: this.headers}); + } + public requestMovie(movie: IMovieRequestModel): Observable { return this.http.post(`${this.url}Movie/`, JSON.stringify(movie), {headers: this.headers}); } + public getTotalMovies(): Observable { + return this.http.get(`${this.url}Movie/total`, {headers: this.headers}); + } + + public getTotalTv(): Observable { + return this.http.get(`${this.url}tv/total`, {headers: this.headers}); + } + public requestTv(tv: ITvRequestViewModel): Observable { return this.http.post(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); } @@ -40,8 +62,8 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}Movie/unavailable`, JSON.stringify(movie), {headers: this.headers}); } - public getMovieRequests(count: number, position: number): Observable { - return this.http.get(`${this.url}movie/${count}/${position}`, {headers: this.headers}); + public getMovieRequests(count: number, position: number, order: OrderType, filter: IFilter): Observable> { + return this.http.get>(`${this.url}movie/${count}/${position}/${order}/${filter.statusFilter}/${filter.availabilityFilter}`, {headers: this.headers}); } public searchMovieRequests(search: string): Observable { @@ -56,8 +78,8 @@ export class RequestService extends ServiceHelpers { return this.http.put(`${this.url}movie/`, JSON.stringify(request), {headers: this.headers}); } - public getTvRequests(count: number, position: number): Observable { - return this.http.get(`${this.url}tv/${count}/${position}`, {headers: this.headers}); + public getTvRequests(count: number, position: number, order: OrderType, status: FilterType, availability: FilterType): Observable> { + return this.http.get>(`${this.url}tv/${count}/${position}/${order}/${status}/${availability}`, {headers: this.headers}); } public getTvRequestsTree(count: number, position: number): Observable { @@ -79,7 +101,7 @@ export class RequestService extends ServiceHelpers { public removeTvRequest(request: ITvRequests) { this.http.delete(`${this.url}tv/${request.id}`, {headers: this.headers}).subscribe(); } - + public markTvAvailable(movie: ITvUpdateModel): Observable { return this.http.post(`${this.url}tv/available`, JSON.stringify(movie), {headers: this.headers}); } @@ -94,11 +116,11 @@ export class RequestService extends ServiceHelpers { public updateChild(child: IChildRequests): Observable { return this.http.put(`${this.url}tv/child`, JSON.stringify(child), {headers: this.headers}); - } + } public denyChild(child: ITvUpdateModel): Observable { return this.http.put(`${this.url}tv/deny`, JSON.stringify(child), {headers: this.headers}); - } + } public approveChild(child: ITvUpdateModel): Observable { return this.http.post(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers}); @@ -106,7 +128,61 @@ export class RequestService extends ServiceHelpers { public deleteChild(child: IChildRequests): Observable { return this.http.delete(`${this.url}tv/child/${child.id}`, {headers: this.headers}); } - public filterMovies(filter: IFilter): Observable { - return this.http.post(`${this.url}movie/filter`, JSON.stringify(filter), {headers: this.headers}); + + public subscribeToMovie(requestId: number): Observable { + return this.http.post(`${this.url}movie/subscribe/${requestId}`, {headers: this.headers}); + } + public unSubscribeToMovie(requestId: number): Observable { + return this.http.post(`${this.url}movie/unsubscribe/${requestId}`, {headers: this.headers}); + } + public subscribeToTv(requestId: number): Observable { + return this.http.post(`${this.url}tv/subscribe/${requestId}`, {headers: this.headers}); + } + public unSubscribeToTv(requestId: number): Observable { + return this.http.post(`${this.url}tv/unsubscribe/${requestId}`, {headers: this.headers}); + } + public setQualityProfile(requestId: number, qualityId: number): Observable { + return this.http.put(`${this.url}tv/quality/${requestId}/${qualityId}`, {headers: this.headers}); + } + public setRootFolder(requestId: number, rootFolderId: number): Observable { + return this.http.put(`${this.url}tv/root/${requestId}/${rootFolderId}`, {headers: this.headers}); } + + // Music + public requestAlbum(Album: IAlbumRequestModel): Observable { + return this.http.post(`${this.url}music/`, JSON.stringify(Album), {headers: this.headers}); + } + + public getTotalAlbums(): Observable { + return this.http.get(`${this.url}music/total`, {headers: this.headers}); + } + + public approveAlbum(Album: IAlbumUpdateModel): Observable { + return this.http.post(`${this.url}music/Approve`, JSON.stringify(Album), {headers: this.headers}); + } + + public denyAlbum(Album: IAlbumUpdateModel): Observable { + return this.http.put(`${this.url}music/Deny`, JSON.stringify(Album), {headers: this.headers}); + } + + public markAlbumAvailable(Album: IAlbumUpdateModel): Observable { + return this.http.post(`${this.url}music/available`, JSON.stringify(Album), {headers: this.headers}); + } + + public markAlbumUnavailable(Album: IAlbumUpdateModel): Observable { + return this.http.post(`${this.url}music/unavailable`, JSON.stringify(Album), {headers: this.headers}); + } + + public getAlbumRequests(count: number, position: number, order: OrderType, filter: IFilter): Observable> { + return this.http.get>(`${this.url}music/${count}/${position}/${order}/${filter.statusFilter}/${filter.availabilityFilter}`, {headers: this.headers}); + } + + public searchAlbumRequests(search: string): Observable { + return this.http.get(`${this.url}music/search/${search}`, {headers: this.headers}); + } + + public removeAlbumRequest(request: IAlbumRequest): any { + this.http.delete(`${this.url}music/${request.id}`, {headers: this.headers}).subscribe(); + } + } diff --git a/src/Ombi/ClientApp/app/services/search.service.ts b/src/Ombi/ClientApp/app/services/search.service.ts index 522b7dddf..9769ad229 100644 --- a/src/Ombi/ClientApp/app/services/search.service.ts +++ b/src/Ombi/ClientApp/app/services/search.service.ts @@ -1,12 +1,13 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { TreeNode } from "primeng/primeng"; import { ISearchMovieResult } from "../interfaces"; import { ISearchTvResult } from "../interfaces"; +import { ISearchAlbumResult, ISearchArtistResult } from "../interfaces/ISearchMusicResult"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -56,16 +57,26 @@ export class SearchService extends ServiceHelpers { return this.http.get(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers}); } - public popularTv(): Observable { - return this.http.get(`${this.url}/Tv/popular/tree`, {headers: this.headers}); + public popularTv(): Observable { + return this.http.get(`${this.url}/Tv/popular`, {headers: this.headers}); } - public mostWatchedTv(): Observable { - return this.http.get(`${this.url}/Tv/mostwatched/tree`, {headers: this.headers}); + public mostWatchedTv(): Observable { + return this.http.get(`${this.url}/Tv/mostwatched`, {headers: this.headers}); } - public anticipatedTv(): Observable { - return this.http.get(`${this.url}/Tv/anticipated/tree`, {headers: this.headers}); + public anticipatedTv(): Observable { + return this.http.get(`${this.url}/Tv/anticipated`, {headers: this.headers}); } - public trendingTv(): Observable { - return this.http.get(`${this.url}/Tv/trending/tree`, {headers: this.headers}); + public trendingTv(): Observable { + return this.http.get(`${this.url}/Tv/trending`, {headers: this.headers}); + } + // Music + public searchArtist(searchTerm: string): Observable { + return this.http.get(`${this.url}/Music/Artist/` + searchTerm); + } + public searchAlbum(searchTerm: string): Observable { + return this.http.get(`${this.url}/Music/Album/` + searchTerm); + } + public getAlbumsForArtist(foreignArtistId: string): Observable { + return this.http.get(`${this.url}/Music/Artist/Album/${foreignArtistId}`); } } diff --git a/src/Ombi/ClientApp/app/services/service.helpers.ts b/src/Ombi/ClientApp/app/services/service.helpers.ts index dc8c33414..c57eae464 100644 --- a/src/Ombi/ClientApp/app/services/service.helpers.ts +++ b/src/Ombi/ClientApp/app/services/service.helpers.ts @@ -1,6 +1,5 @@ import { PlatformLocation } from "@angular/common"; import { HttpClient, HttpHeaders } from "@angular/common/http"; -import "rxjs/add/observable/throw"; export class ServiceHelpers { diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index f6f770a19..b4611bfa8 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -1,7 +1,7 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { IAbout, @@ -18,6 +18,7 @@ import { IJobSettings, IJobSettingsViewModel, ILandingPageSettings, + ILidarrSettings, IMattermostNotifcationSettings, IMobileNotifcationSettings, INewsletterNotificationSettings, @@ -91,10 +92,26 @@ export class SettingsService extends ServiceHelpers { return this.http.post(`${this.url}/Radarr`, JSON.stringify(settings), {headers: this.headers}); } + public getLidarr(): Observable { + return this.http.get(`${this.url}/Lidarr`, {headers: this.headers}); + } + + public lidarrEnabled(): Observable { + return this.http.get(`${this.url}/lidarrenabled`, {headers: this.headers}); + } + + public saveLidarr(settings: ILidarrSettings): Observable { + return this.http.post(`${this.url}/Lidarr`, JSON.stringify(settings), {headers: this.headers}); + } + public getAuthentication(): Observable { return this.http.get(`${this.url}/Authentication`, {headers: this.headers}); } + public getClientId(): Observable { + return this.http.get(`${this.url}/clientid`, {headers: this.headers}); + } + public saveAuthentication(settings: IAuthenticationSettings): Observable { return this.http.post(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers}); } @@ -181,7 +198,7 @@ export class SettingsService extends ServiceHelpers { public getMobileNotificationSettings(): Observable { return this.http.get(`${this.url}/notifications/mobile`, {headers: this.headers}); } - + public saveMobileNotificationSettings(settings: IMobileNotifcationSettings): Observable { return this.http.post(`${this.url}/notifications/mobile`, JSON.stringify(settings), {headers: this.headers}); } @@ -224,7 +241,7 @@ export class SettingsService extends ServiceHelpers { public getTelegramNotificationSettings(): Observable { return this.http.get(`${this.url}/notifications/telegram`, {headers: this.headers}); - } + } public saveTelegramNotificationSettings(settings: ITelegramNotifcationSettings): Observable { return this.http @@ -238,13 +255,13 @@ export class SettingsService extends ServiceHelpers { public saveJobSettings(settings: IJobSettings): Observable { return this.http .post(`${this.url}/jobs`, JSON.stringify(settings), {headers: this.headers}); - } - + } + public testCron(body: ICronViewModelBody): Observable { return this.http .post(`${this.url}/testcron`, JSON.stringify(body), {headers: this.headers}); } - + public getSickRageSettings(): Observable { return this.http.get(`${this.url}/sickrage`, {headers: this.headers}); } @@ -269,14 +286,17 @@ export class SettingsService extends ServiceHelpers { public getNewsletterSettings(): Observable { return this.http.get(`${this.url}/notifications/newsletter`, {headers: this.headers}); - } + } public updateNewsletterDatabase(): Observable { return this.http.post(`${this.url}/notifications/newsletterdatabase`, {headers: this.headers}); - } + } public saveNewsletterSettings(settings: INewsletterNotificationSettings): Observable { return this.http .post(`${this.url}/notifications/newsletter`, JSON.stringify(settings), {headers: this.headers}); } + public verifyUrl(url: string): Observable { + return this.http.post(`${this.url}/customization/urlverify`, JSON.stringify({url}), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/status.service.ts b/src/Ombi/ClientApp/app/services/status.service.ts index 9e84dac4b..795fb1bf1 100644 --- a/src/Ombi/ClientApp/app/services/status.service.ts +++ b/src/Ombi/ClientApp/app/services/status.service.ts @@ -1,8 +1,8 @@ -import { PlatformLocation } from "@angular/common"; +import { PlatformLocation } from "@angular/common"; import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; -import { Observable } from "rxjs/Rx"; +import { Observable } from "rxjs"; import { ServiceHelpers } from "./service.helpers"; diff --git a/src/Ombi/ClientApp/app/settings/about/about.component.html b/src/Ombi/ClientApp/app/settings/about/about.component.html index 6ad185c54..e05677c0f 100644 --- a/src/Ombi/ClientApp/app/settings/about/about.component.html +++ b/src/Ombi/ClientApp/app/settings/about/about.component.html @@ -38,7 +38,7 @@ Discord
    - https://discord.gg/KxYZ64w + https://discord.gg/
    @@ -44,6 +48,12 @@ + + + @@ -90,7 +122,7 @@ Emby User
    Roles + Requests Remaining + + Next Request Due + Last Logged In @@ -81,6 +91,28 @@ + +
    + {{'UserManagment.MovieRemaining' | translate: {remaining: u.movieRequestQuota.remaining, total: u.movieRequestLimit} }} +
    +
    + {{'UserManagment.TvRemaining' | translate: {remaining: u.episodeRequestQuota.remaining, total: u.episodeRequestLimit} }} +
    +
    + {{'UserManagment.MusicRemaining' | translate: {remaining: u.musicRequestQuota.remaining, total: u.musicRequestLimit} }} +
    +
    +
    + {{'UserManagment.MovieDue' | translate: {date: (u.movieRequestQuota.nextRequest | date: 'short')} }} +
    +
    + {{'UserManagment.TvDue' | translate: {date: (u.episodeRequestQuota.nextRequest | date: 'short')} }} +
    +
    + {{'UserManagment.MusicDue' | translate: {date: (u.musicRequestQuota.nextRequest | date: 'short')} }} +
    +
    {{u.lastLoggedIn | date: 'short'}} - Details/Edit + Details/Edit @@ -127,4 +159,5 @@ - \ No newline at end of file + + diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts index 486f814ab..082a6086c 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts @@ -1,7 +1,9 @@ import { Component, OnInit } from "@angular/core"; -import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings,IUser } from "../interfaces"; +import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; +import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces"; import { IdentityService, NotificationService, SettingsService } from "../services"; +import { AddPlexUserComponent } from "./addplexuser.component"; @Component({ templateUrl: "./usermanagement.component.html", @@ -10,7 +12,7 @@ export class UserManagementComponent implements OnInit { public users: IUser[]; public checkAll = false; - public emailSettings: IEmailNotificationSettings; + public emailSettings: IEmailNotificationSettings; public customizationSettings: ICustomizationSettings; public order: string = "userName"; @@ -20,10 +22,13 @@ export class UserManagementComponent implements OnInit { public availableClaims: ICheckbox[]; public bulkMovieLimit?: number; public bulkEpisodeLimit?: number; + public plexEnabled: boolean; - constructor(private readonly identityService: IdentityService, - private readonly settingsService: SettingsService, - private readonly notificationService: NotificationService) { } + constructor(private identityService: IdentityService, + private settingsService: SettingsService, + private notificationService: NotificationService, + private plexSettings: SettingsService, + private modalService: NgbModal) { } public ngOnInit() { this.users = []; @@ -31,13 +36,20 @@ export class UserManagementComponent implements OnInit { this.users = x; }); + this.plexSettings.getPlex().subscribe(x => this.plexEnabled = x.enable); + this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); this.settingsService.getEmailNotificationSettings().subscribe(x => this.emailSettings = x); } + public open() { + const modalRef = this.modalService.open(AddPlexUserComponent, {container:"ombi", backdropClass:"custom-modal-backdrop", windowClass:"window"}); + modalRef.componentInstance.name = "World"; + } + public welcomeEmail(user: IUser) { - if(!user.emailAddress) { + if (!user.emailAddress) { this.notificationService.error("The user needs an email address."); return; } @@ -45,7 +57,13 @@ export class UserManagementComponent implements OnInit { this.notificationService.error("Email Notifications are not setup, cannot send welcome email"); return; } - this.identityService.sendWelcomeEmail(user).subscribe(); + if (!this.emailSettings.notificationTemplates.some(x => { + return x.enabled && x.notificationType === 8; + })) { + this.notificationService.error("The Welcome Email template is not enabled in the Email Setings"); + return; + } + this.identityService.sendWelcomeEmail(user).subscribe(); this.notificationService.success(`Sent a welcome email to ${user.emailAddress}`); } @@ -68,31 +86,31 @@ export class UserManagementComponent implements OnInit { }); this.users.forEach(x => { - if(!x.checked) { + if (!x.checked) { return; } - if(anyRoles) { + if (anyRoles) { x.claims = this.availableClaims; } - if(this.bulkEpisodeLimit && this.bulkEpisodeLimit > 0) { + if (this.bulkEpisodeLimit) { x.episodeRequestLimit = this.bulkEpisodeLimit; } - if(this.bulkMovieLimit && this.bulkMovieLimit > 0) { + if (this.bulkMovieLimit) { x.movieRequestLimit = this.bulkMovieLimit; } this.identityService.updateUser(x).subscribe(y => { - if(!y.successful) { + if (!y.successful) { this.notificationService.error(`Could not update user ${x.userName}. Reason ${y.errors[0]}`); } }); }); - + this.notificationService.success(`Updated users`); this.showBulkEdit = false; this.bulkMovieLimit = undefined; this.bulkEpisodeLimit = undefined; } - + public setOrder(value: string, el: any) { el = el.toElement || el.relatedTarget || el.target || el.srcElement; @@ -109,7 +127,7 @@ export class UserManagementComponent implements OnInit { previousFilter.className = ""; el.className = "active"; } - + this.order = value; - } + } } diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.module.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement.module.ts index 40dba285d..4171fbfbc 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.module.ts +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.module.ts @@ -7,21 +7,23 @@ import { ConfirmationService, ConfirmDialogModule, MultiSelectModule, SidebarMod import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { UpdateDetailsComponent } from "./updatedetails.component"; -import { UserManagementAddComponent } from "./usermanagement-add.component"; -import { UserManagementEditComponent } from "./usermanagement-edit.component"; +import { UserManagementUserComponent } from "./usermanagement-user.component"; import { UserManagementComponent } from "./usermanagement.component"; import { PipeModule } from "../pipes/pipe.module"; -import { IdentityService } from "../services"; +import { IdentityService, PlexService, RadarrService, SonarrService } from "../services"; import { AuthGuard } from "../auth/auth.guard"; import { OrderModule } from "ngx-order-pipe"; +import { AddPlexUserComponent } from "./addplexuser.component"; + +import { SharedModule } from "../shared/shared.module"; const routes: Routes = [ { path: "", component: UserManagementComponent, canActivate: [AuthGuard] }, - { path: "add", component: UserManagementAddComponent, canActivate: [AuthGuard] }, - { path: "edit/:id", component: UserManagementEditComponent, canActivate: [AuthGuard] }, + { path: "user", component: UserManagementUserComponent, canActivate: [AuthGuard] }, + { path: "user/:id", component: UserManagementUserComponent, canActivate: [AuthGuard] }, { path: "updatedetails", component: UpdateDetailsComponent, canActivate: [AuthGuard] }, ]; @@ -38,12 +40,16 @@ const routes: Routes = [ TooltipModule, OrderModule, SidebarModule, + SharedModule, ], declarations: [ UserManagementComponent, - UserManagementAddComponent, - UserManagementEditComponent, UpdateDetailsComponent, + AddPlexUserComponent, + UserManagementUserComponent, + ], + entryComponents:[ + AddPlexUserComponent, ], exports: [ RouterModule, @@ -51,6 +57,9 @@ const routes: Routes = [ providers: [ IdentityService, ConfirmationService, + PlexService, + RadarrService, + SonarrService, ], }) diff --git a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts index 481c96a99..ee5514fcb 100644 --- a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts +++ b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts @@ -1,10 +1,8 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; -import { AuthService } from "../../auth/auth.service"; import { IdentityService } from "../../services"; import { NotificationService } from "../../services"; -import { SettingsService } from "../../services"; @Component({ templateUrl: "./createadmin.component.html", @@ -15,30 +13,16 @@ export class CreateAdminComponent { public password: string; constructor(private identityService: IdentityService, private notificationService: NotificationService, - private router: Router, private auth: AuthService, private settings: SettingsService) { } + private router: Router) { } public createUser() { this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => { - if (x) { - // Log me in. - this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => { - - localStorage.setItem("id_token", c.access_token); - - // Mark that we have done the settings now - this.settings.getOmbi().subscribe(ombi => { - ombi.wizard = true; - - this.settings.saveOmbi(ombi).subscribe(x => { - - this.router.navigate(["search"]); - }); - - }); - - }); + if (x.result) { + this.router.navigate(["login"]); } else { - this.notificationService.error("There was an error... You might want to put this on Github..."); + if(x.errors.length > 0) { + this.notificationService.error(x.errors[0]); + } } }); } diff --git a/src/Ombi/ClientApp/app/wizard/emby/emby.component.ts b/src/Ombi/ClientApp/app/wizard/emby/emby.component.ts index 1a41c649b..7b7c06ed5 100644 --- a/src/Ombi/ClientApp/app/wizard/emby/emby.component.ts +++ b/src/Ombi/ClientApp/app/wizard/emby/emby.component.ts @@ -11,7 +11,7 @@ import { IEmbySettings } from "../../interfaces"; }) export class EmbyComponent implements OnInit { - private embySettings: IEmbySettings; + public embySettings: IEmbySettings; constructor(private embyService: EmbyService, private router: Router, @@ -21,7 +21,7 @@ export class EmbyComponent implements OnInit { public ngOnInit() { this.embySettings = { servers: [], - id:0, + id: 0, enable: true, }; this.embySettings.servers.push({ diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html index 2ba63c43e..a48c0f73e 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html @@ -17,9 +17,16 @@ Please note we do not store this information, we only store your Plex Authorization Token that will allow Ombi to view your media and friends
    - +
    +

    OR

    +
    +
    + +
    +
    + diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts index 146d794fe..d3ee8efa6 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts @@ -1,26 +1,27 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { ConfirmationService } from "primeng/primeng"; - -import { PlexService } from "../../services"; -import { IdentityService, NotificationService, SettingsService } from "../../services"; -import { AuthService } from "./../../auth/auth.service"; +import { PlexService, PlexTvService, SettingsService } from "../../services"; +import { IdentityService, NotificationService } from "../../services"; @Component({ templateUrl: "./plex.component.html", }) -export class PlexComponent { +export class PlexComponent implements OnInit { public login: string; public password: string; + private clientId: string; + constructor(private plexService: PlexService, private router: Router, private notificationService: NotificationService, - private confirmationService: ConfirmationService, - private identityService: IdentityService, - private settings: SettingsService, - private auth: AuthService) { } + private identityService: IdentityService, private plexTv: PlexTvService, + private settingsService: SettingsService) { } + + public ngOnInit(): void { + this.settingsService.getClientId().subscribe(x => this.clientId = x); + } public requestAuthToken() { this.plexService.logIn(this.login, this.password).subscribe(x => { @@ -28,46 +29,33 @@ export class PlexComponent { this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex."); return; } - this.confirmationService.confirm({ - message: "Do you want your Plex user to be the main admin account on Ombi?", - header: "Use Plex Account", - icon: "fa fa-check", - accept: () => { - this.identityService.createWizardUser({ + + this.identityService.createWizardUser({ username: "", password: "", usePlexAdminAccount: true, - }).subscribe(x => { - if (x) { - this.auth.login({ username: this.login, password: this.password, rememberMe:false }).subscribe(c => { - localStorage.setItem("id_token", c.access_token); - - // Mark that we have done the settings now - this.settings.getOmbi().subscribe(ombi => { - ombi.wizard = true; - - this.settings.saveOmbi(ombi).subscribe(x => { - this.settings.getUserManagementSettings().subscribe(usr => { - - usr.importPlexAdmin = true; - this.settings.saveUserManagementSettings(usr).subscribe(saved => { - this.router.navigate(["login"]); - }); - }); - - }); - }); - }); + }).subscribe(y => { + if (y.result) { + this.router.navigate(["login"]); } else { this.notificationService.error("Could not get the Plex Admin Information"); + if(y.errors.length > 0) { + this.notificationService.error(y.errors[0]); + } return; } }); }, - reject: () => { - this.router.navigate(["Wizard/CreateAdmin"]); - }, - }); + ); + } + + public oauth() { + this.plexTv.GetPin(this.clientId, "Ombi").subscribe((pin: any) => { + this.plexService.oAuth({ wizard: true, pin }).subscribe(x => { + if (x.url) { + window.location.href = x.url; + } + }); }); } } diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html new file mode 100644 index 000000000..d60f1d1f0 --- /dev/null +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html @@ -0,0 +1,14 @@ + + +
    +
    +
    +

    Plex Authentication

    +
    + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts new file mode 100644 index 000000000..d6ec14a8f --- /dev/null +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { AuthService } from "../../auth/auth.service"; +import { IdentityService, PlexOAuthService } from "../../services"; + +@Component({ + templateUrl: "./plexoauth.component.html", +}) +export class PlexOAuthComponent implements OnInit { + public pinId: number; + + constructor(private route: ActivatedRoute, + private plexOauth: PlexOAuthService, + private identityService: IdentityService, + private router: Router, + private auth: AuthService) { + + this.route.params + .subscribe((params: any) => { + this.pinId = params.pin; + }); + } + + public ngOnInit(): void { + this.plexOauth.oAuth(this.pinId).subscribe(x => { + if (!x.accessToken) { + return; + // RETURN + } + + this.identityService.createWizardUser({ + username: "", + password: "", + usePlexAdminAccount: true, + }).subscribe(u => { + if (u.result) { + this.auth.oAuth(this.pinId).subscribe(c => { + localStorage.setItem("id_token", c.access_token); + this.router.navigate(["login"]); + }); + } else { + + if(u.errors.length > 0) { + console.log(u.errors[0]); + } + return; + } + }); + + }); + } +} diff --git a/src/Ombi/ClientApp/app/wizard/wizard.module.ts b/src/Ombi/ClientApp/app/wizard/wizard.module.ts index 96cbdddc1..7eae2e4f4 100644 --- a/src/Ombi/ClientApp/app/wizard/wizard.module.ts +++ b/src/Ombi/ClientApp/app/wizard/wizard.module.ts @@ -14,6 +14,8 @@ import { WelcomeComponent } from "./welcome/welcome.component"; import { EmbyService } from "../services"; import { PlexService } from "../services"; import { IdentityService } from "../services"; +import { PlexOAuthService } from "../services"; +import { PlexOAuthComponent } from "./plex/plexoauth.component"; const routes: Routes = [ { path: "", component: WelcomeComponent}, @@ -21,6 +23,7 @@ const routes: Routes = [ { path: "Plex", component: PlexComponent}, { path: "Emby", component: EmbyComponent}, { path: "CreateAdmin", component: CreateAdminComponent}, + { path: "OAuth/:pin", component: PlexOAuthComponent}, ]; @NgModule({ imports: [ @@ -33,6 +36,7 @@ const routes: Routes = [ WelcomeComponent, MediaServerComponent, PlexComponent, + PlexOAuthComponent, CreateAdminComponent, EmbyComponent, ], @@ -44,6 +48,7 @@ const routes: Routes = [ IdentityService, EmbyService, ConfirmationService, + PlexOAuthService, ], }) diff --git a/src/Ombi/ClientApp/imports.ts b/src/Ombi/ClientApp/imports.ts deleted file mode 100644 index bfa7be280..000000000 --- a/src/Ombi/ClientApp/imports.ts +++ /dev/null @@ -1,2 +0,0 @@ -import "rxjs/add/operator/catch"; -import "rxjs/add/operator/map"; diff --git a/src/Ombi/ClientApp/main.ts b/src/Ombi/ClientApp/main.ts index 69914b644..7f926d225 100644 --- a/src/Ombi/ClientApp/main.ts +++ b/src/Ombi/ClientApp/main.ts @@ -1,5 +1,6 @@ -import * as Pace from "pace-progress"; +// Main +import * as Pace from "pace-progress"; Pace.start(); import "bootstrap/dist/js/bootstrap"; @@ -11,8 +12,6 @@ import "./polyfills"; import "hammerjs"; -import "./imports"; - import { enableProdMode } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { AppModule } from "./app/app.module"; @@ -29,7 +28,9 @@ if (module.hot) { oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem); oldRootElem.parentNode.removeChild(oldRootElem); } - modulePromise.then(appModule => appModule.destroy()); + if (modulePromise) { + modulePromise.then(appModule => appModule.destroy()); + } }); } else { enableProdMode(); diff --git a/src/Ombi/ClientApp/polyfills.ts b/src/Ombi/ClientApp/polyfills.ts index 87594e038..a522ba175 100644 --- a/src/Ombi/ClientApp/polyfills.ts +++ b/src/Ombi/ClientApp/polyfills.ts @@ -9,6 +9,7 @@ declare var module: any; if (module.hot) { Error.stackTraceLimit = Infinity; - // tslint:disable:no-var-requires + + // tslint:disable-next-line require("zone.js/dist/long-stack-trace-zone"); } diff --git a/src/Ombi/ClientApp/styles/Styles.scss b/src/Ombi/ClientApp/styles/Styles.scss index bcf9e7829..b41e1c2a7 100644 --- a/src/Ombi/ClientApp/styles/Styles.scss +++ b/src/Ombi/ClientApp/styles/Styles.scss @@ -24,3 +24,7 @@ $bg-colour-disabled: #252424; background: $primary-colour !important; color: white; }*/ + +.label { + margin: 3px; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/styles/Themes/plex.scss b/src/Ombi/ClientApp/styles/Themes/plex.scss index 1e3c27eaf..f965e72c7 100644 --- a/src/Ombi/ClientApp/styles/Themes/plex.scss +++ b/src/Ombi/ClientApp/styles/Themes/plex.scss @@ -345,11 +345,21 @@ button.list-group-item:focus { height: 100%; position: absolute; } + .album-bg { + width: 92%; + height: 100%; + position: absolute; + } .tint { width: 100%; height: 100%; position: absolute; } + .album-tint { + width: 92%; + height: 100%; + position: absolute; + } table.table > thead > tr > th.active { - background-color: transparent; + background-color: $primary-colour; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/styles/_imports.scss b/src/Ombi/ClientApp/styles/_imports.scss index 09da286f8..5b22e1ac4 100644 --- a/src/Ombi/ClientApp/styles/_imports.scss +++ b/src/Ombi/ClientApp/styles/_imports.scss @@ -1,2 +1,2 @@ -@import './styles.scss'; -@import './scrollbar.scss'; \ No newline at end of file +@import './Styles.scss'; +@import './scrollbar.scss'; diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index 619dd8042..a9f3119fa 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -95,6 +95,7 @@ hr { .btn { border-radius: .25rem $i; + margin-bottom: 10px; } .btn-group-separated .btn, @@ -141,6 +142,10 @@ p { font-size: 1.1rem $i; } +.tags { + display: block; +} + label { display: inline-block $i; margin-bottom: .5rem $i; @@ -443,13 +448,14 @@ $border-radius: 10px; margin-right: 10px; position: absolute; left: 0; + top: 3px; bottom: 1px; border: 2px solid #eee; border-radius: 3px; } .checkbox input[type=checkbox] { - display: none; + opacity: 0; } .checkbox input[type=checkbox]:checked + label:before { @@ -541,6 +547,10 @@ $border-radius: 10px; cursor: pointer; } +.table-usermanagement { + margin-top: 20px; +} + .input-group-sm { padding-top: 2px; padding-bottom: 2px; @@ -873,11 +883,6 @@ textarea { display: none; } -.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-right, -.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-down { - display: none; -} - .ui-state-highlight { background: $primary-colour; } @@ -890,15 +895,6 @@ textarea { border: 1px solid $form-color-lighter; } -.ui-treetable tfoot td, .ui-treetable th { - text-align: left; -} - -.ui-treetable tbody td { - white-space: inherit; - overflow: visible; -} - table a:not(.btn) { text-decoration: none; } @@ -936,7 +932,7 @@ a > h4:hover { .backdrop{ box-shadow: 3px 3px 10px #000000; - background-position: center; + background-position: 50% 33%; background-size: cover; } @@ -955,3 +951,61 @@ a > h4:hover { width: 94%; } +.ui-state-active { + background-color: $primary-colour-outline $i; + color: black $i; +} + +#themeContent { + font-family: monospace; +} + +.reported-by, +.reported-user, +.subject-category, +.subject { + display: inline-block; +} + +.custom-modal-backdrop { + opacity: 0.5 !important; + filter: alpha(opacity=0.5); +} + +.window { + opacity: 1 !important; + top: 7%; +} + +.modal.fade .modal-dialog { + -webkit-transform: translate(0, 0%); + -ms-transform: translate(0, 0%); + -o-transform: translate(0, 0%); + transform: translate(0, 0%); + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; +} + +.modal-footer .btn+.btn { + margin-left: 5px; + margin-bottom: 10px; +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: normal; + vertical-align: baseline; + border-radius: .25em; +} + +.album-cover { + width:300px; +} + diff --git a/src/Ombi/Controllers/External/LidarrController.cs b/src/Ombi/Controllers/External/LidarrController.cs new file mode 100644 index 000000000..076d68241 --- /dev/null +++ b/src/Ombi/Controllers/External/LidarrController.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.Lidarr; +using Ombi.Api.Lidarr.Models; +using Ombi.Attributes; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; + +namespace Ombi.Controllers.External +{ + [PowerUser] + [ApiV1] + [Produces("application/json")] + public class LidarrController : Controller + { + public LidarrController(ILidarrApi lidarr, ISettingsService settings, + ICacheService mem) + { + _lidarrApi = lidarr; + _lidarrSettings = settings; + Cache = mem; + } + + private readonly ILidarrApi _lidarrApi; + private readonly ISettingsService _lidarrSettings; + private ICacheService Cache { get; } + + /// + /// Gets the Lidarr profiles. + /// + /// The settings. + /// + [HttpPost("Profiles")] + public async Task> GetProfiles([FromBody] LidarrSettings settings) + { + return await _lidarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + } + + /// + /// Gets the Lidarr root folders. + /// + /// The settings. + /// + [HttpPost("RootFolders")] + public async Task> GetRootFolders([FromBody] LidarrSettings settings) + { + return await _lidarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + } + + /// + /// Gets the Lidarr metadata profiles. + /// + /// The settings. + /// + [HttpPost("Metadata")] + public async Task> GetMetadataProfiles([FromBody] LidarrSettings settings) + { + return await _lidarrApi.GetMetadataProfile(settings.ApiKey, settings.FullUri); + } + /// + /// Gets the Lidarr Langauge profiles. + /// + /// The settings. + /// + [HttpPost("Langauges")] + public async Task> GetLanguageProfiles([FromBody] LidarrSettings settings) + { + return await _lidarrApi.GetLanguageProfile(settings.ApiKey, settings.FullUri); + } + + /// + /// Gets the Lidarr profiles using the saved settings + /// The data is cached for an hour + /// + /// + [HttpGet("Profiles")] + public async Task> GetProfiles() + { + return await Cache.GetOrAdd(CacheKeys.LidarrQualityProfiles, async () => + { + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await _lidarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + } + return null; + }, DateTime.Now.AddHours(1)); + } + + /// + /// Gets the Lidarr root folders using the saved settings. + /// The data is cached for an hour + /// + /// + [HttpGet("RootFolders")] + public async Task> GetRootFolders() + { + return await Cache.GetOrAdd(CacheKeys.LidarrRootFolders, async () => + { + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await _lidarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + } + return null; + }, DateTime.Now.AddHours(1)); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index 2d45d7565..d7cefd091 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -8,9 +8,11 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Plex; using Ombi.Api.Plex.Models; using Ombi.Attributes; +using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Models; using Ombi.Models.External; namespace Ombi.Controllers.External @@ -21,16 +23,18 @@ namespace Ombi.Controllers.External public class PlexController : Controller { public PlexController(IPlexApi plexApi, ISettingsService plexSettings, - ILogger logger) + ILogger logger, IPlexOAuthManager manager) { PlexApi = plexApi; PlexSettings = plexSettings; _log = logger; + _plexOAuthManager = manager; } private IPlexApi PlexApi { get; } private ISettingsService PlexSettings { get; } private readonly ILogger _log; + private readonly IPlexOAuthManager _plexOAuthManager; /// /// Signs into the Plex API. @@ -123,6 +127,100 @@ namespace Ombi.Controllers.External } } + + [HttpGet("Libraries/{machineId}")] + [PowerUser] + public async Task GetPlexLibraries(string machineId) + { + try + { + var s = await PlexSettings.GetSettingsAsync(); + var settings = s.Servers.FirstOrDefault(x => x.MachineIdentifier == machineId); + var libs = await PlexApi.GetLibrariesForMachineId(settings.PlexAuthToken, machineId); + + return new PlexLibrariesLiteResponse + { + Successful = true, + Data = libs.Server.Section + }; + } + catch (Exception e) + { + _log.LogWarning(e, "Error thrown when attempting to obtain the plex libs"); + + var message = e.InnerException != null ? $"{e.Message} - {e.InnerException.Message}" : e.Message; + return new PlexLibrariesLiteResponse + { + Successful = false, + Message = message + }; + } + } + + [HttpPost("user")] + [PowerUser] + public async Task AddUser([FromBody] PlexUserViewModel user) + { + var s = await PlexSettings.GetSettingsAsync(); + var server = s.Servers.FirstOrDefault(x => x.MachineIdentifier == user.MachineIdentifier); + var result = await PlexApi.AddUser(user.Username, user.MachineIdentifier, server.PlexAuthToken, + user.LibsSelected); + if (result.HasError) + { + return Json(new + { + Success = false, + Error = result.Error.Status + }); + } + else + { + return Json(new + { + Success = true + }); + } + } + + /// + /// Gets the plex servers. + /// + /// The u. + /// + [HttpGet("servers")] + [PowerUser] + public async Task GetServers() + { + try + { + var s = await PlexSettings.GetSettingsAsync(); + var servers = new List(); + foreach (var plexServer in s.Servers) + { + servers.Add(new PlexServersAddUserModel + { + ServerId = plexServer.Id, + MachineId = plexServer.MachineIdentifier, + ServerName = plexServer.Name + }); + } + + return Json(new + { + Success = true, + Servers = servers + }); + } + catch (Exception e) + { + _log.LogWarning(e, "Error thrown when attempting to obtain the GetServers for Add User VM"); + return Json(new PlexServersViewModel + { + Success = false, + }); + } + } + /// /// Gets the plex servers. /// @@ -173,5 +271,35 @@ namespace Ombi.Controllers.External // Filter out any dupes return vm.DistinctBy(x => x.Id); } + + [HttpPost("oauth")] + [AllowAnonymous] + public async Task OAuth([FromBody]PlexOAuthViewModel wizard) + { + //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd + // Plex OAuth + // Redirect them to Plex + + Uri url; + if (!wizard.Wizard) + { + url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.id, wizard.Pin.code); + } + else + { + var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; + url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.id, wizard.Pin.code, websiteAddress); + } + + if (url == null) + { + return new JsonResult(new + { + error = "Application URL has not been set" + }); + } + + return new JsonResult(new {url = url.ToString()}); + } } } diff --git a/src/Ombi/Controllers/External/RadarrController.cs b/src/Ombi/Controllers/External/RadarrController.cs index 55fb58455..128b1a9e2 100644 --- a/src/Ombi/Controllers/External/RadarrController.cs +++ b/src/Ombi/Controllers/External/RadarrController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Ombi.Api.Radarr; @@ -12,7 +13,7 @@ using Ombi.Settings.Settings.Models.External; namespace Ombi.Controllers.External { - [PowerUser] + [Authorize] [ApiV1] [Produces("application/json")] public class RadarrController : Controller @@ -34,17 +35,19 @@ namespace Ombi.Controllers.External /// The settings. /// [HttpPost("Profiles")] + [PowerUser] public async Task> GetProfiles([FromBody] RadarrSettings settings) { return await RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri); } /// - /// Gets the Radar root folders. + /// Gets the Radarr root folders. /// /// The settings. /// [HttpPost("RootFolders")] + [PowerUser] public async Task> GetRootFolders([FromBody] RadarrSettings settings) { return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); @@ -70,7 +73,7 @@ namespace Ombi.Controllers.External } /// - /// Gets the Radar root folders using the saved settings. + /// Gets the Radarr root folders using the saved settings. /// The data is cached for an hour /// /// diff --git a/src/Ombi/Controllers/External/SonarrController.cs b/src/Ombi/Controllers/External/SonarrController.cs index 1929c2798..94d1e3edf 100644 --- a/src/Ombi/Controllers/External/SonarrController.cs +++ b/src/Ombi/Controllers/External/SonarrController.cs @@ -11,9 +11,9 @@ using Ombi.Settings.Settings.Models.External; namespace Ombi.Controllers.External { - [Admin] - [ApiV1] - [Produces("application/json")] + [Authorize] + [ApiV1] + [Produces("application/json")] public class SonarrController : Controller { public SonarrController(ISonarrApi sonarr, ISettingsService settings) @@ -31,6 +31,7 @@ namespace Ombi.Controllers.External /// The settings. /// [HttpPost("Profiles")] + [PowerUser] public async Task> GetProfiles([FromBody] SonarrSettings settings) { return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri); @@ -42,6 +43,7 @@ namespace Ombi.Controllers.External /// The settings. /// [HttpPost("RootFolders")] + [PowerUser] public async Task> GetRootFolders([FromBody] SonarrSettings settings) { return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); @@ -55,7 +57,11 @@ namespace Ombi.Controllers.External public async Task> GetProfiles() { var settings = await SonarrSettings.GetSettingsAsync(); - return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + if (settings.Enabled) + { + return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + } + return null; } /// @@ -66,7 +72,12 @@ namespace Ombi.Controllers.External public async Task> GetRootFolders() { var settings = await SonarrSettings.GetSettingsAsync(); - return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + if (settings.Enabled) + { + return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + } + + return null; } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/External/TesterController.cs b/src/Ombi/Controllers/External/TesterController.cs index bad933e86..5e3156bde 100644 --- a/src/Ombi/Controllers/External/TesterController.cs +++ b/src/Ombi/Controllers/External/TesterController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Ombi.Api.CouchPotato; using Ombi.Api.Emby; +using Ombi.Api.Lidarr; using Ombi.Api.Plex; using Ombi.Api.Radarr; using Ombi.Api.SickRage; @@ -14,6 +15,7 @@ using Ombi.Core.Models.UI; using Ombi.Core.Notifications; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Models; using Ombi.Notifications; using Ombi.Notifications.Agents; using Ombi.Notifications.Models; @@ -37,7 +39,8 @@ namespace Ombi.Controllers.External public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN, IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, IPlexApi plex, IEmbyApi emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger log, IEmailProvider provider, - ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter) + ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, IMobileNotification mobileNotification, + ILidarrApi lidarrApi) { Service = service; DiscordNotification = notification; @@ -56,6 +59,8 @@ namespace Ombi.Controllers.External TelegramNotification = telegram; SickRageApi = srApi; Newsletter = newsletter; + MobileNotification = mobileNotification; + LidarrApi = lidarrApi; } private INotificationService Service { get; } @@ -75,6 +80,8 @@ namespace Ombi.Controllers.External private ITelegramNotification TelegramNotification { get; } private ISickRageApi SickRageApi { get; } private INewsletterJob Newsletter { get; } + private IMobileNotification MobileNotification { get; } + private ILidarrApi LidarrApi { get; } /// @@ -388,5 +395,43 @@ namespace Ombi.Controllers.External return false; } } + + [HttpPost("mobile")] + public async Task MobileNotificationTest([FromBody] MobileNotificationTestViewModel settings) + { + try + { + await MobileNotification.NotifyAsync(new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1, UserId = settings.UserId }, settings.Settings); + + return true; + } + catch (Exception e) + { + Log.LogError(LoggingEvents.Api, e, "Could not test Mobile Notifications"); + return false; + } + } + + [HttpPost("lidarr")] + public async Task LidarrTest([FromBody] LidarrSettings settings) + { + try + { + var status = await LidarrApi.Status(settings.ApiKey, settings.FullUri); + if (status != null & status?.version.HasValue() ?? false) + { + return true; + } + else + { + return false; + } + } + catch (Exception e) + { + Log.LogError(LoggingEvents.Api, e, "Could not test Mobile Notifications"); + return false; + } + } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index 6a8aee52c..d138e973e 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -16,6 +16,8 @@ using Ombi.Api.Plex; using Ombi.Attributes; using Ombi.Config; using Ombi.Core.Authentication; +using Ombi.Core.Engine; +using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; using Ombi.Core.Models.UI; using Ombi.Core.Settings; @@ -48,6 +50,7 @@ namespace Ombi.Controllers public IdentityController(OmbiUserManager user, IMapper mapper, RoleManager rm, IEmailProvider prov, ISettingsService s, ISettingsService c, + ISettingsService ombiSettings, IWelcomeEmail welcome, IMovieRequestRepository m, ITvRequestRepository t, @@ -56,7 +59,16 @@ namespace Ombi.Controllers ISettingsService settings, IRepository requestLog, IRepository issues, - IRepository issueComments) + IRepository issueComments, + IRepository notificationRepository, + IRepository subscriptionRepository, + ISettingsService umSettings, + IRepository notificationPreferences, + IRepository userProfiles, + IMusicRequestRepository musicRepo, + IMovieRequestEngine movieRequestEngine, + ITvRequestEngine tvRequestEngine, + IMusicRequestEngine musicEngine) { UserManager = user; Mapper = mapper; @@ -66,6 +78,7 @@ namespace Ombi.Controllers CustomizationSettings = c; WelcomeEmail = welcome; MovieRepo = m; + MusicRepo = musicRepo; TvRepo = t; _log = l; _plexApi = plexApi; @@ -73,6 +86,15 @@ namespace Ombi.Controllers _issuesRepository = issues; _requestLogRepository = requestLog; _issueCommentsRepository = issueComments; + OmbiSettings = ombiSettings; + _requestSubscriptionRepository = subscriptionRepository; + _notificationRepository = notificationRepository; + _userManagementSettings = umSettings; + TvRequestEngine = tvRequestEngine; + MovieRequestEngine = movieRequestEngine; + _userNotificationPreferences = notificationPreferences; + _userQualityProfiles = userProfiles; + MusicRequestEngine = musicEngine; } private OmbiUserManager UserManager { get; } @@ -81,16 +103,25 @@ namespace Ombi.Controllers private IEmailProvider EmailProvider { get; } private ISettingsService EmailSettings { get; } private ISettingsService CustomizationSettings { get; } + private readonly ISettingsService _userManagementSettings; + private ISettingsService OmbiSettings { get; } private IWelcomeEmail WelcomeEmail { get; } private IMovieRequestRepository MovieRepo { get; } private ITvRequestRepository TvRepo { get; } + private IMovieRequestEngine MovieRequestEngine { get; } + private IMusicRequestEngine MusicRequestEngine { get; } + private ITvRequestEngine TvRequestEngine { get; } + private IMusicRequestRepository MusicRepo { get; } private readonly ILogger _log; private readonly IPlexApi _plexApi; private readonly ISettingsService _plexSettings; private readonly IRepository _issuesRepository; private readonly IRepository _issueCommentsRepository; private readonly IRepository _requestLogRepository; - + private readonly IRepository _notificationRepository; + private readonly IRepository _requestSubscriptionRepository; + private readonly IRepository _userNotificationPreferences; + private readonly IRepository _userQualityProfiles; /// /// This is what the Wizard will call when creating the user for the very first time. @@ -104,13 +135,13 @@ namespace Ombi.Controllers [HttpPost("Wizard")] [ApiExplorerSettings(IgnoreApi = true)] [AllowAnonymous] - public async Task CreateWizardUser([FromBody] CreateUserWizardModel user) + public async Task CreateWizardUser([FromBody] CreateUserWizardModel user) { var users = UserManager.Users; - if (users.Any()) + if (users.Any(x => !x.UserName.Equals("api", StringComparison.InvariantCultureIgnoreCase))) { // No one should be calling this. Only the wizard - return false; + return new SaveWizardResult { Result = false, Errors = new List { "Looks like there is an existing user!" } }; } if (user.UsePlexAdminAccount) @@ -120,7 +151,7 @@ namespace Ombi.Controllers if (authToken.IsNullOrEmpty()) { _log.LogWarning("Could not find an auth token to create the plex user with"); - return false; + return new SaveWizardResult { Result = false }; } var plexUser = await _plexApi.GetAccount(authToken); var adminUser = new OmbiUser @@ -131,6 +162,11 @@ namespace Ombi.Controllers ProviderUserId = plexUser.user.id }; + await _userManagementSettings.SaveSettingsAsync(new UserManagementSettings + { + ImportPlexAdmin = true + }); + return await SaveWizardUser(user, adminUser); } @@ -143,9 +179,10 @@ namespace Ombi.Controllers return await SaveWizardUser(user, userToCreate); } - private async Task SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate) + private async Task SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate) { IdentityResult result; + var retVal = new SaveWizardResult(); // When creating the admin as the plex user, we do not pass in the password. if (user.Password.HasValue()) { @@ -173,8 +210,16 @@ namespace Ombi.Controllers if (!result.Succeeded) { LogErrors(result); + retVal.Errors.AddRange(result.Errors.Select(x => x.Description)); } - return result.Succeeded; + + // Update the wizard flag + var settings = await OmbiSettings.GetSettingsAsync(); + settings.Wizard = true; + await OmbiSettings.SaveSettingsAsync(settings); + + retVal.Result = result.Succeeded; + return retVal; } private void LogErrors(IdentityResult result) @@ -194,6 +239,7 @@ namespace Ombi.Controllers await CreateRole(OmbiRoles.RequestMovie); await CreateRole(OmbiRoles.RequestTv); await CreateRole(OmbiRoles.Disabled); + await CreateRole(OmbiRoles.ReceivesNewsletter); } private async Task CreateRole(string role) @@ -212,7 +258,7 @@ namespace Ombi.Controllers [PowerUser] public async Task> GetAllUsers() { - var users = await UserManager.Users + var users = await UserManager.Users.Where(x => x.UserType != UserType.SystemUser) .ToListAsync(); var model = new List(); @@ -265,7 +311,8 @@ namespace Ombi.Controllers LastLoggedIn = user.LastLoggedIn, HasLoggedIn = user.LastLoggedIn.HasValue, EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0, - MovieRequestLimit = user.MovieRequestLimit ?? 0 + MovieRequestLimit = user.MovieRequestLimit ?? 0, + MusicRequestLimit = user.MusicRequestLimit ?? 0, }; foreach (var role in userRoles) @@ -289,6 +336,31 @@ namespace Ombi.Controllers }); } + if (vm.EpisodeRequestLimit > 0) + { + vm.EpisodeRequestQuota = await TvRequestEngine.GetRemainingRequests(user); + } + + if (vm.MovieRequestLimit > 0) + { + vm.MovieRequestQuota = await MovieRequestEngine.GetRemainingRequests(user); + } + + if (vm.MusicRequestLimit > 0) + { + vm.MusicRequestQuota = await MusicRequestEngine.GetRemainingRequests(user); + } + + // Get the quality profiles + vm.UserQualityProfiles = await _userQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == user.Id); + if (vm.UserQualityProfiles == null) + { + vm.UserQualityProfiles = new UserQualityProfiles + { + UserId = user.Id + }; + } + return vm; } @@ -317,6 +389,7 @@ namespace Ombi.Controllers UserType = UserType.LocalUser, MovieRequestLimit = user.MovieRequestLimit, EpisodeRequestLimit = user.EpisodeRequestLimit, + MusicRequestLimit = user.MusicRequestLimit, UserAccessToken = Guid.NewGuid().ToString("N"), }; var userResult = await UserManager.CreateAsync(ombiUser, user.Password); @@ -346,6 +419,20 @@ namespace Ombi.Controllers }; } + // Add the quality profiles + if (user.UserQualityProfiles != null) + { + user.UserQualityProfiles.UserId = ombiUser.Id; + await _userQualityProfiles.Add(user.UserQualityProfiles); + } + else + { + user.UserQualityProfiles = new UserQualityProfiles + { + UserId = ombiUser.Id + }; + } + return new OmbiIdentityResult { Successful = true @@ -460,6 +547,7 @@ namespace Ombi.Controllers user.Email = ui.EmailAddress; user.MovieRequestLimit = ui.MovieRequestLimit; user.EpisodeRequestLimit = ui.EpisodeRequestLimit; + user.MusicRequestLimit = ui.MusicRequestLimit; var updateResult = await UserManager.UpdateAsync(user); if (!updateResult.Succeeded) { @@ -501,6 +589,34 @@ namespace Ombi.Controllers Errors = messages }; } + // Add the quality profiles + if (ui.UserQualityProfiles != null) + { + var currentQualityProfiles = await + _userQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == user.Id); + var add = false; + if (currentQualityProfiles == null) + { + currentQualityProfiles = new UserQualityProfiles + { + UserId = user.Id + }; + add = true; + } + + currentQualityProfiles.RadarrQualityProfile = ui.UserQualityProfiles.RadarrQualityProfile; + currentQualityProfiles.RadarrRootPath = ui.UserQualityProfiles.RadarrRootPath; + currentQualityProfiles.SonarrQualityProfile = ui.UserQualityProfiles.SonarrQualityProfile; + currentQualityProfiles.SonarrQualityProfileAnime = ui.UserQualityProfiles.SonarrQualityProfileAnime; + currentQualityProfiles.SonarrRootPath = ui.UserQualityProfiles.SonarrRootPath; + currentQualityProfiles.SonarrRootPathAnime = ui.UserQualityProfiles.SonarrRootPathAnime; + if (add) + { + await _userQualityProfiles.Add(currentQualityProfiles); + } + await _userQualityProfiles.SaveChangesAsync(); + } + return new OmbiIdentityResult { @@ -532,7 +648,10 @@ namespace Ombi.Controllers // We need to delete all the requests first var moviesUserRequested = MovieRepo.GetAll().Where(x => x.RequestedUserId == userId); var tvUserRequested = TvRepo.GetChild().Where(x => x.RequestedUserId == userId); - + var musicRequested = MusicRepo.GetAll().Where(x => x.RequestedUserId == userId); + var notificationPreferences = _userNotificationPreferences.GetAll().Where(x => x.UserId == userId); + var userQuality = await _userQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == userId); + if (moviesUserRequested.Any()) { await MovieRepo.DeleteRange(moviesUserRequested); @@ -541,6 +660,18 @@ namespace Ombi.Controllers { await TvRepo.DeleteChildRange(tvUserRequested); } + if (musicRequested.Any()) + { + await MusicRepo.DeleteRange(musicRequested); + } + if (notificationPreferences.Any()) + { + await _userNotificationPreferences.DeleteRange(notificationPreferences); + } + if (userQuality != null) + { + await _userQualityProfiles.Delete(userQuality); + } // Delete any issues and request logs var issues = _issuesRepository.GetAll().Where(x => x.UserReportedId == userId); @@ -559,6 +690,19 @@ namespace Ombi.Controllers await _issueCommentsRepository.DeleteRange(issueComments); } + // Delete the Subscriptions and mobile notification ids + var subs = _requestSubscriptionRepository.GetAll().Where(x => x.UserId == userId); + var mobileIds = _notificationRepository.GetAll().Where(x => x.UserId == userId); + if (subs.Any()) + { + await _requestSubscriptionRepository.DeleteRange(subs); + } + + if (mobileIds.Any()) + { + await _notificationRepository.DeleteRange(mobileIds); + } + var result = await UserManager.DeleteAsync(userToDelete); if (result.Succeeded) { @@ -747,6 +891,96 @@ namespace Ombi.Controllers return user.UserAccessToken; } + [HttpGet("notificationpreferences")] + public async Task> GetUserPreferences() + { + var user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name); + return await GetPreferences(user); + } + + [HttpGet("notificationpreferences/{userId}")] + public async Task> GetUserPreferences(string userId) + { + var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == userId); + return await GetPreferences(user); + } + + private readonly List _excludedAgents = new List + { + NotificationAgent.Email, + NotificationAgent.Mobile + }; + private async Task> GetPreferences(OmbiUser user) + { + var userPreferences = await _userNotificationPreferences.GetAll().Where(x => x.UserId == user.Id).ToListAsync(); + + var agents = Enum.GetValues(typeof(NotificationAgent)).Cast().Where(x => !_excludedAgents.Contains(x)); + foreach (var a in agents) + { + var agent = userPreferences.FirstOrDefault(x => x.Agent == a); + if (agent == null) + { + // Create the default + userPreferences.Add(new UserNotificationPreferences + { + Agent = a, + UserId = user.Id, + }); + } + } + + return userPreferences; + } + + [HttpPost("NotificationPreferences")] + public async Task AddUserNotificationPreference([FromBody] List preferences) + { + foreach (var pref in preferences) + { + + // Make sure the user exists + var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == pref.UserId); + if (user == null) + { + return NotFound(); + } + // Check if we are editing a different user than ourself, if we are then we need to power user role + var me = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name); + if (!me.Id.Equals(user.Id, StringComparison.InvariantCultureIgnoreCase)) + { + var isPowerUser = await UserManager.IsInRoleAsync(me, OmbiRoles.PowerUser); + var isAdmin = await UserManager.IsInRoleAsync(me, OmbiRoles.Admin); + if (!isPowerUser && !isAdmin) + { + return Unauthorized(); + } + } + + // Make sure we don't already have a preference for this agent + var existingPreference = await _userNotificationPreferences.GetAll() + .FirstOrDefaultAsync(x => x.UserId == user.Id && x.Agent == pref.Agent); + if (existingPreference != null) + { + // Update it + existingPreference.Value = pref.Value; + existingPreference.Enabled = pref.Enabled; + await _userNotificationPreferences.SaveChangesAsync(); + } + else + { + await _userNotificationPreferences.Add(new UserNotificationPreferences + { + Agent = pref.Agent, + Enabled = pref.Enabled, + UserId = pref.UserId, + Value = pref.Value + }); + } + + } + return Json(true); + } + private async Task> AddRoles(IEnumerable roles, OmbiUser ombiUser) { var roleResult = new List(); diff --git a/src/Ombi/Controllers/ImagesController.cs b/src/Ombi/Controllers/ImagesController.cs index 692966258..6c05cdd3a 100644 --- a/src/Ombi/Controllers/ImagesController.cs +++ b/src/Ombi/Controllers/ImagesController.cs @@ -35,6 +35,10 @@ namespace Ombi.Controllers [HttpGet("tv/{tvdbid}")] public async Task GetTvBanner(int tvdbid) { + if (tvdbid <= 0) + { + return string.Empty; + } var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1)); var images = await FanartTvApi.GetTvImages(tvdbid, key.Value); @@ -90,6 +94,10 @@ namespace Ombi.Controllers [HttpGet("poster/tv/{tvdbid}")] public async Task GetTvPoster(int tvdbid) { + if (tvdbid <= 0) + { + return string.Empty; + } var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1)); var images = await FanartTvApi.GetTvImages(tvdbid, key.Value); @@ -145,6 +153,10 @@ namespace Ombi.Controllers [HttpGet("background/tv/{tvdbid}")] public async Task GetTvBackground(int tvdbid) { + if (tvdbid <= 0) + { + return string.Empty; + } var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1)); var images = await FanartTvApi.GetTvImages(tvdbid, key.Value); @@ -170,8 +182,8 @@ namespace Ombi.Controllers [HttpGet("background")] public async Task GetBackgroundImage() { - var moviesArray = Options.Movies; - var tvArray = Options.TvShows; + var moviesArray = Options.Movies ?? new int[0]; + var tvArray = Options.TvShows ?? new int[0]; var rand = new Random(); var movieUrl = string.Empty; @@ -179,7 +191,7 @@ namespace Ombi.Controllers var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1)); - if (moviesArray.Any()) + if (moviesArray.Length > 0) { var item = rand.Next(moviesArray.Length); var result = await FanartTvApi.GetMovieImages(moviesArray[item].ToString(), key.Value); @@ -189,9 +201,12 @@ namespace Ombi.Controllers result = await FanartTvApi.GetMovieImages(moviesArray[item].ToString(), key.Value); } - movieUrl = result.moviebackground[0].url; + var otherRand = new Random(); + var res = otherRand.Next(result.moviebackground.Length); + + movieUrl = result.moviebackground[res].url; } - if (tvArray.Any()) + if (tvArray.Length > 0) { var item = rand.Next(tvArray.Length); var result = await FanartTvApi.GetTvImages(tvArray[item], key.Value); @@ -200,8 +215,10 @@ namespace Ombi.Controllers { result = await FanartTvApi.GetTvImages(tvArray[item], key.Value); } + var otherRand = new Random(); + var res = otherRand.Next(result.showbackground.Length); - tvUrl = result.showbackground[0].url; + tvUrl = result.showbackground[res].url; } if (!string.IsNullOrEmpty(movieUrl) && !string.IsNullOrEmpty(tvUrl)) diff --git a/src/Ombi/Controllers/IssuesController.cs b/src/Ombi/Controllers/IssuesController.cs index 507c04f78..a228a63ef 100644 --- a/src/Ombi/Controllers/IssuesController.cs +++ b/src/Ombi/Controllers/IssuesController.cs @@ -133,7 +133,11 @@ namespace Ombi.Controllers i.IssueCategory = null; i.UserReportedId = (await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name)).Id; await _issues.Add(i); - + var category = await _categories.GetAll().FirstOrDefaultAsync(x => i.IssueCategoryId == x.Id); + if (category != null) + { + i.IssueCategory = category; + } var notificationModel = new NotificationOptions { RequestId = i.RequestId ?? 0, @@ -142,7 +146,7 @@ namespace Ombi.Controllers RequestType = i.RequestType, Recipient = string.Empty, AdditionalInformation = $"{i.Subject} | {i.Description}", - UserId = i.UserReportedId + UserId = i.UserReportedId, }; AddIssueNotificationSubstitutes(notificationModel, i, User.Identity.Name); @@ -178,6 +182,7 @@ namespace Ombi.Controllers var roles = await _userManager.GetRolesAsync(c.User); vm.Add(new IssueCommentChatViewModel { + Id = c.Id, Comment = c.Comment, Date = c.Date, Username = c.User.UserAlias, @@ -195,7 +200,7 @@ namespace Ombi.Controllers { var user = await _userManager.Users.Where(x => User.Identity.Name == x.UserName) .FirstOrDefaultAsync(); - var issue = await _issues.GetAll().Include(x => x.UserReported).FirstOrDefaultAsync(x => x.Id == comment.IssueId); + var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == comment.IssueId); if (issue == null) { return null; @@ -236,13 +241,25 @@ namespace Ombi.Controllers return await _issueComments.Add(newComment); } + /// + /// Deletes a comment on a issue + /// + [HttpDelete("comments/{id:int}")] + [PowerUser] + public async Task DeleteComment(int id) + { + var comment = await _issueComments.GetAll().FirstOrDefaultAsync(x => x.Id == id); + + await _issueComments.Delete(comment); + return true; + } [HttpPost("status")] public async Task UpdateStatus([FromBody] IssueStateViewModel model) { var user = await _userManager.Users.Where(x => User.Identity.Name == x.UserName) .FirstOrDefaultAsync(); - var issue = await _issues.GetAll().Include(x => x.UserReported).FirstOrDefaultAsync(x => x.Id == model.IssueId); + var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == model.IssueId); if (issue == null) { return false; diff --git a/src/Ombi/Controllers/JobController.cs b/src/Ombi/Controllers/JobController.cs index 76ca42c41..a89346250 100644 --- a/src/Ombi/Controllers/JobController.cs +++ b/src/Ombi/Controllers/JobController.cs @@ -117,7 +117,18 @@ namespace Ombi.Controllers [HttpPost("plexcontentcacher")] public bool StartPlexContentCacher() { - BackgroundJob.Enqueue(() => _plexContentSync.CacheContent()); + BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(false)); + return true; + } + + /// + /// Runs a smaller version of the content cacher + /// + /// + [HttpPost("plexrecentlyadded")] + public bool StartRecentlyAdded() + { + BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(true)); return true; } diff --git a/src/Ombi/Controllers/MobileController.cs b/src/Ombi/Controllers/MobileController.cs index 95703351c..70a6f1053 100644 --- a/src/Ombi/Controllers/MobileController.cs +++ b/src/Ombi/Controllers/MobileController.cs @@ -68,6 +68,7 @@ namespace Ombi.Controllers { vm.Add(new MobileUsersViewModel { + UserId = u.Id, Username = u.UserAlias, Devices = u.NotificationUserIds.Count }); diff --git a/src/Ombi/Controllers/MusicRequestController.cs b/src/Ombi/Controllers/MusicRequestController.cs new file mode 100644 index 000000000..0d763cd86 --- /dev/null +++ b/src/Ombi/Controllers/MusicRequestController.cs @@ -0,0 +1,154 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Core.Engine; +using Ombi.Core.Models.Requests; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Store.Entities.Requests; +using Ombi.Attributes; +using Ombi.Core.Models; +using Ombi.Core.Models.UI; + +namespace Ombi.Controllers +{ + [Authorize] + [Route("api/v1/request/music")] + [Produces("application/json")] + public class MusicRequestController : Controller + { + public MusicRequestController(IMusicRequestEngine engine) + { + _engine = engine; + } + + private readonly IMusicRequestEngine _engine; + + /// + /// Gets album requests. + /// + /// The count of items you want to return. + /// The position. + /// The way we want to order. + /// + /// + [HttpGet("{count:int}/{position:int}/{orderType:int}/{statusType:int}/{availabilityType:int}")] + public async Task> GetRequests(int count, int position, int orderType, int statusType, int availabilityType) + { + return await _engine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); + } + + /// + /// Gets the total amount of album requests. + /// + [HttpGet("total")] + public async Task GetTotalAlbums() + { + return await _engine.GetTotal(); + } + + /// + /// Gets all album requests. + /// + [HttpGet] + public async Task> GetRequests() + { + return await _engine.GetRequests(); + } + + /// + /// Requests a album. + /// + /// The album. + /// + [HttpPost] + public async Task Request([FromBody] MusicAlbumRequestViewModel album) + { + return await _engine.RequestAlbum(album); + } + + /// + /// Searches for a specific album request + /// + /// The search term. + /// + [HttpGet("search/{searchTerm}")] + public async Task> Search(string searchTerm) + { + return await _engine.SearchAlbumRequest(searchTerm); + } + + /// + /// Deletes the specified album request. + /// + /// The request identifier. + /// + [HttpDelete("{requestId:int}")] + [PowerUser] + public async Task DeleteRequest(int requestId) + { + await _engine.RemoveAlbumRequest(requestId); + } + + /// + /// Approves the specified album request. + /// + /// The albums's ID + /// + [HttpPost("approve")] + [PowerUser] + public async Task ApproveAlbum([FromBody] AlbumUpdateModel model) + { + return await _engine.ApproveAlbumById(model.Id); + } + + /// + /// Set's the specified album as available + /// + /// The album's ID + /// + [HttpPost("available")] + [PowerUser] + public async Task MarkAvailable([FromBody] AlbumUpdateModel model) + { + return await _engine.MarkAvailable(model.Id); + } + + /// + /// Set's the specified album as unavailable + /// + /// The album's ID + /// + [HttpPost("unavailable")] + [PowerUser] + public async Task MarkUnAvailable([FromBody] AlbumUpdateModel model) + { + return await _engine.MarkUnavailable(model.Id); + } + + /// + /// Denies the specified album request. + /// + /// The album's ID + /// + [HttpPut("deny")] + [PowerUser] + public async Task Deny([FromBody] AlbumUpdateModel model) + { + return await _engine.DenyAlbumById(model.Id); + } + + /// + /// Gets model containing remaining number of music requests. + /// + [HttpGet("remaining")] + public async Task GetRemainingMusicRequests() + { + return await _engine.GetRemainingRequests(); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Controllers/PlexOAuthController.cs b/src/Ombi/Controllers/PlexOAuthController.cs new file mode 100644 index 000000000..2aad2a2a9 --- /dev/null +++ b/src/Ombi/Controllers/PlexOAuthController.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http.Internal; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.Plex; +using Ombi.Core.Authentication; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; + +namespace Ombi.Controllers +{ + [ApiExplorerSettings(IgnoreApi = true)] + [ApiV1] + [AllowAnonymous] + public class PlexOAuthController : Controller + { + public PlexOAuthController(IPlexOAuthManager manager, IPlexApi plexApi, ISettingsService plexSettings, + ILogger log) + { + _manager = manager; + _plexApi = plexApi; + _plexSettings = plexSettings; + _log = log; + } + + private readonly IPlexOAuthManager _manager; + private readonly IPlexApi _plexApi; + private readonly ISettingsService _plexSettings; + private readonly ILogger _log; + + [HttpGet("{pinId:int}")] + public async Task OAuthWizardCallBack([FromRoute] int pinId) + { + var accessToken = await _manager.GetAccessTokenFromPin(pinId); + if (accessToken.IsNullOrEmpty()) + { + return Json(new + { + success = false, + error = "Authentication did not work. Please try again" + }); + } + var settings = await _plexSettings.GetSettingsAsync(); + var server = await _plexApi.GetServer(accessToken); + var servers = server.Server.FirstOrDefault(); + if (servers == null) + { + _log.LogWarning("Looks like we can't find any Plex Servers"); + } + _log.LogDebug("Adding first server"); + + settings.Enable = true; + settings.Servers = new List { + new PlexServers + { + PlexAuthToken = accessToken, + Id = new Random().Next(), + Ip = servers?.LocalAddresses?.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries)?.FirstOrDefault() ?? string.Empty, + MachineIdentifier = servers?.MachineIdentifier ?? string.Empty, + Port = int.Parse(servers?.Port ?? "0"), + Ssl = (servers?.Scheme ?? "http") != "http", + Name = "Server 1", + } + }; + + await _plexSettings.SaveSettingsAsync(settings); + return Json(new { accessToken }); + } + } +} diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 59bd75606..25270f9dd 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -8,7 +8,11 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Store.Entities.Requests; using System.Diagnostics; +using Ombi.Attributes; +using Ombi.Core.Models.UI; using Ombi.Models; +using Ombi.Store.Entities; +using Ombi.Core.Models; namespace Ombi.Controllers { @@ -31,10 +35,27 @@ namespace Ombi.Controllers /// /// The count of items you want to return. /// The position. - [HttpGet("movie/{count:int}/{position:int}")] - public async Task> GetRequests(int count, int position) + /// The way we want to order. + /// + /// + [HttpGet("movie/{count:int}/{position:int}/{orderType:int}/{statusType:int}/{availabilityType:int}")] + public async Task> GetRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await MovieRequestEngine.GetRequests(count, position); + return await MovieRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); + } + + /// + /// Gets the total amount of movie requests. + /// + [HttpGet("movie/total")] + public async Task GetTotalMovies() + { + return await MovieRequestEngine.GetTotal(); } /// @@ -74,6 +95,7 @@ namespace Ombi.Controllers /// The request identifier. /// [HttpDelete("movie/{requestId:int}")] + [PowerUser] public async Task DeleteRequest(int requestId) { await MovieRequestEngine.RemoveMovieRequest(requestId); @@ -85,6 +107,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPut("movie")] + [PowerUser] public async Task UpdateRequest([FromBody] MovieRequests model) { return await MovieRequestEngine.UpdateMovieRequest(model); @@ -96,6 +119,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPost("movie/approve")] + [PowerUser] public async Task ApproveMovie([FromBody] MovieUpdateModel model) { return await MovieRequestEngine.ApproveMovieById(model.Id); @@ -107,6 +131,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPost("movie/available")] + [PowerUser] public async Task MarkMovieAvailable([FromBody] MovieUpdateModel model) { return await MovieRequestEngine.MarkAvailable(model.Id); @@ -118,6 +143,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPost("movie/unavailable")] + [PowerUser] public async Task MarkMovieUnAvailable([FromBody] MovieUpdateModel model) { return await MovieRequestEngine.MarkUnavailable(model.Id); @@ -129,33 +155,59 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPut("movie/deny")] + [PowerUser] public async Task DenyMovie([FromBody] MovieUpdateModel model) { return await MovieRequestEngine.DenyMovieById(model.Id); } + /// + /// Gets the total amount of TV requests. + /// + [HttpGet("tv/total")] + public async Task GetTotalTV() + { + return await TvRequestEngine.GetTotal(); + } + /// /// Gets the tv requests. /// /// The count of items you want to return. /// The position. + /// + /// + /// /// - [HttpGet("tv/{count:int}/{position:int}/tree")] - public async Task>>> GetTvRequestsTree(int count, int position) + [HttpGet("tv/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + public async Task> GetTvRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await TvRequestEngine.GetRequestsTreeNode(count, position); + return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); } /// - /// Gets the tv requests. + /// Gets the tv requests lite. /// /// The count of items you want to return. /// The position. + /// + /// + /// /// - [HttpGet("tv/{count:int}/{position:int}")] - public async Task> GetTvRequests(int count, int position) + [HttpGet("tvlite/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + public async Task> GetTvRequestsLite(int count, int position, int orderType, int statusType, int availabilityType) { - return await TvRequestEngine.GetRequests(count, position); + return await TvRequestEngine.GetRequestsLite(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); } /// @@ -168,6 +220,27 @@ namespace Ombi.Controllers return await TvRequestEngine.GetRequests(); } + /// + /// Gets the tv requests without the whole object graph (Does not include seasons/episodes). + /// + /// + [HttpGet("tvlite")] + public async Task> GetTvRequestsLite() + { + return await TvRequestEngine.GetRequestsLite(); + } + + /// + /// Returns the full request object for the specified requestId + /// + /// + /// + [HttpGet("tv/{requestId:int}")] + public async Task GetTvRequest(int requestId) + { + return await TvRequestEngine.GetTvRequest(requestId); + } + /// /// Requests a tv show/episode/season. /// @@ -190,23 +263,13 @@ namespace Ombi.Controllers return await TvRequestEngine.SearchTvRequest(searchTerm); } - /// - /// Searches for a specific tv request - /// - /// The search term. - /// - [HttpGet("tv/search/{searchTerm}/tree")] - public async Task>>> SearchTvTree(string searchTerm) - { - return await TvRequestEngine.SearchTvRequestTree(searchTerm); - } - /// /// Deletes the a specific tv request /// /// The request identifier. /// [HttpDelete("tv/{requestId:int}")] + [PowerUser] public async Task DeleteTvRequest(int requestId) { await TvRequestEngine.RemoveTvRequest(requestId); @@ -218,17 +281,47 @@ namespace Ombi.Controllers /// The model. /// [HttpPut("tv")] + [PowerUser] public async Task UpdateRequest([FromBody] TvRequests model) { return await TvRequestEngine.UpdateTvRequest(model); } + /// + /// Updates the root path for this tv show + /// + /// + /// + /// + [HttpPut("tv/root/{requestId:int}/{rootFolderId:int}")] + [PowerUser] + public async Task UpdateRootFolder(int requestId, int rootFolderId) + { + await TvRequestEngine.UpdateRootPath(requestId, rootFolderId); + return true; + } + + /// + /// Updates the quality profile for this tv show + /// + /// + /// + /// + [HttpPut("tv/quality/{requestId:int}/{qualityId:int}")] + [PowerUser] + public async Task UpdateQuality(int requestId, int qualityId) + { + await TvRequestEngine.UpdateQualityProfile(requestId, qualityId); + return true; + } + /// /// Updates the a specific child request /// /// The model. /// [HttpPut("tv/child")] + [PowerUser] public async Task UpdateChild([FromBody] ChildRequests child) { return await TvRequestEngine.UpdateChildRequest(child); @@ -240,6 +333,7 @@ namespace Ombi.Controllers /// This is the child request's ID /// [HttpPut("tv/deny")] + [PowerUser] public async Task DenyChild([FromBody] TvUpdateModel model) { return await TvRequestEngine.DenyChildRequest(model.Id); @@ -251,6 +345,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPost("tv/available")] + [PowerUser] public async Task MarkTvAvailable([FromBody] TvUpdateModel model) { return await TvRequestEngine.MarkAvailable(model.Id); @@ -262,6 +357,7 @@ namespace Ombi.Controllers /// The Movie's ID /// [HttpPost("tv/unavailable")] + [PowerUser] public async Task MarkTvUnAvailable([FromBody] TvUpdateModel model) { return await TvRequestEngine.MarkUnavailable(model.Id); @@ -273,6 +369,7 @@ namespace Ombi.Controllers /// This is the child request's ID /// [HttpPost("tv/approve")] + [PowerUser] public async Task ApproveChild([FromBody] TvUpdateModel model) { return await TvRequestEngine.ApproveChildRequest(model.Id); @@ -283,6 +380,7 @@ namespace Ombi.Controllers /// /// The model. /// + [PowerUser] [HttpDelete("tv/child/{requestId:int}")] public async Task DeleteChildRequest(int requestId) { @@ -329,14 +427,61 @@ namespace Ombi.Controllers } /// - /// Returns a filtered list + /// Subscribes for notifications to a movie request /// - /// - /// - [HttpPost("movie/filter")] - public async Task> Filter([FromBody] FilterViewModel vm) + [HttpPost("movie/subscribe/{requestId:int}")] + public async Task SubscribeToMovie(int requestId) + { + await MovieRequestEngine.SubscribeToRequest(requestId, RequestType.Movie); + return true; + } + + /// + /// Subscribes for notifications to a TV request + /// + [HttpPost("tv/subscribe/{requestId:int}")] + public async Task SubscribeToTv(int requestId) + { + await TvRequestEngine.SubscribeToRequest(requestId, RequestType.TvShow); + return true; + } + + /// + /// UnSubscribes for notifications to a movie request + /// + [HttpPost("movie/unsubscribe/{requestId:int}")] + public async Task UnSubscribeToMovie(int requestId) + { + await MovieRequestEngine.UnSubscribeRequest(requestId, RequestType.Movie); + return true; + } + + /// + /// UnSubscribes for notifications to a TV request + /// + [HttpPost("tv/unsubscribe/{requestId:int}")] + public async Task UnSubscribeToTv(int requestId) + { + await TvRequestEngine.UnSubscribeRequest(requestId, RequestType.TvShow); + return true; + } + + /// + /// Gets model containing remaining number of movie requests. + /// + [HttpGet("movie/remaining")] + public async Task GetRemainingMovieRequests() + { + return await MovieRequestEngine.GetRemainingRequests(); + } + + /// + /// Gets model containing remaining number of tv requests. + /// + [HttpGet("tv/remaining")] + public async Task GetRemainingTvRequests() { - return await MovieRequestEngine.Filter(vm); + return await TvRequestEngine.GetRemainingRequests(); } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/SearchController.cs b/src/Ombi/Controllers/SearchController.cs index 7060a973e..2a7327481 100644 --- a/src/Ombi/Controllers/SearchController.cs +++ b/src/Ombi/Controllers/SearchController.cs @@ -1,14 +1,14 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; - +using Ombi.Api.Lidarr.Models; using Ombi.Core; using Ombi.Core.Engine; using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models; using Ombi.Core.Models.Search; using StackExchange.Profiling; @@ -19,16 +19,18 @@ namespace Ombi.Controllers [Produces("application/json")] public class SearchController : Controller { - public SearchController(IMovieEngine movie, ITvSearchEngine tvEngine, ILogger logger) + public SearchController(IMovieEngine movie, ITvSearchEngine tvEngine, ILogger logger, IMusicSearchEngine music) { MovieEngine = movie; TvEngine = tvEngine; Logger = logger; + MusicEngine = music; } private ILogger Logger { get; } private IMovieEngine MovieEngine { get; } private ITvSearchEngine TvEngine { get; } + private IMusicSearchEngine MusicEngine { get; } /// /// Searches for a movie. @@ -127,31 +129,6 @@ namespace Ombi.Controllers return await TvEngine.Search(searchTerm); } - /// - /// Searches for a Tv Show. - /// - /// The search term. - /// We use TvMaze as the Provider - /// - [HttpGet("tv/{searchTerm}/tree")] - public async Task>> SearchTvTreeNode(string searchTerm) - { - return await TvEngine.SearchTreeNode(searchTerm); - } - - /// - /// Gets extra show information. - /// - /// The TVDB identifier. - /// We use TvMaze as the Provider - /// - [HttpGet("tv/info/{tvdbId}/tree")] - public async Task> GetShowInfoTreeNode(int tvdbId) - { - if (tvdbId == 0) return new TreeNode(); - return await TvEngine.GetShowInformationTreeNode(tvdbId); - } - /// /// Gets extra show information. /// @@ -164,17 +141,6 @@ namespace Ombi.Controllers return await TvEngine.GetShowInformation(tvdbId); } - /// - /// Returns Popular Tv Shows - /// - /// We use Trakt.tv as the Provider - /// - [HttpGet("tv/popular/tree")] - public async Task>> PopularTvTree() - { - return await TvEngine.PopularTree(); - } - /// /// Returns Popular Tv Shows /// @@ -191,66 +157,66 @@ namespace Ombi.Controllers /// /// We use Trakt.tv as the Provider /// - [HttpGet("tv/anticipated/tree")] - public async Task>> AnticipatedTvTree() + [HttpGet("tv/anticipated")] + public async Task> AnticipatedTv() { - return await TvEngine.AnticipatedTree(); + return await TvEngine.Anticipated(); } /// - /// Returns most Anticiplateds tv shows. + /// Returns Most watched shows. /// /// We use Trakt.tv as the Provider /// - [HttpGet("tv/anticipated")] - public async Task> AnticipatedTv() + [HttpGet("tv/mostwatched")] + public async Task> MostWatched() { - return await TvEngine.Anticipated(); + return await TvEngine.MostWatches(); } /// - /// Returns Most watched shows. + /// Returns trending shows /// /// We use Trakt.tv as the Provider /// - [HttpGet("tv/mostwatched/tree")] - public async Task>> MostWatchedTree() + [HttpGet("tv/trending")] + public async Task> Trending() { - return await TvEngine.MostWatchesTree(); + return await TvEngine.Trending(); } /// - /// Returns Most watched shows. + /// Returns the artist information we searched for /// - /// We use Trakt.tv as the Provider + /// We use Lidarr as the Provider /// - [HttpGet("tv/mostwatched")] - public async Task> MostWatched() + [HttpGet("music/artist/{searchTerm}")] + public async Task> SearchArtist(string searchTerm) { - return await TvEngine.MostWatches(); + return await MusicEngine.SearchArtist(searchTerm); } /// - /// Returns trending shows + /// Returns the album information we searched for /// - /// We use Trakt.tv as the Provider + /// We use Lidarr as the Provider /// - [HttpGet("tv/trending/tree")] - public async Task>> TrendingTree() + [HttpGet("music/album/{searchTerm}")] + public async Task> SearchAlbum(string searchTerm) { - return await TvEngine.TrendingTree(); + return await MusicEngine.SearchAlbum(searchTerm); } /// - /// Returns trending shows + /// Returns all albums for the artist using the ForeignArtistId /// - /// We use Trakt.tv as the Provider + /// We use Lidarr as the Provider /// - [HttpGet("tv/trending")] - public async Task> Trending() + [HttpGet("music/artist/album/{foreignArtistId}")] + public async Task> GetAlbumsByArtist(string foreignArtistId) { - return await TvEngine.Trending(); + return await MusicEngine.GetArtistAlbums(foreignArtistId); } } } diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 9ffa9d81f..9785af553 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -1,21 +1,15 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using AutoMapper; using Hangfire; -using Hangfire.RecurringJobExtensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.PlatformAbstractions; using NCrontab; using Ombi.Api.Emby; using Ombi.Attributes; @@ -45,16 +39,6 @@ namespace Ombi.Controllers [Produces("application/json")] public class SettingsController : Controller { - /// - /// Initializes a new instance of the class. - /// - /// The resolver. - /// The mapper. - /// The templateRepo. - /// The embyApi. - /// The radarrCacher. - /// The memory cache. - /// The memory cache. public SettingsController(ISettingsResolver resolver, IMapper mapper, INotificationTemplatesRepository templateRepo, @@ -115,7 +99,7 @@ namespace Ombi.Controllers OsArchitecture = RuntimeInformation.OSArchitecture.ToString(), OsDescription = RuntimeInformation.OSDescription, ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(), - ApplicationBasePath =PlatformServices.Default.Application.ApplicationBasePath + ApplicationBasePath =Directory.GetCurrentDirectory() }; var version = AssemblyHelper.GetRuntimeVersion(); @@ -142,7 +126,22 @@ namespace Ombi.Controllers [HttpGet("plex")] public async Task PlexSettings() { - return await Get(); + var s = await Get(); + return s; + } + + [HttpGet("clientid")] + [AllowAnonymous] + public async Task GetClientId() + { + var s = await Get(); + if (s.InstallId == Guid.Empty) + { + s.InstallId = Guid.NewGuid(); + // Save it + await PlexSettings(s); + } + return s.InstallId.ToString("N"); } /// @@ -153,6 +152,10 @@ namespace Ombi.Controllers [HttpPost("plex")] public async Task PlexSettings([FromBody]PlexSettings plex) { + if (plex.InstallId == null || plex.InstallId == Guid.Empty) + { + plex.InstallId = Guid.NewGuid(); + } var result = await Save(plex); return result; } @@ -232,6 +235,13 @@ namespace Ombi.Controllers return await Save(settings); } + [ApiExplorerSettings(IgnoreApi = true)] + [HttpPost("customization/urlverify")] + public bool VerifyUrl([FromBody]UrlVerifyModel url) + { + return Uri.TryCreate(url.Url, UriKind.Absolute, out var __); + } + /// /// Get's the preset themes available /// @@ -308,6 +318,39 @@ namespace Ombi.Controllers return await Get(); } + /// + /// Gets the Lidarr Settings. + /// + /// + [HttpGet("lidarr")] + public async Task LidarrSettings() + { + return await Get(); + } + + /// + /// Gets the Lidarr Settings. + /// + /// + [HttpGet("lidarrenabled")] + [AllowAnonymous] + public async Task LidarrEnabled() + { + var settings = await Get(); + return settings.Enabled; + } + + /// + /// Save the Lidarr settings. + /// + /// The settings. + /// + [HttpPost("lidarr")] + public async Task LidarrSettings([FromBody]LidarrSettings settings) + { + return await Save(settings); + } + /// /// Save the Authentication settings. /// @@ -473,7 +516,11 @@ namespace Ombi.Controllers j.UserImporter = j.UserImporter.HasValue() ? j.UserImporter : JobSettingsHelper.UserImporter(j); j.SickRageSync = j.SickRageSync.HasValue() ? j.SickRageSync : JobSettingsHelper.SickRageSync(j); j.RefreshMetadata = j.RefreshMetadata.HasValue() ? j.RefreshMetadata : JobSettingsHelper.RefreshMetadata(j); - + j.PlexRecentlyAddedSync = j.PlexRecentlyAddedSync.HasValue() ? j.PlexRecentlyAddedSync : JobSettingsHelper.PlexRecentlyAdded(j); + j.Newsletter = j.Newsletter.HasValue() ? j.Newsletter : JobSettingsHelper.Newsletter(j); + j.LidarrArtistSync = j.LidarrArtistSync.HasValue() ? j.LidarrArtistSync : JobSettingsHelper.LidarrArtistSync(j); + j.IssuesPurge = j.IssuesPurge.HasValue() ? j.IssuesPurge : JobSettingsHelper.IssuePurge(j); + return j; } diff --git a/src/Ombi/Controllers/StatsController.cs b/src/Ombi/Controllers/StatsController.cs new file mode 100644 index 000000000..0d871d80f --- /dev/null +++ b/src/Ombi/Controllers/StatsController.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Attributes; +using Ombi.Core.Engine; + +namespace Ombi.Controllers +{ + [ApiV1] + [Admin] + [Authorize] + [Produces("application/json")] + public class StatsController : Controller + { + public StatsController(IUserStatsEngine eng) + { + _statsEngine = eng; + } + + private readonly IUserStatsEngine _statsEngine; + + [HttpGet] + public async Task GetUserStats(SummaryRequest req) + { + return await _statsEngine.GetSummary(req); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Controllers/StatusController.cs b/src/Ombi/Controllers/StatusController.cs index 14ead2ba1..bc0fd3908 100644 --- a/src/Ombi/Controllers/StatusController.cs +++ b/src/Ombi/Controllers/StatusController.cs @@ -30,6 +30,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ombi.Core.Settings; +using Ombi.Helpers; using Ombi.Settings.Settings.Models; namespace Ombi.Controllers @@ -57,6 +58,18 @@ namespace Ombi.Controllers } + /// + /// Returns information about this ombi instance + /// + /// + [AllowAnonymous] + [HttpGet("info")] + public string GetInfo() + { + return AssemblyHelper.GetRuntimeVersion(); + } + + /// /// Checks to see if we have run through the wizard /// diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 18da61e3a..1314a741a 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; +using System.Net.Http; using System.Security.Claims; using System.Text; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using Ombi.Api; using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Models; @@ -21,20 +23,23 @@ namespace Ombi.Controllers { [ApiV1] [Produces("application/json")] - public class TokenController + public class TokenController : Controller { - public TokenController(OmbiUserManager um, IOptions ta, IAuditRepository audit, ITokenRepository token) + public TokenController(OmbiUserManager um, IOptions ta, IAuditRepository audit, ITokenRepository token, + IPlexOAuthManager oAuthManager) { _userManager = um; _tokenAuthenticationOptions = ta.Value; _audit = audit; _token = token; + _plexOAuthManager = oAuthManager; } private readonly TokenAuthentication _tokenAuthenticationOptions; private readonly IAuditRepository _audit; private readonly ITokenRepository _token; private readonly OmbiUserManager _userManager; + private readonly IPlexOAuthManager _plexOAuthManager; /// /// Gets the token. @@ -44,71 +49,131 @@ namespace Ombi.Controllers [HttpPost] public async Task GetToken([FromBody] UserAuthModel model) { - await _audit.Record(AuditType.None, AuditArea.Authentication, + if (!model.UsePlexOAuth) + { + await _audit.Record(AuditType.None, AuditArea.Authentication, $"UserName {model.Username} attempting to authenticate"); - var user = await _userManager.FindByNameAsync(model.Username); - - if (user == null) - { - // Could this be an email login? - user = await _userManager.FindByEmailAsync(model.Username); + var user = await _userManager.FindByNameAsync(model.Username); if (user == null) { - return new UnauthorizedResult(); - } + // Could this be an email login? + user = await _userManager.FindByEmailAsync(model.Username); - user.EmailLogin = true; - } + if (user == null) + { + return new UnauthorizedResult(); + } + + user.EmailLogin = true; + } - // Verify Password - if (await _userManager.CheckPasswordAsync(user, model.Password)) - { - var roles = await _userManager.GetRolesAsync(user); - if (roles.Contains(OmbiRoles.Disabled)) + // Verify Password + if (await _userManager.CheckPasswordAsync(user, model.Password)) { - return new UnauthorizedResult(); + return await CreateToken(model.RememberMe, user); } - - user.LastLoggedIn = DateTime.UtcNow; - await _userManager.UpdateAsync(user); - - var claims = new List - { - new Claim(JwtRegisteredClaimNames.Sub, user.UserName), - new Claim(ClaimTypes.NameIdentifier, user.Id), - new Claim(ClaimTypes.Name, user.UserName), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) - }; - claims.AddRange(roles.Select(role => new Claim("role", role))); - - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); - var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - - - var token = new JwtSecurityToken( - claims: claims, - expires: model.RememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), - signingCredentials: creds, - audience: "Ombi", issuer:"Ombi" - ); - var accessToken = new JwtSecurityTokenHandler().WriteToken(token); - if (model.RememberMe) + } + else + { + // Plex OAuth + // Redirect them to Plex + + var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; + //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd + var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.id, model.PlexTvPin.code, websiteAddress); + if (url == null) { - // Save the token so we can refresh it later - //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); + return new JsonResult(new + { + error = "Application URL has not been set" + }); } + return new JsonResult(new { url = url.ToString(), pinId = model.PlexTvPin.id }); + } + + return new UnauthorizedResult(); + } + + private async Task CreateToken(bool rememberMe, OmbiUser user) + { + var roles = await _userManager.GetRolesAsync(user); + + if (roles.Contains(OmbiRoles.Disabled)) + { + return new UnauthorizedResult(); + } + + user.LastLoggedIn = DateTime.UtcNow; + await _userManager.UpdateAsync(user); + + var claims = new List + { + new Claim(JwtRegisteredClaimNames.Sub, user.UserName), + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + claims.AddRange(roles.Select(role => new Claim("role", role))); + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + + var token = new JwtSecurityToken( + claims: claims, + expires: rememberMe ? DateTime.Now.AddDays(7) : DateTime.Now.AddDays(1), + signingCredentials: creds, + audience: "Ombi", issuer: "Ombi" + ); + var accessToken = new JwtSecurityTokenHandler().WriteToken(token); + if (rememberMe) + { + // Save the token so we can refresh it later + //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); + } + + return new JsonResult(new + { + access_token = accessToken, + expiration = token.ValidTo + }); + } + + [HttpGet("{pinId:int}")] + public async Task OAuth(int pinId) + { + var accessToken = await _plexOAuthManager.GetAccessTokenFromPin(pinId); + if (accessToken.IsNullOrEmpty()) + { + // Looks like we are not authenticated. return new JsonResult(new { - access_token = accessToken, - expiration = token.ValidTo + errorMessage = "Could not authenticate with Plex" }); } - return new UnauthorizedResult(); + // Let's look for the users account + var account = await _plexOAuthManager.GetAccount(accessToken); + + // Get the ombi user + var user = await _userManager.FindByNameAsync(account.user.username); + + if (user == null) + { + // Could this be an email login? + user = await _userManager.FindByEmailAsync(account.user.email); + + if (user == null) + { + return new UnauthorizedResult(); + } + } + + return await CreateToken(true, user); } /// @@ -127,7 +192,7 @@ namespace Ombi.Controllers { return new UnauthorizedResult(); } - + throw new NotImplementedException(); } diff --git a/src/Ombi/ErrorHandlingMiddlewear.cs b/src/Ombi/ErrorHandlingMiddlewear.cs index a50407ccd..01e22d0b0 100644 --- a/src/Ombi/ErrorHandlingMiddlewear.cs +++ b/src/Ombi/ErrorHandlingMiddlewear.cs @@ -2,6 +2,8 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace Ombi @@ -29,6 +31,9 @@ namespace Ombi private static Task HandleExceptionAsync(HttpContext context, Exception exception) { + var loggerFact = context.RequestServices.GetService(); + var logger = loggerFact.CreateLogger(); + logger.LogError(exception, "Something bad happened, ErrorMiddleware caught this"); var code = HttpStatusCode.InternalServerError; // 500 if unexpected //if (exception is NotFoundException) code = HttpStatusCode.NotFound; diff --git a/src/Ombi/Models/AlbumUpdateModel.cs b/src/Ombi/Models/AlbumUpdateModel.cs new file mode 100644 index 000000000..7d7374c43 --- /dev/null +++ b/src/Ombi/Models/AlbumUpdateModel.cs @@ -0,0 +1,33 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: MovieUpdateModel.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 +namespace Ombi.Core.Models.Requests +{ + public class AlbumUpdateModel + { + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/External/PlexLibrariesResponse.cs b/src/Ombi/Models/External/PlexLibrariesResponse.cs index d1033f12e..e5aaa5a3c 100644 --- a/src/Ombi/Models/External/PlexLibrariesResponse.cs +++ b/src/Ombi/Models/External/PlexLibrariesResponse.cs @@ -25,6 +25,7 @@ // ************************************************************************/ #endregion +using System.Collections.Generic; using Ombi.Api.Plex.Models; namespace Ombi.Models.External @@ -35,4 +36,11 @@ namespace Ombi.Models.External public bool Successful { get; set; } public string Message { get; set; } } + + public class PlexLibrariesLiteResponse + { + public List Data { get; set; } + public bool Successful { get; set; } + public string Message { get; set; } + } } \ No newline at end of file diff --git a/src/Ombi/Models/External/PlexServersAddUserModel.cs b/src/Ombi/Models/External/PlexServersAddUserModel.cs new file mode 100644 index 000000000..8d272b37e --- /dev/null +++ b/src/Ombi/Models/External/PlexServersAddUserModel.cs @@ -0,0 +1,9 @@ +namespace Ombi.Models.External +{ + public class PlexServersAddUserModel + { + public string ServerName { get; set; } + public int ServerId { get; set; } + public string MachineId { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/External/PlexUserViewModel.cs b/src/Ombi/Models/External/PlexUserViewModel.cs new file mode 100644 index 000000000..7dddf6a9f --- /dev/null +++ b/src/Ombi/Models/External/PlexUserViewModel.cs @@ -0,0 +1,9 @@ +namespace Ombi.Models.External +{ + public class PlexUserViewModel + { + public string Username { get; set; } + public string MachineIdentifier { get; set; } + public int[] LibsSelected { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/Identity/AddNotificationPreference.cs b/src/Ombi/Models/Identity/AddNotificationPreference.cs new file mode 100644 index 000000000..51dc7f6fe --- /dev/null +++ b/src/Ombi/Models/Identity/AddNotificationPreference.cs @@ -0,0 +1,12 @@ +using Ombi.Helpers; + +namespace Ombi.Models.Identity +{ + public class AddNotificationPreference + { + public NotificationAgent Agent { get; set; } + public string UserId { get; set; } + public string Value { get; set; } + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/Identity/SaveWizardResult.cs b/src/Ombi/Models/Identity/SaveWizardResult.cs new file mode 100644 index 000000000..0d9cdd048 --- /dev/null +++ b/src/Ombi/Models/Identity/SaveWizardResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ombi.Models.Identity +{ + public class SaveWizardResult + { + public bool Result { get; set; } + public List Errors { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/src/Ombi/Models/IssueCommentChatViewModel.cs b/src/Ombi/Models/IssueCommentChatViewModel.cs index cc943263c..a75ccdfcd 100644 --- a/src/Ombi/Models/IssueCommentChatViewModel.cs +++ b/src/Ombi/Models/IssueCommentChatViewModel.cs @@ -4,6 +4,7 @@ namespace Ombi.Models { public class IssueCommentChatViewModel { + public int Id { get; set; } public string Comment { get; set; } public DateTime Date { get; set; } public string Username { get; set; } diff --git a/src/Ombi/Models/MobileNotificationTestViewModel.cs b/src/Ombi/Models/MobileNotificationTestViewModel.cs new file mode 100644 index 000000000..bba7a3a41 --- /dev/null +++ b/src/Ombi/Models/MobileNotificationTestViewModel.cs @@ -0,0 +1,10 @@ +using Ombi.Settings.Settings.Models.Notifications; + +namespace Ombi.Models +{ + public class MobileNotificationTestViewModel + { + public string UserId { get; set; } + public MobileNotificationSettings Settings { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/MobileUsersViewModel.cs b/src/Ombi/Models/MobileUsersViewModel.cs index e7d99569f..d10ed68bc 100644 --- a/src/Ombi/Models/MobileUsersViewModel.cs +++ b/src/Ombi/Models/MobileUsersViewModel.cs @@ -2,6 +2,7 @@ { public class MobileUsersViewModel { + public string UserId { get; set; } public string Username { get; set; } public int Devices { get; set; } } diff --git a/src/Ombi/Models/PlexOAuthViewModel.cs b/src/Ombi/Models/PlexOAuthViewModel.cs new file mode 100644 index 000000000..dfc2eda8f --- /dev/null +++ b/src/Ombi/Models/PlexOAuthViewModel.cs @@ -0,0 +1,10 @@ +using Ombi.Api.Plex.Models.OAuth; + +namespace Ombi.Models +{ + public class PlexOAuthViewModel + { + public bool Wizard { get; set; } + public OAuthPin Pin { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/UrlVerifyModel.cs b/src/Ombi/Models/UrlVerifyModel.cs new file mode 100644 index 000000000..01fdb0559 --- /dev/null +++ b/src/Ombi/Models/UrlVerifyModel.cs @@ -0,0 +1,7 @@ +namespace Ombi.Models +{ + public class UrlVerifyModel + { + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/UserAuthModel.cs b/src/Ombi/Models/UserAuthModel.cs index 5b3d69a29..5f240699b 100644 --- a/src/Ombi/Models/UserAuthModel.cs +++ b/src/Ombi/Models/UserAuthModel.cs @@ -1,4 +1,6 @@ -namespace Ombi.Models +using Ombi.Api.Plex.Models.OAuth; + +namespace Ombi.Models { public class UserAuthModel { @@ -6,5 +8,7 @@ public string Password { get; set; } public bool RememberMe { get; set; } public bool UsePlexAdminAccount { get; set; } + public bool UsePlexOAuth { get; set; } + public OAuthPin PlexTvPin { get; set; } } } \ No newline at end of file diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 3b7e7400e..7489ad77f 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,7 +1,6 @@ - - + - netcoreapp2.0 + netcoreapp2.1 win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; false Latest @@ -9,8 +8,11 @@ $(SemVer) $(FullVer) + 3.0 + + + false - bin\Debug\netcoreapp2.0\Swagger.xml 1701;1702;1705;1591; @@ -21,17 +23,13 @@ 1701;1702;1705;1591; TRACE;RELEASE;NETCOREAPP2_0 - - - - - + @@ -44,7 +42,17 @@ - + + + + + + + @@ -54,21 +62,19 @@ - - - - + + - - - - + + + + @@ -76,13 +82,14 @@ - + + @@ -92,18 +99,4 @@ - - - - - - - - - - - - - - diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 9ced1715e..7e9fa6f78 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -23,11 +23,13 @@ namespace Ombi var host = string.Empty; var storagePath = string.Empty; + var baseUrl = string.Empty; var result = Parser.Default.ParseArguments(args) .WithParsed(o => { host = o.Host; storagePath = o.StoragePath; + baseUrl = o.BaseUrl; }).WithNotParsed(err => { foreach (var e in err) @@ -47,6 +49,7 @@ namespace Ombi { var config = ctx.ApplicationConfigurations.ToList(); var url = config.FirstOrDefault(x => x.Type == ConfigurationTypes.Url); + var dbBaseUrl = config.FirstOrDefault(x => x.Type == ConfigurationTypes.BaseUrl); if (url == null) { url = new ApplicationConfiguration @@ -65,6 +68,25 @@ namespace Ombi ctx.SaveChanges(); urlValue = url.Value; } + + if (dbBaseUrl == null) + { + if (baseUrl.HasValue() && baseUrl.StartsWith("/")) + { + dbBaseUrl = new ApplicationConfiguration + { + Type = ConfigurationTypes.BaseUrl, + Value = baseUrl + }; + ctx.ApplicationConfigurations.Add(dbBaseUrl); + ctx.SaveChanges(); + } + } + else if(baseUrl.HasValue() && !baseUrl.Equals(dbBaseUrl.Value)) + { + dbBaseUrl.Value = baseUrl; + ctx.SaveChanges(); + } } DeleteSchedulesDb(); @@ -118,5 +140,8 @@ namespace Ombi [Option("storage", Required = false, HelpText = "Storage path, where we save the logs and database")] public string StoragePath { get; set; } + [Option("baseurl", Required = false, HelpText = "The base URL for reverse proxy scenarios")] + public string BaseUrl { get; set; } + } } diff --git a/src/Ombi/Properties/launchSettings.json b/src/Ombi/Properties/launchSettings.json index ec5deb319..19e5d23af 100644 --- a/src/Ombi/Properties/launchSettings.json +++ b/src/Ombi/Properties/launchSettings.json @@ -3,13 +3,14 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5000/", + "applicationUrl": "http://localhost:3577/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", + "commandLineArgs": "--host http://*:3577", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index a3cb7f4be..ffdd87b53 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -1,19 +1,12 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; -using System.Security.Principal; -using System.Threading.Tasks; using AutoMapper; using AutoMapper.EquivalencyExpression; using Hangfire; -using Hangfire.Console; using Hangfire.Dashboard; using Hangfire.SQLite; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SpaServices.Webpack; @@ -31,8 +24,8 @@ using Ombi.Schedule; using Ombi.Settings.Settings.Models; using Ombi.Store.Context; using Ombi.Store.Entities; +using Ombi.Store.Repository; using Serilog; -using Serilog.Events; namespace Ombi { @@ -85,7 +78,6 @@ namespace Ombi // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { - // Add framework services. services.AddDbContext(); @@ -132,9 +124,15 @@ namespace Ombi { x.UseSQLiteStorage(sqliteStorage); x.UseActivator(new IoCJobActivator(services.BuildServiceProvider())); - x.UseConsole(); + //x.UseConsole(); }); + services.AddCors(o => o.AddPolicy("MyPolicy", builder => + { + builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + })); // Build the intermediate service provider return services.BuildServiceProvider(); @@ -158,7 +156,12 @@ namespace Ombi app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { HotModuleReplacement = true, - ConfigFile = "webpack.dev.js" + ConfigFile = "webpack.config.ts", + + //EnvParam = new + //{ + // aot = true // can't use AOT with HMR currently https://github.com/angular/angular-cli/issues/6347 + //} }); } @@ -173,24 +176,40 @@ namespace Ombi userManager.CreateAsync(new OmbiUser {UserName = "API User", UserType = UserType.LocalUser}).Wait(); ombiService.SaveSettings(settings); } + + // Check if it's in the startup args + var appConfig = serviceProvider.GetService(); + var baseUrl = appConfig.Get(ConfigurationTypes.BaseUrl).Result; + if (baseUrl != null) + { + if (baseUrl.Value.HasValue()) + { + settings.BaseUrl = baseUrl.Value; + ombiService.SaveSettings(settings); + } + } if (settings.BaseUrl.HasValue()) { app.UsePathBase(settings.BaseUrl); } app.UseHangfireServer(new BackgroundJobServerOptions { WorkerCount = 1, ServerTimeout = TimeSpan.FromDays(1), ShutdownTimeout = TimeSpan.FromDays(1)}); - app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire", - new DashboardOptions - { - Authorization = new[] { new HangfireAuthorizationFilter() } - }); - GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 3 }); + if (env.IsDevelopment()) + { + app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire", + new DashboardOptions + { + Authorization = new[] {new HangfireAuthorizationFilter()} + }); + } + GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 3 }); + // Setup the scheduler var jobSetup = app.ApplicationServices.GetService(); jobSetup.Setup(); ctx.Seed(); - + var provider = new FileExtensionContentTypeProvider { Mappings = { [".map"] = "application/octet-stream" } }; app.UseStaticFiles(new StaticFileOptions() @@ -201,15 +220,18 @@ namespace Ombi app.UseAuthentication(); app.UseMiddleware(); + app.UseMiddleware(); - app.ApiKeyMiddlewear(serviceProvider); + app.UseCors("MyPolicy"); + //app.ApiKeyMiddlewear(app.ApplicationServices); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - c.ShowJsonEditor(); }); - + + + app.UseMvc(routes => { routes.MapRoute( @@ -220,6 +242,8 @@ namespace Ombi name: "spa-fallback", defaults: new { controller = "Home", action = "Index" }); }); + + ombiService.Dispose(); } } diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index e4dae18e4..d81227ff4 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -1,29 +1,38 @@ using System; +using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; -using System.Security.Principal; +using System.Reflection; using System.Text; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.PlatformAbstractions; -using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using Ombi.Config; -using Ombi.Core.Authentication; -using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Models.Identity; -using Ombi.Settings.Settings.Models; using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; namespace Ombi { + public class AddRequiredHeaderParameter : IOperationFilter + { + + public void Apply(Operation operation, OperationFilterContext context) + { + if (operation.Parameters == null) + operation.Parameters = new List(); + + operation.Parameters.Add(new NonBodyParameter + { + Name = "ApiKey", + In = "header", + Type = "apiKey", + + }); + } + } public static class StartupExtensions { public static void AddSwagger(this IServiceCollection services) @@ -35,17 +44,37 @@ namespace Ombi { Version = "v1", Title = "Ombi Api", - Description = "The API for Ombi, most of these calls require an auth token that you can get from calling POST:\"/api/v1/token\" with the body of: \n {\n\"username\":\"YOURUSERNAME\",\n\"password\":\"YOURPASSWORD\"\n} \n" + - "You can then use the returned token in the JWT Token field e.g. \"Bearer Token123xxff\"", Contact = new Contact { - Email = "tidusjar@gmail.com", Name = "Jamie Rees", Url = "https://www.ombi.io/" } }); + var security = new Dictionary> + { + //{"Bearer", new string[] { }}, + {"ApiKey", new string[] { }}, + }; + + //c.AddSecurityDefinition("Bearer", new ApiKeyScheme + //{ + // Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + // Name = "Authorization", + // In = "header", + // Type = "apiKey" + //}); + + c.AddSecurityDefinition("ApiKey", new ApiKeyScheme + { + Description = "API Key provided by Ombi. Example: \"ApiKey: {token}\"", + Name = "ApiKey", + In = "header", + Type = "apiKey" + }); + c.AddSecurityRequirement(security); c.CustomSchemaIds(x => x.FullName); - var basePath = PlatformServices.Default.Application.ApplicationBasePath; + c.OperationFilter(); + var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var xmlPath = Path.Combine(basePath, "Swagger.xml"); try { @@ -55,13 +84,7 @@ namespace Ombi { Console.WriteLine(e); } - c.AddSecurityDefinition("Bearer", new JwtBearer - { - Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", - Name = "Authorization", - In = "header", - Type = "apiKey", - }); + c.OperationFilter(); c.DescribeAllParametersInCamelCase(); @@ -114,90 +137,5 @@ namespace Ombi x.TokenValidationParameters = tokenValidationParameters; }); } - - - public static void ApiKeyMiddlewear(this IApplicationBuilder app, IServiceProvider serviceProvider) - { - app.Use(async (context, next) => - { - if (context.Request.Path.StartsWithSegments(new PathString("/api"))) - { - // Let's check if this is an API Call - if (context.Request.Headers["ApiKey"].Any()) - { - // validate the supplied API key - // Validate it - var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault(); - await ValidateApiKey(serviceProvider, context, next, headerKey); - } - else if (context.Request.Query.ContainsKey("apikey")) - { - if (context.Request.Query.TryGetValue("apikey", out var queryKey)) - { - await ValidateApiKey(serviceProvider, context, next, queryKey); - } - } - // User access token used by the mobile app - else if (context.Request.Headers["UserAccessToken"].Any()) - { - var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault(); - await ValidateUserAccessToken(serviceProvider, context, next, headerKey); - } - else - { - await next(); - } - } - else - { - await next(); - } - }); - } - - private static async Task ValidateUserAccessToken(IServiceProvider serviceProvider, HttpContext context, Func next, string key) - { - if (key.IsNullOrEmpty()) - { - await context.Response.WriteAsync("Invalid User Access Token"); - return; - } - - var um = serviceProvider.GetService(); - var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); - if (user == null) - { - context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; - await context.Response.WriteAsync("Invalid User Access Token"); - } - else - { - - var identity = new GenericIdentity(user.UserName); - var roles = await um.GetRolesAsync(user); - var principal = new GenericPrincipal(identity, roles.ToArray()); - context.User = principal; - await next(); - } - } - - private static async Task ValidateApiKey(IServiceProvider serviceProvider, HttpContext context, Func next, string key) - { - var settingsProvider = serviceProvider.GetService>(); - var ombiSettings = settingsProvider.GetSettings(); - var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase); - if (!valid) - { - context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; - await context.Response.WriteAsync("Invalid API Key"); - } - else - { - var identity = new GenericIdentity("API"); - var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); - context.User = principal; - await next(); - } - } } } \ No newline at end of file diff --git a/src/Ombi/Views/Shared/_Layout.cshtml b/src/Ombi/Views/Shared/_Layout.cshtml index cafd75357..85b8adfd7 100644 --- a/src/Ombi/Views/Shared/_Layout.cshtml +++ b/src/Ombi/Views/Shared/_Layout.cshtml @@ -17,56 +17,71 @@ } var appName = customization.ApplicationName; - if(string.IsNullOrEmpty(appName)) + if (string.IsNullOrEmpty(appName)) { appName = "Ombi"; } - + } - - + + + @* + + + + + + + + + + + e.com/store/apps/details?id=com.tidusjar.Ombi"> + + *@ + - + @appName - - - - + + + + @@ -75,26 +90,43 @@ O:::::::OOO:::::::Om::::m m::::m m::::mb:::::bbbbbb::::::bi::::::i + + + - - - - + + + + + + - @{ + @{ if (customization.HasPresetTheme) { + if (!string.IsNullOrEmpty(baseUrl)) + { + if (!customization.PresetThemeContent.Contains("/" + baseUrl)) + { + var index = customization.PresetThemeContent.IndexOf("/api/"); + if (index > 0) + { + customization.PresetThemeContent = customization.PresetThemeContent.Insert(index, "/" + baseUrl); + } + } + } + + } if (!string.IsNullOrEmpty(customization.CustomCssLink)) { - + } } diff --git a/src/Ombi/appsettings.json b/src/Ombi/appsettings.json index ed9a1b88a..4ee01911a 100644 --- a/src/Ombi/appsettings.json +++ b/src/Ombi/appsettings.json @@ -2,15 +2,14 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Debug", - "System": "Debug", + "Default": "Information", + "System": "Information", "Microsoft": "None", "Hangfire": "None" } }, "ApplicationSettings": { "Verison": "{{VERSIONNUMBER}}", - "OmbiService": "https://ombiservice.azurewebsites.net/", "Branch": "{{BRANCH}}", "FriendlyVersion": "v3.0.0" }, @@ -29,7 +28,13 @@ 155, 13, 1891, - 399106 + 399106, + 351286, + 348350, + 260513, + 372058, + 299536, + 383498 ], "TvShows": [ 121361, @@ -37,7 +42,10 @@ 81189, 79126, 79349, - 275274 + 275274, + 305288, + 296762, + 280619 ] } } diff --git a/src/Ombi/gulpfile.js b/src/Ombi/gulpfile.js index ae7472871..5723c21cc 100644 --- a/src/Ombi/gulpfile.js +++ b/src/Ombi/gulpfile.js @@ -1,21 +1,25 @@ -'use strict'; +"use strict"; -const gulp = require('gulp'); -const run = require('gulp-run'); -const runSequence = require('run-sequence'); -const del = require('del'); -const path = require('path'); -const fs = require('fs'); +const gulp = require("gulp"); +const run = require("gulp-run"); +const runSequence = require("run-sequence"); +const del = require("del"); +const path = require("path"); +const fs = require("fs"); -const outputDir = './wwwroot/dist'; +const outputDir = "./wwwroot/dist"; +global.aot = true; function getEnvOptions() { - var options = []; + const options = []; if (global.prod) { - options.push('--env.prod'); + options.push("--env.prod"); } if (global.analyse) { - options.push('--env.analyse'); + options.push("--env.analyse"); + } + if (global.aot) { + options.push("--env.aot"); } if (options.length > 0) { return " " + options.join(" "); @@ -24,12 +28,12 @@ function getEnvOptions() { } } - -function webpack(vendor) { - return run(`webpack --config webpack.config${vendor ? '.vendor' : ''}.ts${getEnvOptions()}`).exec(); +function webpack(type) { + // 'webpack' instead of direct path can cause https://github.com/angular/angular-cli/issues/6417 + return run(`node ${path.join('node_modules', 'webpack', 'bin', 'webpack.js')} --config webpack.config${type ? `.${type}` : ""}.ts${getEnvOptions()}`).exec(); } -gulp.task('vendor', () => { +gulp.task("vendor", () => { let build = false; const vendorPath = path.join(outputDir, "vendor.js"); const vendorExists = fs.existsSync(vendorPath); @@ -37,44 +41,47 @@ gulp.task('vendor', () => { const vendorStat = fs.statSync(vendorPath); const packageStat = fs.statSync("package.json"); const vendorConfigStat = fs.statSync("webpack.config.vendor.ts"); + const commonConfigStat = fs.statSync("webpack.config.common.ts"); if (packageStat.mtime > vendorStat.mtime) { build = true; } if (vendorConfigStat.mtime > vendorStat.mtime) { build = true; } + if (commonConfigStat.mtime > vendorStat.mtime) { + build = true; + } } else { build = true; } if (build) { - return webpack(true); + return webpack("vendor"); } }); - -gulp.task('vendor_force', () => { - return webpack(true); +gulp.task("vendor_force", () => { + return webpack("vendor"); }) -gulp.task('main', () => { +gulp.task("main", () => { return webpack() }); -gulp.task('prod_var', () => { +gulp.task("prod_var", () => { global.prod = true; }) -gulp.task('analyse_var', () => { +gulp.task("analyse_var", () => { global.analyse = true; }) -gulp.task('clean', () => { +gulp.task("clean", () => { del.sync(outputDir, { force: true }); }); - -gulp.task('lint', () => run("npm run lint").exec()); -gulp.task('build', callback => runSequence('vendor', 'main', callback)); -gulp.task('analyse', callback => runSequence('analyse_var', 'build')); -gulp.task('full', callback => runSequence('clean', 'build')); -gulp.task('publish', callback => runSequence('prod_var', 'build')); \ No newline at end of file +gulp.task("lint", () => run("npm run lint").exec()); +gulp.task("lint_fix", () => run("npm run lint -- --fix").exec()); +gulp.task("build", callback => runSequence("vendor", "main", callback)); +gulp.task("analyse", callback => runSequence("analyse_var", "clean", "build", callback)); +gulp.task("full", callback => runSequence("clean", "build", callback)); +gulp.task("publish", callback => runSequence("prod_var", "full", callback)); \ No newline at end of file diff --git a/src/Ombi/package-lock.json b/src/Ombi/package-lock.json deleted file mode 100644 index e3ef65167..000000000 --- a/src/Ombi/package-lock.json +++ /dev/null @@ -1,13398 +0,0 @@ -{ - "name": "ombi", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular/animations": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.2.5.tgz", - "integrity": "sha512-70ElCmaeDxLQc2OkgYhJjXj4zjtdjI4K1D5ZZm/uSPLlUcqC6uf6skCXlhMawQoPbsL/SXE5xw2HlMgEbhUysw==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/cdk": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.2.1.tgz", - "integrity": "sha512-8vsHeRymM+p82JeBzanrjmxp0koTU5W8cXO05ojECRsj6gUE/C950rMfFDga7fC8Pu5KTru/hWQoOcKErb3Uzg==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/common": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.5.tgz", - "integrity": "sha512-jagCxo+75pcTwjuO1ZheIiTlKBJ6REFKFWoUPTzaSS6fnzReFJ+VPf4Pb0bWtHL1lWvbvnzmITOJPB9wmuM3fg==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/compiler": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.5.tgz", - "integrity": "sha512-YU/r5omexkrrBF3bZaseWrc2Iotk6hIdUWkPIL3gPC0hKJ3wBeB3sHCBujPQXktWdMBbQRujNSMZtgra3Oh1xQ==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/core": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.5.tgz", - "integrity": "sha512-Uo7R3LrsvA24JkRbwXWUZWp7NSEpwdTUxT1NScyjrBK+t8ybSL5/42Jo21md5M4pjeCsIgUXlGoCm1QtT5aYnQ==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/forms": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.5.tgz", - "integrity": "sha512-3feqqTuv9rIu7ZOsLCtM/ugNFz5RPujLHkE8bU1gsMM4/eMYruIFir2vbjnhMkD3K6KptEg4iO6tDW18diwXug==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/http": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.5.tgz", - "integrity": "sha512-VqTCkAnebe+M9Bqrfp1QYpBQCTbXide/NxrQfwiJY87kjKFeRBuy9/XH/2S5wIwlF5Yx3bmlaIufd9VI5r/0aQ==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/material": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.2.1.tgz", - "integrity": "sha512-94VmxclpIwXAxeudz9AfMg0m46/TEx/GsDZ7R9yOtrbptAr9xSgOumiEqET4Xjb35/mzgD/PKqlcMWyHJCkyVQ==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/platform-browser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.5.tgz", - "integrity": "sha512-iPAuoG/c3pD3hnk1g0VgJu/pzNITvLQyT0W71MDMSuxLxs291kq+U2jklm40pStISd1mPbCNKmvz/7M+WbdLhg==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.5.tgz", - "integrity": "sha512-IMEe2qUTC3CA3KoswmJJs+O2Lkyd5GXgl5ULupqhhm/TOL2FLk00kwv8k3Epaf2d1wXcjK3BMG7aAwc6RLH7QA==", - "requires": { - "tslib": "1.8.0" - } - }, - "@angular/platform-server": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-5.2.5.tgz", - "integrity": "sha512-IpuEDNyoVfGO94jd1s+4IgoTBkWigwqD4YQTpcsC1mdY2Ax7NXXTAx28ZQF5EvPbSxsHGB5zG3oR7KE7GMNhYQ==", - "requires": { - "domino": "1.0.30", - "tslib": "1.8.0", - "xhr2": "0.1.4" - } - }, - "@angular/router": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.5.tgz", - "integrity": "sha512-I8U0iy59lz0dAxU4zxRQHagfUPWF+MikLNMirRL1lrA49PG+5K1tiuIQ6p+8fZFAJ5UXwNHyXqYuWqsKRiVBHQ==", - "requires": { - "tslib": "1.8.0" - } - }, - "@auth0/angular-jwt": { - "version": "1.0.0-beta.9", - "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-1.0.0-beta.9.tgz", - "integrity": "sha1-ZQIsNJ7ck97DMS+TO5VccZ6GKmI=" - }, - "@ng-bootstrap/ng-bootstrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0.tgz", - "integrity": "sha1-jyrnDbL+Hcv14Ky0ncKxu7or6NI=" - }, - "@ngu/carousel": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@ngu/carousel/-/carousel-1.4.8.tgz", - "integrity": "sha512-3Eq8gawkveOaSOyNLpRTuSFBBWae1mx93/9qxdmgkAjBeDW5K62rCH1VdbZ7YMTWfLctXAldLrBUBCi3b29Upg==" - }, - "@ngx-translate/core": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-8.0.0.tgz", - "integrity": "sha1-dR/WtRLYDzp0jS3o38lt/vopr+A=" - }, - "@ngx-translate/http-loader": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-2.0.1.tgz", - "integrity": "sha1-qmd4jmS/qGUmkad7Ais7QDEgkRM=" - }, - "@types/chai": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.4.tgz", - "integrity": "sha1-/oYxXZpmgn/usW9zvJVGiOyVDhg=", - "dev": true - }, - "@types/core-js": { - "version": "0.9.46", - "resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-0.9.46.tgz", - "integrity": "sha512-LooLR6XHes9V+kNYRz1Qm8w3atw9QMn7XeZUmIpUelllF9BdryeUKd/u0Wh5ErcjpWfG39NrToU9MF7ngsTFVw==" - }, - "@types/extract-text-webpack-plugin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.1.tgz", - "integrity": "sha512-26bBfpIM6emIr0iJwO+2nBypfaybfBlGEpDf/ik7X9Xo05uFGCEPQS+kpYv801GgmCXbovDqS4+iV7P39VhEFQ==", - "requires": { - "@types/webpack": "3.8.7" - } - }, - "@types/intro.js": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/intro.js/-/intro.js-2.4.3.tgz", - "integrity": "sha1-2r3sCWBHMuJaMhoAQN2Ow0GQ3/Y=" - }, - "@types/jasmine": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.6.2.tgz", - "integrity": "sha1-bm1MsYPNVcehrWJwvO0Q/dU2ejw=", - "dev": true - }, - "@types/node": { - "version": "8.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.4.tgz", - "integrity": "sha512-dSvD36qnQs78G1BPsrZFdPpvLgMW/dnvr5+nTW2csMs5TiP9MOXrjUbnMZOEwnIuBklXtn7b6TPA2Cuq07bDHA==" - }, - "@types/tapable": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-0.2.4.tgz", - "integrity": "sha512-pclMAvhPnXJcJu1ZZ8bQthuUcdDWzDuxDdbSf6l1U6s4fP6EBiZpPsOZYqFOrbqDV97sXGFSsb6AUpiLfv4xIA==" - }, - "@types/uglify-js": { - "version": "2.6.30", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-2.6.30.tgz", - "integrity": "sha512-NjiBNGFl58vHJeijl63w1fWRIjLnrfOvimsXF5b3lTzEzkTV1BnVsbqQeLejg54upsHPWIF63aiub5TEwH619A==", - "requires": { - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "@types/webpack": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-3.8.7.tgz", - "integrity": "sha512-DbGvtpa6TNuHUxVzIIFs3BPsu2nD59frxrcb2RN8Ebo9kil4zvgTj62YQaD4AmayBEoK1hQeVw5ua/Pnwg06wQ==", - "requires": { - "@types/node": "8.9.4", - "@types/tapable": "0.2.4", - "@types/uglify-js": "2.6.30", - "source-map": "0.7.1" - }, - "dependencies": { - "source-map": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.1.tgz", - "integrity": "sha512-c+mPk+5wTnOd1y9oyVp0EQIo3N8k8gdxzxOo7jq1ApEN2+ew5c/UV+F1wtNHeS6Aa4JPsxiq5OU+95aMpRTGJA==" - } - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" - }, - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", - "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==" - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "requires": { - "acorn": "4.0.13" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "ajv": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.4.tgz", - "integrity": "sha1-Pa+ai2ciEpn9ro2C0RftjmyAJEs=", - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" - } - }, - "ajv-keywords": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", - "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=" - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "angular-router-loader": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/angular-router-loader/-/angular-router-loader-0.8.2.tgz", - "integrity": "sha512-0XoVLYTwE3G692f5cd+bRCL/P8tACsmDRFrQ9h8H9qOE0OMx+jq+oLPTbPrM4dbvr9wueJxiTs7hvXoYNxRUQA==", - "requires": { - "loader-utils": "1.1.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "angular2-moment": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/angular2-moment/-/angular2-moment-1.8.0.tgz", - "integrity": "sha512-cBppm4FM9sOtZr4eSnodjbIhvn3bZzuw1VIb3VN53/aWKQVrNosQVG7l0dnX5ifdy08i9Yd/5eGYOXw+Lb63YA==", - "requires": { - "moment": "2.20.1" - } - }, - "angular2-template-loader": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz", - "integrity": "sha1-wNROkP/w+sleiyPwQ6zaf9HFHXw=", - "requires": { - "loader-utils": "0.2.17" - } - }, - "ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" - }, - "ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - } - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-slice": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.0.0.tgz", - "integrity": "sha1-5zA08A3MH0CHYAj9IP6ud71LfC8=" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "aspnet-webpack": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-2.0.3.tgz", - "integrity": "sha512-TwutLcYQlLikMJ8mcv8D9KSr5BkkFbpQDY/LABj/dMwz6EAM9azqV9WfgrQ+1UPmycS6VVgQZhITmCmTOAiYtg==", - "requires": { - "connect": "3.6.5", - "es6-promise": "3.3.1", - "memory-fs": "0.3.0", - "require-from-string": "1.2.1", - "webpack-dev-middleware": "1.12.0", - "webpack-node-externals": "1.6.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "requires": { - "util": "0.10.3" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" - }, - "assertion-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" - }, - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "requires": { - "lodash": "4.17.4" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000808", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "awesome-typescript-loader": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.4.1.tgz", - "integrity": "sha512-fYxBtN6s4Dm6vtsROWi8IQ4I+KcmwRWePAVvhI06mFcHbtHfZopOs4qGNu9LyCPEw403LDROKFA+NVV6ig5yNw==", - "requires": { - "colors": "1.1.2", - "enhanced-resolve": "3.3.0", - "loader-utils": "1.1.0", - "lodash": "4.17.4", - "micromatch": "3.1.4", - "mkdirp": "0.5.1", - "object-assign": "4.1.1", - "source-map-support": "0.4.18" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.5", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.0", - "pascalcase": "0.1.1" - } - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=" - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "bfj-node4": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.2.1.tgz", - "integrity": "sha512-w+OTPD/R0AvDVR/sy/uVUVeoCpEgUoYj9/1P2zB6mR1yx7F/ADzLX4nlvZ/91WWzGgdZnuLxWP/J89D7ZDt0DA==", - "requires": { - "bluebird": "3.5.1", - "check-types": "7.3.0", - "tryer": "1.0.0" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=" - }, - "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=" - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "requires": { - "inherits": "2.0.3" - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=" - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=" - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.16.3" - } - }, - "bootstrap": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz", - "integrity": "sha1-WjiTlFSfIzMIdaOxUGVldPip63E=" - }, - "bootswatch": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-3.3.7.tgz", - "integrity": "sha1-62+amoUjuHpwbqkd7sPg1+qoqx8=" - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - } - } - }, - "braces": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", - "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.1", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browserify-aes": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha1-OLerVe24Bv8tzaGn8WIHc6R3xJ8=", - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "requires": { - "browserify-aes": "1.1.1", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", - "requires": { - "pako": "1.0.6" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "requires": { - "caniuse-db": "1.0.30000808", - "electron-to-chromium": "1.3.33" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "cacache": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.2.tgz", - "integrity": "sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ==", - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.1", - "mississippi": "1.3.1", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.2.2", - "unique-filename": "1.1.0", - "y18n": "3.2.1" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" - } - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - } - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000808", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" - } - }, - "caniuse-db": { - "version": "1.0.30000808", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000808.tgz", - "integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs=" - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - }, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - } - } - }, - "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", - "dev": true, - "requires": { - "assertion-error": "1.0.2", - "check-error": "1.0.2", - "deep-eql": "3.0.1", - "get-func-name": "2.0.0", - "pathval": "1.1.0", - "type-detect": "4.0.3" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "check-types": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.3.0.tgz", - "integrity": "sha1-Ro9XGkQ1wkJI9f0MsOjYfDw0Hn0=" - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.3", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", - "requires": { - "chalk": "1.1.3" - } - }, - "class-utils": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.5.tgz", - "integrity": "sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA=", - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" - } - } - }, - "clean-css": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", - "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", - "requires": { - "source-map": "0.5.7" - } - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", - "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=" - }, - "clone-deep": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz", - "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=", - "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "3.2.2", - "shallow-clone": "0.1.2" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "requires": { - "q": "1.5.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "requires": { - "clone": "1.0.2", - "color-convert": "1.9.0", - "color-string": "0.3.0" - } - }, - "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "requires": { - "color-name": "1.1.3" - } - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "requires": { - "color": "0.11.4", - "css-color-names": "0.0.4", - "has": "1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" - }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - } - }, - "connect": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", - "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", - "requires": { - "debug": "2.6.9", - "finalhandler": "1.0.6", - "parseurl": "1.3.2", - "utils-merge": "1.0.1" - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "requires": { - "date-now": "0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", - "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "core-js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.10" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "requires": { - "lru-cache": "4.1.1", - "which": "1.3.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - } - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.10.1" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.6", - "randomfill": "1.0.3" - } - }, - "css": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz", - "integrity": "sha1-c6TIHehdtmTU7mdPfUcIXjstVdw=", - "requires": { - "inherits": "2.0.3", - "source-map": "0.1.43", - "source-map-resolve": "0.3.1", - "urix": "0.1.0" - }, - "dependencies": { - "atob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", - "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=" - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "requires": { - "amdefine": "1.0.1" - } - }, - "source-map-resolve": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", - "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=", - "requires": { - "atob": "1.1.3", - "resolve-url": "0.2.1", - "source-map-url": "0.3.0", - "urix": "0.1.0" - } - }, - "source-map-url": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", - "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=" - } - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" - }, - "css-loader": { - "version": "0.28.9", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.9.tgz", - "integrity": "sha512-r3dgelMm/mkPz5Y7m9SeiGE46i2VsEU/OYbez+1llfxtv8b2y5/b5StaeEvPK3S5tlNQI+tDW/xDIhKJoZgDtw==", - "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "icss-utils": "2.1.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.2.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "postcss-value-parser": "3.3.0", - "source-list-map": "2.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=" - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "requires": { - "array-find-index": "1.0.2" - } - }, - "custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=", - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "requires": { - "es5-ext": "0.10.38" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" - }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", - "dev": true, - "requires": { - "type-detect": "4.0.3" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "requires": { - "clone": "1.0.2" - } - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "1.0.2" - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "requires": { - "globby": "6.1.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "p-map": "1.2.0", - "pify": "3.0.0", - "rimraf": "2.6.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "deprecated": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", - "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=" - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-file": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", - "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", - "requires": { - "fs-exists-sync": "0.1.0" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", - "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==" - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "1.0.0", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" - }, - "domino": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", - "integrity": "sha1-VKQVTsrpaGFmgPj+ujzt/zVccfQ=" - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" - }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "requires": { - "readable-stream": "1.1.14" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "duplexify": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.3.tgz", - "integrity": "sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==", - "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "stream-shift": "1.0.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "1.4.0" - } - } - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" - }, - "electron-to-chromium": { - "version": "1.3.33", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz", - "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU=" - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" - }, - "end-of-stream": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", - "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", - "requires": { - "once": "1.3.3" - }, - "dependencies": { - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", - "requires": { - "wrappy": "1.0.2" - } - } - } - }, - "engine.io": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", - "dev": true, - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "ws": "1.1.2" - }, - "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - } - } - }, - "engine.io-client": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parsejson": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "1.1.2", - "xmlhttprequest-ssl": "1.5.3", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - } - } - }, - "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" - } - }, - "enhanced-resolve": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.3.0.tgz", - "integrity": "sha1-lQlk7MfwMypCMhtnOzjcj/FVNbM=", - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" - }, - "dependencies": { - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - } - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "requires": { - "prr": "0.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es5-ext": { - "version": "0.10.38", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.38.tgz", - "integrity": "sha512-jCMyePo7AXbUESwbl8Qi01VSH2piY9s/a3rSU/5w/MlTIx8HPL1xn2InGN8ejt/xulcJgnTO7vqNtOAxzYd2Kg==", - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38" - } - }, - "es6-templates": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", - "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", - "requires": { - "recast": "0.11.23", - "through": "2.3.8" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38" - } - }, - "event-source-polyfill": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-0.0.11.tgz", - "integrity": "sha1-RXdpZqUERmMWrUc4SMYq9L11Gtg=" - }, - "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - } - } - }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" - }, - "dependencies": { - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "0.1.1" - } - }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" - } - }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "2.2.3" - }, - "dependencies": { - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "3.2.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", - "requires": { - "os-homedir": "1.0.2" - } - }, - "expose-loader": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-0.7.4.tgz", - "integrity": "sha512-lweINkewAXcQtNjd7j1gO3cd8O/8lNYijsEwH4YZ+Dv3gT2Kh9/YvJov5Mdp2A75QIhgOvsSyRa/ZG3wYjNZpA==" - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "requires": { - "accepts": "1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.1", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", - "utils-merge": "1.0.1", - "vary": "1.1.2" - }, - "dependencies": { - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - }, - "extglob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.3.tgz", - "integrity": "sha512-AyptZexgu7qppEPq59DtN/XJGZDrLcVxSHai+4hdgMMS9EpF4GBvygcWWApno8lL9qSjVpYt7Raao28qzJX1ww==", - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "extract-text-webpack-plugin": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", - "integrity": "sha1-XwQ+qgL5dQqSWLeMCm4NwUCPsvc=", - "requires": { - "async": "2.5.0", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0", - "webpack-sources": "1.0.1" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", - "requires": { - "chalk": "1.1.3", - "time-stamp": "1.1.0" - }, - "dependencies": { - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" - } - } - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" - }, - "file-loader": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.6.tgz", - "integrity": "sha512-873ztuL+/hfvXbLDJ262PGO6XjERnybJu2gW1/5j8HUfxSiFJI9Hj/DhZ50ZGRUxBvuNiazb/cM2rh9pqrxP6Q==", - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" - }, - "filesize": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.0.tgz", - "integrity": "sha512-g5OWtoZWcPI56js1DFhIEqyG9tnu/7sG3foHwgS9KGYFMfsYguI3E+PRVCmtmE96VajQIEMRU2OhN+ME589Gdw==" - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - } - }, - "finalhandler": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", - "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "requires": { - "commondir": "1.0.1", - "make-dir": "1.1.0", - "pkg-dir": "2.0.0" - } - }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=" - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "findup-sync": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", - "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", - "requires": { - "detect-file": "0.1.0", - "is-glob": "2.0.1", - "micromatch": "2.3.11", - "resolve-dir": "0.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - } - } - }, - "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", - "requires": { - "expand-tilde": "2.0.2", - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0", - "object.pick": "1.3.0", - "parse-filepath": "1.0.1" - }, - "dependencies": { - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "requires": { - "homedir-polyfill": "1.0.1" - } - } - } - }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" - }, - "flagged-respawn": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", - "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=" - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=" - }, - "flush-write-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", - "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "requires": { - "for-in": "1.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "1.0.0" - } - }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "optional": true, - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "gaze": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", - "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", - "requires": { - "globule": "0.1.0" - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "2.0.1" - } - }, - "glob-stream": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", - "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", - "requires": { - "glob": "4.5.3", - "glob2base": "0.0.12", - "minimatch": "2.0.10", - "ordered-read-streams": "0.1.0", - "through2": "0.6.5", - "unique-stream": "1.0.0" - }, - "dependencies": { - "glob": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", - "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "2.0.10", - "once": "1.4.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "requires": { - "brace-expansion": "1.1.8" - } - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" - } - } - } - }, - "glob-watcher": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", - "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", - "requires": { - "gaze": "0.5.2" - } - }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", - "requires": { - "find-index": "0.1.1" - } - }, - "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", - "requires": { - "global-prefix": "0.1.5", - "is-windows": "0.2.0" - } - }, - "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", - "requires": { - "homedir-polyfill": "1.0.1", - "ini": "1.3.4", - "is-windows": "0.2.0", - "which": "1.3.0" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "globule": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", - "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", - "requires": { - "glob": "3.1.21", - "lodash": "1.0.2", - "minimatch": "0.2.14" - }, - "dependencies": { - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "requires": { - "graceful-fs": "1.2.3", - "inherits": "1.0.2", - "minimatch": "0.2.14" - } - }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" - }, - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" - }, - "lodash": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", - "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - } - } - }, - "glogg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", - "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", - "requires": { - "sparkles": "1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "gulp": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", - "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", - "requires": { - "archy": "1.0.0", - "chalk": "1.1.3", - "deprecated": "0.0.1", - "gulp-util": "3.0.8", - "interpret": "1.0.4", - "liftoff": "2.3.0", - "minimist": "1.2.0", - "orchestrator": "0.3.8", - "pretty-hrtime": "1.0.3", - "semver": "4.3.6", - "tildify": "1.2.0", - "v8flags": "2.1.1", - "vinyl-fs": "0.3.14" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "gulp-run": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/gulp-run/-/gulp-run-1.7.1.tgz", - "integrity": "sha1-4XwKy3wwtuKu7iPAREKpbAys7/o=", - "requires": { - "gulp-util": "3.0.8", - "lodash.defaults": "4.2.0", - "lodash.template": "4.4.0", - "vinyl": "0.4.6" - }, - "dependencies": { - "clone": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", - "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.templatesettings": "4.1.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "requires": { - "lodash._reinterpolate": "3.0.0" - } - }, - "vinyl": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", - "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", - "requires": { - "clone": "0.2.0", - "clone-stats": "0.0.1" - } - } - } - }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "requires": { - "array-differ": "1.0.0", - "array-uniq": "1.0.3", - "beeper": "1.1.1", - "chalk": "1.1.3", - "dateformat": "2.2.0", - "fancy-log": "1.3.0", - "gulplog": "1.0.0", - "has-gulplog": "0.1.0", - "lodash._reescape": "3.0.0", - "lodash._reevaluate": "3.0.0", - "lodash._reinterpolate": "3.0.0", - "lodash.template": "3.6.2", - "minimist": "1.2.0", - "multipipe": "0.1.2", - "object-assign": "3.0.0", - "replace-ext": "0.0.1", - "through2": "2.0.3", - "vinyl": "0.5.3" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" - } - } - }, - "gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", - "requires": { - "glogg": "1.0.0" - } - }, - "gzip-size": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", - "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=", - "requires": { - "duplexer": "0.1.1", - "pify": "3.0.0" - } - }, - "hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "2.0.1" - } - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, - "has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", - "requires": { - "sparkles": "1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha1-NA3tvmKQGHFRweodd3o0SJNd+EY=", - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "requires": { - "parse-passwd": "1.0.0" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=" - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" - }, - "html-loader": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.1.tgz", - "integrity": "sha1-Tx6DlqHqarQr7cmH36wFgHCGHr4=", - "requires": { - "es6-templates": "0.2.3", - "fastparse": "1.1.1", - "html-minifier": "3.5.6", - "loader-utils": "1.1.0", - "object-assign": "4.1.1" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "html-minifier": { - "version": "3.5.6", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.6.tgz", - "integrity": "sha512-88FjtKrlak2XjczhxrBomgzV4jmGzM3UnHRBScRkJcmcRum0kb+IwhVAETJ8AVp7j0p3xugjSaw9L+RmI5/QOA==", - "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.9", - "commander": "2.11.0", - "he": "1.1.1", - "ncname": "1.0.0", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.1.5" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "uglify-js": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.5.tgz", - "integrity": "sha512-tSqlO7/GZHAVSw6mbtJt2kz0ZcUrKUH7Xg92o52aE+gL0r6cXiASZY4dpHqQ7RVGXmoQuPA2qAkG4TkP59f8XA==", - "requires": { - "commander": "2.11.0", - "source-map": "0.6.1" - } - } - } - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - }, - "dependencies": { - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } - } - }, - "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", - "dev": true, - "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "requires": { - "postcss": "6.0.17" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "postcss": { - "version": "6.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", - "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "requires": { - "repeating": "2.0.1" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" - }, - "interpret": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", - "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=" - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" - }, - "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", - "requires": { - "is-relative": "0.2.1", - "is-windows": "0.2.0" - } - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", - "requires": { - "kind-of": "6.0.2" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "1.10.0" - } - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-odd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", - "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", - "requires": { - "is-number": "3.0.0" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "requires": { - "is-path-inside": "1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", - "requires": { - "isobject": "3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, - "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", - "requires": { - "is-unc-path": "0.1.2" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "requires": { - "html-comment-regex": "1.1.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", - "requires": { - "unc-path-regex": "0.1.2" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - }, - "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" - }, - "js-base64": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", - "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==" - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "karma": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha1-hcwI6eCiLXzpzKN8ShvoJPaisa4=", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "body-parser": "1.18.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.5", - "core-js": "2.5.3", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", - "isbinaryfile": "3.0.2", - "lodash": "3.10.1", - "log4js": "0.6.38", - "mime": "1.4.1", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.1.5", - "range-parser": "1.2.0", - "rimraf": "2.6.2", - "safe-buffer": "5.1.1", - "socket.io": "1.7.3", - "source-map": "0.5.7", - "tmp": "0.0.31", - "useragent": "2.2.1" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } - } - }, - "karma-chai": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", - "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", - "dev": true - }, - "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=", - "dev": true, - "requires": { - "fs-access": "1.0.1", - "which": "1.3.0" - } - }, - "karma-cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", - "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", - "dev": true, - "requires": { - "resolve": "1.4.0" - } - }, - "karma-jasmine": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz", - "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=", - "dev": true - }, - "karma-webpack": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-2.0.5.tgz", - "integrity": "sha1-T1aIfjLPT5WDORwjiEFd4Grwbv0=", - "dev": true, - "requires": { - "async": "0.9.2", - "loader-utils": "0.2.17", - "lodash": "3.10.1", - "source-map": "0.1.43", - "webpack-dev-middleware": "1.12.0" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=" - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "requires": { - "set-getter": "0.1.0" - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "1.0.0" - } - }, - "liftoff": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", - "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", - "requires": { - "extend": "3.0.1", - "findup-sync": "0.4.3", - "fined": "1.1.0", - "flagged-respawn": "0.3.2", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.mapvalues": "4.6.0", - "rechoir": "0.6.2", - "resolve": "1.4.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "0.2.1" - } - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=" - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" - }, - "lodash._basetostring": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=" - }, - "lodash._basevalues": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=" - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" - }, - "lodash._reescape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=" - }, - "lodash._reevaluate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", - "requires": { - "lodash._root": "3.0.1" - } - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "lodash.mergewith": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=" - }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=" - }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "requires": { - "lodash._basecopy": "3.0.1", - "lodash._basetostring": "3.0.1", - "lodash._basevalues": "3.0.0", - "lodash._isiterateecall": "3.0.9", - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0", - "lodash.keys": "3.1.2", - "lodash.restparam": "3.6.1", - "lodash.templatesettings": "3.1.1" - } - }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", - "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "log4js": { - "version": "0.6.38", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", - "dev": true, - "requires": { - "readable-stream": "1.0.34", - "semver": "4.3.6" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=" - }, - "make-dir": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", - "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", - "requires": { - "pify": "3.0.0" - } - }, - "make-error": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", - "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=" - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "1.0.1" - } - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=" - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "requires": { - "mimic-fn": "1.2.0" - } - }, - "memory-fs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", - "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.4.tgz", - "integrity": "sha512-kFRtviKYoAJT+t7HggMl0tBFGNAKLw/S7N+CO9qfEQyisob1Oy4pao+geRbkyeEd+V9aOkvZ4mhuyPvI/q9Sfg==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.0", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "extglob": "2.0.3", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.6", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mississippi": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.1.tgz", - "integrity": "sha512-/6rB8YXFbAtsUVRphIRQqB0+9c7VaPHCjVtvto+JqwVxgz8Zz+I+f68/JgQ+Pb4VlZb2svA9OtdXnHHsZz7ltg==", - "requires": { - "concat-stream": "1.6.0", - "duplexify": "3.5.3", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.2", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "1.0.3", - "pumpify": "1.4.0", - "stream-each": "1.2.2", - "through2": "2.0.3" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "1.4.0" - } - } - } - }, - "mixin-deep": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", - "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", - "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "moment": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", - "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", - "requires": { - "duplexer2": "0.0.2" - } - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" - }, - "nanomatch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.6.tgz", - "integrity": "sha512-WJ6XTCbvWXUFPbi/bDwKcYkCeOGUHzaJj72KbuPqGn78Ba/F5Vu26Zlo6SuMQbCIst1RGKL1zfWBCOGAlbRLAg==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "is-odd": "1.0.0", - "kind-of": "5.1.0", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "natives": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.0.tgz", - "integrity": "sha1-6f+EFBimsux6SV6TmYT3jxY+bjE=" - }, - "ncname": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", - "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", - "requires": { - "xml-char-classes": "1.0.0" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "ng2-cookies": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ng2-cookies/-/ng2-cookies-1.0.12.tgz", - "integrity": "sha1-Pz5hPgE3sGSbcFxngHS0vQgUnMw=" - }, - "ngx-clipboard": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/ngx-clipboard/-/ngx-clipboard-8.1.1.tgz", - "integrity": "sha1-V0OQpt1IqhpYNQoUsaDXZCBElfU=", - "requires": { - "ngx-window-token": "0.0.4" - } - }, - "ngx-infinite-scroll": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-0.6.1.tgz", - "integrity": "sha1-/U5QVjQ1ANry5Xr4Alj/PQDNPJw=" - }, - "ngx-order-pipe": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ngx-order-pipe/-/ngx-order-pipe-2.0.1.tgz", - "integrity": "sha1-+NXHCZnfiDMkNvMW94z0pQ5MK6Q=" - }, - "ngx-window-token": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/ngx-window-token/-/ngx-window-token-0.0.4.tgz", - "integrity": "sha1-R+eqpGVBHEq197oXYBvFk8lWxzY=" - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", - "requires": { - "lower-case": "1.1.4" - } - }, - "node-gyp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", - "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", - "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.4", - "request": "2.79.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" - } - } - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=", - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.8.0", - "string_decoder": "1.0.3", - "timers-browserify": "2.0.6", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - } - }, - "node-sass": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz", - "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==", - "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.2", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.0", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.8.0", - "node-gyp": "3.6.2", - "npmlog": "4.1.2", - "request": "2.79.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" - }, - "dependencies": { - "gaze": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", - "requires": { - "globule": "1.2.0" - } - }, - "globule": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", - "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", - "requires": { - "glob": "7.1.2", - "lodash": "4.17.4", - "minimatch": "3.0.4" - } - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "requires": { - "abbrev": "1.1.1" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "4.3.6", - "validate-npm-package-license": "3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "npm": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-5.6.0.tgz", - "integrity": "sha512-mt839mCsI5hzdBJLf1iRBwt610P35iUfvqLVuL7VFdanUwRBAmGtbsjdGIuzegplR95xx+fTHE0vBMuMJp1sLQ==", - "requires": { - "JSONStream": "1.3.1", - "abbrev": "1.1.1", - "ansi-regex": "3.0.0", - "ansicolors": "0.3.2", - "ansistyles": "0.1.3", - "aproba": "1.2.0", - "archy": "1.0.0", - "bin-links": "1.1.0", - "bluebird": "3.5.1", - "cacache": "10.0.1", - "call-limit": "1.1.0", - "chownr": "1.0.1", - "cli-table2": "0.2.0", - "cmd-shim": "2.0.2", - "columnify": "1.5.4", - "config-chain": "1.1.11", - "debuglog": "1.0.1", - "detect-indent": "5.0.0", - "dezalgo": "1.0.3", - "editor": "1.0.0", - "find-npm-prefix": "1.0.1", - "fs-vacuum": "1.2.10", - "fs-write-stream-atomic": "1.0.10", - "gentle-fs": "2.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "has-unicode": "2.0.1", - "hosted-git-info": "2.5.0", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "inflight": "1.0.6", - "inherits": "2.0.3", - "ini": "1.3.4", - "init-package-json": "1.10.1", - "is-cidr": "1.0.0", - "lazy-property": "1.0.0", - "libnpx": "9.7.1", - "lockfile": "1.0.3", - "lodash._baseindexof": "3.1.0", - "lodash._baseuniq": "4.6.0", - "lodash._bindcallback": "3.0.1", - "lodash._cacheindexof": "3.0.2", - "lodash._createcache": "3.1.2", - "lodash._getnative": "3.9.1", - "lodash.clonedeep": "4.5.0", - "lodash.restparam": "3.6.1", - "lodash.union": "4.6.0", - "lodash.uniq": "4.5.0", - "lodash.without": "4.4.0", - "lru-cache": "4.1.1", - "meant": "1.0.1", - "mississippi": "1.3.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "nopt": "4.0.1", - "normalize-package-data": "2.4.0", - "npm-cache-filename": "1.0.2", - "npm-install-checks": "3.0.0", - "npm-lifecycle": "2.0.0", - "npm-package-arg": "6.0.0", - "npm-packlist": "1.1.10", - "npm-profile": "2.0.5", - "npm-registry-client": "8.5.0", - "npm-user-validate": "1.0.0", - "npmlog": "4.1.2", - "once": "1.4.0", - "opener": "1.4.3", - "osenv": "0.1.4", - "pacote": "7.0.2", - "path-is-inside": "1.0.2", - "promise-inflight": "1.0.1", - "qrcode-terminal": "0.11.0", - "query-string": "5.0.1", - "qw": "1.0.1", - "read": "1.0.7", - "read-cmd-shim": "1.0.1", - "read-installed": "4.0.3", - "read-package-json": "2.0.12", - "read-package-tree": "5.1.6", - "readable-stream": "2.3.3", - "readdir-scoped-modules": "1.0.2", - "request": "2.83.0", - "retry": "0.10.1", - "rimraf": "2.6.2", - "safe-buffer": "5.1.1", - "semver": "5.4.1", - "sha": "2.0.1", - "slide": "1.1.6", - "sorted-object": "2.0.1", - "sorted-union-stream": "2.1.3", - "ssri": "5.0.0", - "strip-ansi": "4.0.0", - "tar": "4.0.2", - "text-table": "0.2.0", - "uid-number": "0.0.6", - "umask": "1.1.0", - "unique-filename": "1.1.0", - "unpipe": "1.0.0", - "update-notifier": "2.3.0", - "uuid": "3.1.0", - "validate-npm-package-license": "3.0.1", - "validate-npm-package-name": "3.0.0", - "which": "1.3.0", - "worker-farm": "1.5.1", - "wrappy": "1.0.2", - "write-file-atomic": "2.1.0" - }, - "dependencies": { - "JSONStream": { - "version": "1.3.1", - "bundled": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - }, - "dependencies": { - "jsonparse": { - "version": "1.3.1", - "bundled": true - }, - "through": { - "version": "2.3.8", - "bundled": true - } - } - }, - "abbrev": { - "version": "1.1.1", - "bundled": true - }, - "ansi-regex": { - "version": "3.0.0", - "bundled": true - }, - "ansicolors": { - "version": "0.3.2", - "bundled": true - }, - "ansistyles": { - "version": "0.1.3", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true - }, - "archy": { - "version": "1.0.0", - "bundled": true - }, - "bin-links": { - "version": "1.1.0", - "bundled": true, - "requires": { - "bluebird": "3.5.1", - "cmd-shim": "2.0.2", - "fs-write-stream-atomic": "1.0.10", - "gentle-fs": "2.0.1", - "graceful-fs": "4.1.11", - "slide": "1.1.6" - } - }, - "bluebird": { - "version": "3.5.1", - "bundled": true - }, - "cacache": { - "version": "10.0.1", - "bundled": true, - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.1", - "mississippi": "1.3.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.0.0", - "unique-filename": "1.1.0", - "y18n": "3.2.1" - }, - "dependencies": { - "ssri": { - "version": "5.0.0", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "y18n": { - "version": "3.2.1", - "bundled": true - } - } - }, - "call-limit": { - "version": "1.1.0", - "bundled": true - }, - "chownr": { - "version": "1.0.1", - "bundled": true - }, - "cli-table2": { - "version": "0.2.0", - "bundled": true, - "requires": { - "colors": "1.1.2", - "lodash": "3.10.1", - "string-width": "1.0.2" - }, - "dependencies": { - "colors": { - "version": "1.1.2", - "bundled": true, - "optional": true - }, - "lodash": { - "version": "3.10.1", - "bundled": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "bundled": true - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true - } - } - } - } - } - } - }, - "cmd-shim": { - "version": "2.0.2", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1" - } - }, - "columnify": { - "version": "1.5.4", - "bundled": true, - "requires": { - "strip-ansi": "3.0.1", - "wcwidth": "1.0.1" - }, - "dependencies": { - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true - } - } - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "requires": { - "defaults": "1.0.3" - }, - "dependencies": { - "defaults": { - "version": "1.0.3", - "bundled": true, - "requires": { - "clone": "1.0.2" - }, - "dependencies": { - "clone": { - "version": "1.0.2", - "bundled": true - } - } - } - } - } - } - }, - "config-chain": { - "version": "1.1.11", - "bundled": true, - "requires": { - "ini": "1.3.4", - "proto-list": "1.2.4" - }, - "dependencies": { - "proto-list": { - "version": "1.2.4", - "bundled": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true - }, - "detect-indent": { - "version": "5.0.0", - "bundled": true - }, - "dezalgo": { - "version": "1.0.3", - "bundled": true, - "requires": { - "asap": "2.0.5", - "wrappy": "1.0.2" - }, - "dependencies": { - "asap": { - "version": "2.0.5", - "bundled": true - } - } - }, - "editor": { - "version": "1.0.0", - "bundled": true - }, - "find-npm-prefix": { - "version": "1.0.1", - "bundled": true - }, - "fs-vacuum": { - "version": "1.2.10", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "path-is-inside": "1.0.2", - "rimraf": "2.6.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "gentle-fs": { - "version": "2.0.1", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "fs-vacuum": "1.2.10", - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "path-is-inside": "1.0.2", - "read-cmd-shim": "1.0.1", - "slide": "1.1.6" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - }, - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - } - } - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - } - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true - }, - "hosted-git-info": { - "version": "2.5.0", - "bundled": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true - }, - "init-package-json": { - "version": "1.10.1", - "bundled": true, - "requires": { - "glob": "7.1.2", - "npm-package-arg": "5.1.2", - "promzard": "0.3.0", - "read": "1.0.7", - "read-package-json": "2.0.12", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1", - "validate-npm-package-name": "3.0.0" - }, - "dependencies": { - "npm-package-arg": { - "version": "5.1.2", - "bundled": true, - "requires": { - "hosted-git-info": "2.5.0", - "osenv": "0.1.4", - "semver": "5.4.1", - "validate-npm-package-name": "3.0.0" - } - }, - "promzard": { - "version": "0.3.0", - "bundled": true, - "requires": { - "read": "1.0.7" - } - } - } - }, - "is-cidr": { - "version": "1.0.0", - "bundled": true, - "requires": { - "cidr-regex": "1.0.6" - }, - "dependencies": { - "cidr-regex": { - "version": "1.0.6", - "bundled": true - } - } - }, - "lazy-property": { - "version": "1.0.0", - "bundled": true - }, - "libnpx": { - "version": "9.7.1", - "bundled": true, - "requires": { - "dotenv": "4.0.0", - "npm-package-arg": "5.1.2", - "rimraf": "2.6.2", - "safe-buffer": "5.1.1", - "update-notifier": "2.3.0", - "which": "1.3.0", - "y18n": "3.2.1", - "yargs": "8.0.2" - }, - "dependencies": { - "dotenv": { - "version": "4.0.0", - "bundled": true - }, - "npm-package-arg": { - "version": "5.1.2", - "bundled": true, - "requires": { - "hosted-git-info": "2.5.0", - "osenv": "0.1.4", - "semver": "5.4.1", - "validate-npm-package-name": "3.0.0" - } - }, - "y18n": { - "version": "3.2.1", - "bundled": true - }, - "yargs": { - "version": "8.0.2", - "bundled": true, - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "bundled": true - }, - "cliui": { - "version": "3.2.0", - "bundled": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "bundled": true - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true - } - } - }, - "wrap-ansi": { - "version": "2.1.0", - "bundled": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - } - } - }, - "decamelize": { - "version": "1.2.0", - "bundled": true - }, - "get-caller-file": { - "version": "1.0.2", - "bundled": true - }, - "os-locale": { - "version": "2.1.0", - "bundled": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - }, - "dependencies": { - "execa": { - "version": "0.7.0", - "bundled": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - }, - "dependencies": { - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "requires": { - "shebang-regex": "1.0.0" - }, - "dependencies": { - "shebang-regex": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "get-stream": { - "version": "3.0.0", - "bundled": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "requires": { - "path-key": "2.0.1" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "bundled": true - } - } - }, - "p-finally": { - "version": "1.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "strip-eof": { - "version": "1.0.0", - "bundled": true - } - } - }, - "lcid": { - "version": "1.0.0", - "bundled": true, - "requires": { - "invert-kv": "1.0.0" - }, - "dependencies": { - "invert-kv": { - "version": "1.0.0", - "bundled": true - } - } - }, - "mem": { - "version": "1.1.0", - "bundled": true, - "requires": { - "mimic-fn": "1.1.0" - }, - "dependencies": { - "mimic-fn": { - "version": "1.1.0", - "bundled": true - } - } - } - } - }, - "read-pkg-up": { - "version": "2.0.0", - "bundled": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "bundled": true, - "requires": { - "locate-path": "2.0.0" - }, - "dependencies": { - "locate-path": { - "version": "2.0.0", - "bundled": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - }, - "dependencies": { - "p-locate": { - "version": "2.0.0", - "bundled": true, - "requires": { - "p-limit": "1.1.0" - }, - "dependencies": { - "p-limit": { - "version": "1.1.0", - "bundled": true - } - } - }, - "path-exists": { - "version": "3.0.0", - "bundled": true - } - } - } - } - }, - "read-pkg": { - "version": "2.0.0", - "bundled": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "2.0.0", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "bundled": true, - "requires": { - "error-ex": "1.3.1" - }, - "dependencies": { - "error-ex": { - "version": "1.3.1", - "bundled": true, - "requires": { - "is-arrayish": "0.2.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.2.1", - "bundled": true - } - } - } - } - }, - "pify": { - "version": "2.3.0", - "bundled": true - }, - "strip-bom": { - "version": "3.0.0", - "bundled": true - } - } - }, - "path-type": { - "version": "2.0.0", - "bundled": true, - "requires": { - "pify": "2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "bundled": true - } - } - } - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "bundled": true - }, - "require-main-filename": { - "version": "1.0.1", - "bundled": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true - } - } - }, - "which-module": { - "version": "2.0.0", - "bundled": true - }, - "yargs-parser": { - "version": "7.0.0", - "bundled": true, - "requires": { - "camelcase": "4.1.0" - } - } - } - } - } - }, - "lockfile": { - "version": "1.0.3", - "bundled": true - }, - "lodash._baseindexof": { - "version": "3.1.0", - "bundled": true - }, - "lodash._baseuniq": { - "version": "4.6.0", - "bundled": true, - "requires": { - "lodash._createset": "4.0.3", - "lodash._root": "3.0.1" - }, - "dependencies": { - "lodash._createset": { - "version": "4.0.3", - "bundled": true - }, - "lodash._root": { - "version": "3.0.1", - "bundled": true - } - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "bundled": true - }, - "lodash._cacheindexof": { - "version": "3.0.2", - "bundled": true - }, - "lodash._createcache": { - "version": "3.1.2", - "bundled": true, - "requires": { - "lodash._getnative": "3.9.1" - } - }, - "lodash._getnative": { - "version": "3.9.1", - "bundled": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "bundled": true - }, - "lodash.restparam": { - "version": "3.6.1", - "bundled": true - }, - "lodash.union": { - "version": "4.6.0", - "bundled": true - }, - "lodash.uniq": { - "version": "4.5.0", - "bundled": true - }, - "lodash.without": { - "version": "4.4.0", - "bundled": true - }, - "lru-cache": { - "version": "4.1.1", - "bundled": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - }, - "dependencies": { - "pseudomap": { - "version": "1.0.2", - "bundled": true - }, - "yallist": { - "version": "2.1.2", - "bundled": true - } - } - }, - "meant": { - "version": "1.0.1", - "bundled": true - }, - "mississippi": { - "version": "1.3.0", - "bundled": true, - "requires": { - "concat-stream": "1.6.0", - "duplexify": "3.5.0", - "end-of-stream": "1.4.0", - "flush-write-stream": "1.0.2", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "1.0.2", - "pumpify": "1.3.5", - "stream-each": "1.2.0", - "through2": "2.0.3" - }, - "dependencies": { - "concat-stream": { - "version": "1.6.0", - "bundled": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - }, - "dependencies": { - "typedarray": { - "version": "0.0.6", - "bundled": true - } - } - }, - "duplexify": { - "version": "3.5.0", - "bundled": true, - "requires": { - "end-of-stream": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "stream-shift": "1.0.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.0.0", - "bundled": true, - "requires": { - "once": "1.3.3" - }, - "dependencies": { - "once": { - "version": "1.3.3", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "bundled": true - } - } - }, - "end-of-stream": { - "version": "1.4.0", - "bundled": true, - "requires": { - "once": "1.4.0" - } - }, - "flush-write-stream": { - "version": "1.0.2", - "bundled": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "from2": { - "version": "2.3.0", - "bundled": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "parallel-transform": { - "version": "1.1.0", - "bundled": true, - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.3" - }, - "dependencies": { - "cyclist": { - "version": "0.2.2", - "bundled": true - } - } - }, - "pump": { - "version": "1.0.2", - "bundled": true, - "requires": { - "end-of-stream": "1.4.0", - "once": "1.4.0" - } - }, - "pumpify": { - "version": "1.3.5", - "bundled": true, - "requires": { - "duplexify": "3.5.0", - "inherits": "2.0.3", - "pump": "1.0.2" - } - }, - "stream-each": { - "version": "1.2.0", - "bundled": true, - "requires": { - "end-of-stream": "1.4.0", - "stream-shift": "1.0.0" - }, - "dependencies": { - "stream-shift": { - "version": "1.0.0", - "bundled": true - } - } - }, - "through2": { - "version": "2.0.3", - "bundled": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - }, - "dependencies": { - "xtend": { - "version": "4.0.1", - "bundled": true - } - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "bundled": true - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - }, - "dependencies": { - "copy-concurrently": { - "version": "1.0.5", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "run-queue": { - "version": "1.0.3", - "bundled": true, - "requires": { - "aproba": "1.2.0" - } - } - } - }, - "node-gyp": { - "version": "3.6.2", - "bundled": true, - "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.4", - "request": "2.83.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" - }, - "dependencies": { - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - } - } - } - } - }, - "nopt": { - "version": "3.0.6", - "bundled": true, - "requires": { - "abbrev": "1.1.1" - } - }, - "semver": { - "version": "5.3.0", - "bundled": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - }, - "dependencies": { - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.3" - } - } - } - } - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.4" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "bundled": true, - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" - }, - "dependencies": { - "is-builtin-module": { - "version": "1.0.0", - "bundled": true, - "requires": { - "builtin-modules": "1.1.1" - }, - "dependencies": { - "builtin-modules": { - "version": "1.1.1", - "bundled": true - } - } - } - } - }, - "npm-cache-filename": { - "version": "1.0.2", - "bundled": true - }, - "npm-install-checks": { - "version": "3.0.0", - "bundled": true, - "requires": { - "semver": "5.4.1" - } - }, - "npm-lifecycle": { - "version": "2.0.0", - "bundled": true, - "requires": { - "byline": "5.0.0", - "graceful-fs": "4.1.11", - "node-gyp": "3.6.2", - "resolve-from": "4.0.0", - "slide": "1.1.6", - "uid-number": "0.0.6", - "umask": "1.1.0", - "which": "1.3.0" - }, - "dependencies": { - "byline": { - "version": "5.0.0", - "bundled": true - }, - "resolve-from": { - "version": "4.0.0", - "bundled": true - } - } - }, - "npm-package-arg": { - "version": "6.0.0", - "bundled": true, - "requires": { - "hosted-git-info": "2.5.0", - "osenv": "0.1.4", - "semver": "5.4.1", - "validate-npm-package-name": "3.0.0" - } - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" - }, - "dependencies": { - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "requires": { - "minimatch": "3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - } - } - } - } - } - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true - } - } - }, - "npm-profile": { - "version": "2.0.5", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "make-fetch-happen": "2.5.0" - }, - "dependencies": { - "make-fetch-happen": { - "version": "2.5.0", - "bundled": true, - "requires": { - "agentkeepalive": "3.3.0", - "cacache": "9.3.0", - "http-cache-semantics": "3.8.0", - "http-proxy-agent": "2.0.0", - "https-proxy-agent": "2.1.0", - "lru-cache": "4.1.1", - "mississippi": "1.3.0", - "node-fetch-npm": "2.0.2", - "promise-retry": "1.1.1", - "socks-proxy-agent": "3.0.1", - "ssri": "4.1.6" - }, - "dependencies": { - "agentkeepalive": { - "version": "3.3.0", - "bundled": true, - "requires": { - "humanize-ms": "1.2.1" - }, - "dependencies": { - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "cacache": { - "version": "9.3.0", - "bundled": true, - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.1", - "mississippi": "1.3.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "4.1.6", - "unique-filename": "1.1.0", - "y18n": "3.2.1" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "bundled": true - } - } - }, - "http-cache-semantics": { - "version": "3.8.0", - "bundled": true - }, - "http-proxy-agent": { - "version": "2.0.0", - "bundled": true, - "requires": { - "agent-base": "4.1.1", - "debug": "2.6.9" - }, - "dependencies": { - "agent-base": { - "version": "4.1.1", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "https-proxy-agent": { - "version": "2.1.0", - "bundled": true, - "requires": { - "agent-base": "4.1.1", - "debug": "2.6.9" - }, - "dependencies": { - "agent-base": { - "version": "4.1.1", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "requires": { - "encoding": "0.1.12", - "json-parse-better-errors": "1.0.1", - "safe-buffer": "5.1.1" - }, - "dependencies": { - "encoding": { - "version": "0.1.12", - "bundled": true, - "requires": { - "iconv-lite": "0.4.19" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.19", - "bundled": true - } - } - }, - "json-parse-better-errors": { - "version": "1.0.1", - "bundled": true - } - } - }, - "promise-retry": { - "version": "1.1.1", - "bundled": true, - "requires": { - "err-code": "1.1.2", - "retry": "0.10.1" - }, - "dependencies": { - "err-code": { - "version": "1.1.2", - "bundled": true - } - } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "bundled": true, - "requires": { - "agent-base": "4.1.1", - "socks": "1.1.10" - }, - "dependencies": { - "agent-base": { - "version": "4.1.1", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "socks": { - "version": "1.1.10", - "bundled": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" - }, - "dependencies": { - "ip": { - "version": "1.1.5", - "bundled": true - }, - "smart-buffer": { - "version": "1.1.15", - "bundled": true - } - } - } - } - }, - "ssri": { - "version": "4.1.6", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - } - } - }, - "npm-registry-client": { - "version": "8.5.0", - "bundled": true, - "requires": { - "concat-stream": "1.6.0", - "graceful-fs": "4.1.11", - "normalize-package-data": "2.4.0", - "npm-package-arg": "5.1.2", - "npmlog": "4.1.2", - "once": "1.4.0", - "request": "2.83.0", - "retry": "0.10.1", - "semver": "5.4.1", - "slide": "1.1.6", - "ssri": "4.1.6" - }, - "dependencies": { - "concat-stream": { - "version": "1.6.0", - "bundled": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - }, - "dependencies": { - "typedarray": { - "version": "0.0.6", - "bundled": true - } - } - }, - "npm-package-arg": { - "version": "5.1.2", - "bundled": true, - "requires": { - "hosted-git-info": "2.5.0", - "osenv": "0.1.4", - "semver": "5.4.1", - "validate-npm-package-name": "3.0.0" - } - }, - "ssri": { - "version": "4.1.6", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "npm-user-validate": { - "version": "1.0.0", - "bundled": true - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - }, - "dependencies": { - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - }, - "dependencies": { - "delegates": { - "version": "1.0.0", - "bundled": true - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "bundled": true - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true - } - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "requires": { - "string-width": "1.0.2" - } - } - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - } - } - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "opener": { - "version": "1.4.3", - "bundled": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - }, - "dependencies": { - "os-homedir": { - "version": "1.0.2", - "bundled": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true - } - } - }, - "pacote": { - "version": "7.0.2", - "bundled": true, - "requires": { - "bluebird": "3.5.1", - "cacache": "10.0.1", - "get-stream": "3.0.0", - "glob": "7.1.2", - "lru-cache": "4.1.1", - "make-fetch-happen": "2.6.0", - "minimatch": "3.0.4", - "mississippi": "1.3.0", - "normalize-package-data": "2.4.0", - "npm-package-arg": "6.0.0", - "npm-packlist": "1.1.10", - "npm-pick-manifest": "2.1.0", - "osenv": "0.1.4", - "promise-inflight": "1.0.1", - "promise-retry": "1.1.1", - "protoduck": "4.0.0", - "safe-buffer": "5.1.1", - "semver": "5.4.1", - "ssri": "5.0.0", - "tar": "4.0.2", - "unique-filename": "1.1.0", - "which": "1.3.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true - }, - "make-fetch-happen": { - "version": "2.6.0", - "bundled": true, - "requires": { - "agentkeepalive": "3.3.0", - "cacache": "10.0.1", - "http-cache-semantics": "3.8.0", - "http-proxy-agent": "2.0.0", - "https-proxy-agent": "2.1.0", - "lru-cache": "4.1.1", - "mississippi": "1.3.0", - "node-fetch-npm": "2.0.2", - "promise-retry": "1.1.1", - "socks-proxy-agent": "3.0.1", - "ssri": "5.0.0" - }, - "dependencies": { - "agentkeepalive": { - "version": "3.3.0", - "bundled": true, - "requires": { - "humanize-ms": "1.2.1" - }, - "dependencies": { - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "http-cache-semantics": { - "version": "3.8.0", - "bundled": true - }, - "http-proxy-agent": { - "version": "2.0.0", - "bundled": true, - "requires": { - "agent-base": "4.1.2", - "debug": "2.6.9" - }, - "dependencies": { - "agent-base": { - "version": "4.1.2", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "https-proxy-agent": { - "version": "2.1.0", - "bundled": true, - "requires": { - "agent-base": "4.1.2", - "debug": "2.6.9" - }, - "dependencies": { - "agent-base": { - "version": "4.1.2", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "requires": { - "encoding": "0.1.12", - "json-parse-better-errors": "1.0.1", - "safe-buffer": "5.1.1" - }, - "dependencies": { - "encoding": { - "version": "0.1.12", - "bundled": true, - "requires": { - "iconv-lite": "0.4.19" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.19", - "bundled": true - } - } - }, - "json-parse-better-errors": { - "version": "1.0.1", - "bundled": true - } - } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "bundled": true, - "requires": { - "agent-base": "4.1.2", - "socks": "1.1.10" - }, - "dependencies": { - "agent-base": { - "version": "4.1.2", - "bundled": true, - "requires": { - "es6-promisify": "5.0.0" - }, - "dependencies": { - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "requires": { - "es6-promise": "4.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.1.1", - "bundled": true - } - } - } - } - }, - "socks": { - "version": "1.1.10", - "bundled": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" - }, - "dependencies": { - "ip": { - "version": "1.1.5", - "bundled": true - }, - "smart-buffer": { - "version": "1.1.15", - "bundled": true - } - } - } - } - } - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - } - } - } - } - }, - "npm-pick-manifest": { - "version": "2.1.0", - "bundled": true, - "requires": { - "npm-package-arg": "6.0.0", - "semver": "5.4.1" - } - }, - "promise-retry": { - "version": "1.1.1", - "bundled": true, - "requires": { - "err-code": "1.1.2", - "retry": "0.10.1" - }, - "dependencies": { - "err-code": { - "version": "1.1.2", - "bundled": true - } - } - }, - "protoduck": { - "version": "4.0.0", - "bundled": true, - "requires": { - "genfun": "4.0.1" - }, - "dependencies": { - "genfun": { - "version": "4.0.1", - "bundled": true - } - } - } - } - }, - "path-is-inside": { - "version": "1.0.2", - "bundled": true - }, - "promise-inflight": { - "version": "1.0.1", - "bundled": true - }, - "qrcode-terminal": { - "version": "0.11.0", - "bundled": true - }, - "query-string": { - "version": "5.0.1", - "bundled": true, - "requires": { - "decode-uri-component": "0.2.0", - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - }, - "dependencies": { - "decode-uri-component": { - "version": "0.2.0", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true - }, - "strict-uri-encode": { - "version": "1.1.0", - "bundled": true - } - } - }, - "qw": { - "version": "1.0.1", - "bundled": true - }, - "read": { - "version": "1.0.7", - "bundled": true, - "requires": { - "mute-stream": "0.0.7" - }, - "dependencies": { - "mute-stream": { - "version": "0.0.7", - "bundled": true - } - } - }, - "read-cmd-shim": { - "version": "1.0.1", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11" - } - }, - "read-installed": { - "version": "4.0.3", - "bundled": true, - "requires": { - "debuglog": "1.0.1", - "graceful-fs": "4.1.11", - "read-package-json": "2.0.12", - "readdir-scoped-modules": "1.0.2", - "semver": "5.4.1", - "slide": "1.1.6", - "util-extend": "1.0.3" - }, - "dependencies": { - "util-extend": { - "version": "1.0.3", - "bundled": true - } - } - }, - "read-package-json": { - "version": "2.0.12", - "bundled": true, - "requires": { - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "json-parse-better-errors": "1.0.1", - "normalize-package-data": "2.4.0", - "slash": "1.0.0" - }, - "dependencies": { - "json-parse-better-errors": { - "version": "1.0.1", - "bundled": true - }, - "slash": { - "version": "1.0.0", - "bundled": true - } - } - }, - "read-package-tree": { - "version": "5.1.6", - "bundled": true, - "requires": { - "debuglog": "1.0.1", - "dezalgo": "1.0.3", - "once": "1.4.0", - "read-package-json": "2.0.12", - "readdir-scoped-modules": "1.0.2" - } - }, - "readable-stream": { - "version": "2.3.3", - "bundled": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "string_decoder": { - "version": "1.0.3", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - } - } - }, - "readdir-scoped-modules": { - "version": "1.0.2", - "bundled": true, - "requires": { - "debuglog": "1.0.1", - "dezalgo": "1.0.3", - "graceful-fs": "4.1.11", - "once": "1.4.0" - } - }, - "request": { - "version": "2.83.0", - "bundled": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - }, - "dependencies": { - "aws-sign2": { - "version": "0.7.0", - "bundled": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - }, - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "bundled": true - } - } - }, - "extend": { - "version": "3.0.1", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true - }, - "form-data": { - "version": "2.3.1", - "bundled": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - }, - "dependencies": { - "asynckit": { - "version": "0.4.0", - "bundled": true - } - } - }, - "har-validator": { - "version": "5.0.3", - "bundled": true, - "requires": { - "ajv": "5.2.3", - "har-schema": "2.0.0" - }, - "dependencies": { - "ajv": { - "version": "5.2.3", - "bundled": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" - }, - "dependencies": { - "co": { - "version": "4.6.0", - "bundled": true - }, - "fast-deep-equal": { - "version": "1.0.0", - "bundled": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "bundled": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "requires": { - "jsonify": "0.0.0" - }, - "dependencies": { - "jsonify": { - "version": "0.0.0", - "bundled": true - } - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "bundled": true - } - } - }, - "hawk": { - "version": "6.0.2", - "bundled": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.0.2" - }, - "dependencies": { - "boom": { - "version": "4.3.1", - "bundled": true, - "requires": { - "hoek": "4.2.0" - } - }, - "cryptiles": { - "version": "3.1.2", - "bundled": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "bundled": true, - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "hoek": { - "version": "4.2.0", - "bundled": true - }, - "sntp": { - "version": "2.0.2", - "bundled": true, - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "extsprintf": { - "version": "1.3.0", - "bundled": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "bundled": true - } - } - } - } - }, - "sshpk": { - "version": "1.13.1", - "bundled": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "asn1": { - "version": "0.2.3", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true - }, - "mime-types": { - "version": "2.1.17", - "bundled": true, - "requires": { - "mime-db": "1.30.0" - }, - "dependencies": { - "mime-db": { - "version": "1.30.0", - "bundled": true - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true - }, - "performance-now": { - "version": "2.1.0", - "bundled": true - }, - "qs": { - "version": "6.5.1", - "bundled": true - }, - "stringstream": { - "version": "0.0.5", - "bundled": true - }, - "tough-cookie": { - "version": "2.3.3", - "bundled": true, - "requires": { - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "bundled": true - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "retry": { - "version": "0.10.1", - "bundled": true - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "semver": { - "version": "5.4.1", - "bundled": true - }, - "sha": { - "version": "2.0.1", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "readable-stream": "2.3.3" - } - }, - "slide": { - "version": "1.1.6", - "bundled": true - }, - "sorted-object": { - "version": "2.0.1", - "bundled": true - }, - "sorted-union-stream": { - "version": "2.1.3", - "bundled": true, - "requires": { - "from2": "1.3.0", - "stream-iterate": "1.2.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "bundled": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "1.1.14" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "bundled": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "isarray": { - "version": "0.0.1", - "bundled": true - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true - } - } - } - } - }, - "stream-iterate": { - "version": "1.2.0", - "bundled": true, - "requires": { - "readable-stream": "2.3.3", - "stream-shift": "1.0.0" - }, - "dependencies": { - "stream-shift": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "ssri": { - "version": "5.0.0", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "requires": { - "ansi-regex": "3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true - } - } - }, - "tar": { - "version": "4.0.2", - "bundled": true, - "requires": { - "chownr": "1.0.1", - "minipass": "2.2.1", - "minizlib": "1.0.4", - "mkdirp": "0.5.1", - "yallist": "3.0.2" - }, - "dependencies": { - "minipass": { - "version": "2.2.1", - "bundled": true, - "requires": { - "yallist": "3.0.2" - } - }, - "minizlib": { - "version": "1.0.4", - "bundled": true, - "requires": { - "minipass": "2.2.1" - } - }, - "yallist": { - "version": "3.0.2", - "bundled": true - } - } - }, - "text-table": { - "version": "0.2.0", - "bundled": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true - }, - "umask": { - "version": "1.1.0", - "bundled": true - }, - "unique-filename": { - "version": "1.1.0", - "bundled": true, - "requires": { - "unique-slug": "2.0.0" - }, - "dependencies": { - "unique-slug": { - "version": "2.0.0", - "bundled": true, - "requires": { - "imurmurhash": "0.1.4" - } - } - } - }, - "unpipe": { - "version": "1.0.0", - "bundled": true - }, - "update-notifier": { - "version": "2.3.0", - "bundled": true, - "requires": { - "boxen": "1.2.1", - "chalk": "2.1.0", - "configstore": "3.1.1", - "import-lazy": "2.1.0", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" - }, - "dependencies": { - "boxen": { - "version": "1.2.1", - "bundled": true, - "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.1.0", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "1.0.0" - }, - "dependencies": { - "ansi-align": { - "version": "2.0.0", - "bundled": true, - "requires": { - "string-width": "2.1.1" - } - }, - "camelcase": { - "version": "4.1.0", - "bundled": true - }, - "cli-boxes": { - "version": "1.0.0", - "bundled": true - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true - } - } - }, - "term-size": { - "version": "1.2.0", - "bundled": true, - "requires": { - "execa": "0.7.0" - }, - "dependencies": { - "execa": { - "version": "0.7.0", - "bundled": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - }, - "dependencies": { - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "requires": { - "shebang-regex": "1.0.0" - }, - "dependencies": { - "shebang-regex": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "get-stream": { - "version": "3.0.0", - "bundled": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "requires": { - "path-key": "2.0.1" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "bundled": true - } - } - }, - "p-finally": { - "version": "1.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "strip-eof": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "widest-line": { - "version": "1.0.0", - "bundled": true, - "requires": { - "string-width": "1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - }, - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "bundled": true - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true - } - } - } - } - } - } - } - } - }, - "chalk": { - "version": "2.1.0", - "bundled": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "bundled": true, - "requires": { - "color-convert": "1.9.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.0", - "bundled": true, - "requires": { - "color-name": "1.1.3" - }, - "dependencies": { - "color-name": { - "version": "1.1.3", - "bundled": true - } - } - } - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true - }, - "supports-color": { - "version": "4.4.0", - "bundled": true, - "requires": { - "has-flag": "2.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "bundled": true - } - } - } - } - }, - "configstore": { - "version": "3.1.1", - "bundled": true, - "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.1.11", - "make-dir": "1.0.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.1.0", - "xdg-basedir": "3.0.0" - }, - "dependencies": { - "dot-prop": { - "version": "4.2.0", - "bundled": true, - "requires": { - "is-obj": "1.0.1" - }, - "dependencies": { - "is-obj": { - "version": "1.0.1", - "bundled": true - } - } - }, - "make-dir": { - "version": "1.0.0", - "bundled": true, - "requires": { - "pify": "2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "bundled": true - } - } - }, - "unique-string": { - "version": "1.0.0", - "bundled": true, - "requires": { - "crypto-random-string": "1.0.0" - }, - "dependencies": { - "crypto-random-string": { - "version": "1.0.0", - "bundled": true - } - } - } - } - }, - "import-lazy": { - "version": "2.1.0", - "bundled": true - }, - "is-installed-globally": { - "version": "0.1.0", - "bundled": true, - "requires": { - "global-dirs": "0.1.0", - "is-path-inside": "1.0.0" - }, - "dependencies": { - "global-dirs": { - "version": "0.1.0", - "bundled": true, - "requires": { - "ini": "1.3.4" - } - }, - "is-path-inside": { - "version": "1.0.0", - "bundled": true, - "requires": { - "path-is-inside": "1.0.2" - } - } - } - }, - "is-npm": { - "version": "1.0.0", - "bundled": true - }, - "latest-version": { - "version": "3.1.0", - "bundled": true, - "requires": { - "package-json": "4.0.1" - }, - "dependencies": { - "package-json": { - "version": "4.0.1", - "bundled": true, - "requires": { - "got": "6.7.1", - "registry-auth-token": "3.3.1", - "registry-url": "3.1.0", - "semver": "5.4.1" - }, - "dependencies": { - "got": { - "version": "6.7.1", - "bundled": true, - "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.0", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" - }, - "dependencies": { - "create-error-class": { - "version": "3.0.2", - "bundled": true, - "requires": { - "capture-stack-trace": "1.0.0" - }, - "dependencies": { - "capture-stack-trace": { - "version": "1.0.0", - "bundled": true - } - } - }, - "duplexer3": { - "version": "0.1.4", - "bundled": true - }, - "get-stream": { - "version": "3.0.0", - "bundled": true - }, - "is-redirect": { - "version": "1.0.0", - "bundled": true - }, - "is-retry-allowed": { - "version": "1.1.0", - "bundled": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true - }, - "lowercase-keys": { - "version": "1.0.0", - "bundled": true - }, - "timed-out": { - "version": "4.0.1", - "bundled": true - }, - "unzip-response": { - "version": "2.0.1", - "bundled": true - }, - "url-parse-lax": { - "version": "1.0.0", - "bundled": true, - "requires": { - "prepend-http": "1.0.4" - }, - "dependencies": { - "prepend-http": { - "version": "1.0.4", - "bundled": true - } - } - } - } - }, - "registry-auth-token": { - "version": "3.3.1", - "bundled": true, - "requires": { - "rc": "1.2.1", - "safe-buffer": "5.1.1" - }, - "dependencies": { - "rc": { - "version": "1.2.1", - "bundled": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "deep-extend": { - "version": "0.4.2", - "bundled": true - }, - "minimist": { - "version": "1.2.0", - "bundled": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - } - } - } - } - }, - "registry-url": { - "version": "3.1.0", - "bundled": true, - "requires": { - "rc": "1.2.1" - }, - "dependencies": { - "rc": { - "version": "1.2.1", - "bundled": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "deep-extend": { - "version": "0.4.2", - "bundled": true - }, - "minimist": { - "version": "1.2.0", - "bundled": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - } - } - } - } - } - } - } - } - }, - "semver-diff": { - "version": "2.1.0", - "bundled": true, - "requires": { - "semver": "5.4.1" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "bundled": true - } - } - }, - "uuid": { - "version": "3.1.0", - "bundled": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "bundled": true, - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - }, - "dependencies": { - "spdx-correct": { - "version": "1.0.2", - "bundled": true, - "requires": { - "spdx-license-ids": "1.2.2" - }, - "dependencies": { - "spdx-license-ids": { - "version": "1.2.2", - "bundled": true - } - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "bundled": true - } - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "bundled": true, - "requires": { - "builtins": "1.0.3" - }, - "dependencies": { - "builtins": { - "version": "1.0.3", - "bundled": true - } - } - }, - "which": { - "version": "1.3.0", - "bundled": true, - "requires": { - "isexe": "2.0.0" - }, - "dependencies": { - "isexe": { - "version": "2.0.0", - "bundled": true - } - } - }, - "worker-farm": { - "version": "1.5.1", - "bundled": true, - "requires": { - "errno": "0.1.4", - "xtend": "4.0.1" - }, - "dependencies": { - "errno": { - "version": "0.1.4", - "bundled": true, - "requires": { - "prr": "0.0.0" - }, - "dependencies": { - "prr": { - "version": "0.0.0", - "bundled": true - } - } - }, - "xtend": { - "version": "4.0.1", - "bundled": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - }, - "write-file-atomic": { - "version": "2.1.0", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" - } - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" - } - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "3.0.1" - } - }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "requires": { - "array-each": "1.0.1", - "array-slice": "1.0.0", - "for-own": "1.0.0", - "isobject": "3.0.1" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } - } - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "3.0.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1.0.2" - } - }, - "opener": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", - "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=" - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.2" - } - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, - "orchestrator": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", - "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", - "requires": { - "end-of-stream": "0.1.5", - "sequencify": "0.0.7", - "stream-consume": "0.1.0" - } - }, - "ordered-read-streams": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", - "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=" - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "1.2.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=" - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "pace-progress": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pace-progress/-/pace-progress-1.0.2.tgz", - "integrity": "sha1-/cVlxX3ZFyWjFns2C/JXjTw7VI0=" - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg=" - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "requires": { - "no-case": "2.3.2" - } - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.1.1", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" - } - }, - "parse-filepath": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", - "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", - "requires": { - "is-absolute": "0.2.6", - "map-cache": "0.2.2", - "path-root": "0.1.1" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "1.3.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" - }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "requires": { - "path-root-regex": "0.1.2" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha1-o14TxkeZsGzhUyD0WcIw5o5zut4=", - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "requires": { - "find-up": "2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "2.0.0" - } - } - } - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "requires": { - "ansi-cyan": "0.1.1", - "ansi-red": "0.1.1", - "arr-diff": "1.1.0", - "arr-union": "2.1.0", - "extend-shallow": "1.1.4" - }, - "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "requires": { - "arr-flatten": "1.1.0", - "array-slice": "0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=" - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=" - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "requires": { - "kind-of": "1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=" - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", - "requires": { - "chalk": "1.1.3", - "js-base64": "2.3.2", - "source-map": "0.5.7", - "supports-color": "3.2.3" - }, - "dependencies": { - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=" - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", - "requires": { - "postcss": "6.0.17" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "postcss": { - "version": "6.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", - "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.17" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "postcss": { - "version": "6.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", - "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.17" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "postcss": { - "version": "6.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", - "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.17" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "postcss": { - "version": "6.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", - "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", - "requires": { - "chalk": "2.3.1", - "source-map": "0.6.1", - "supports-color": "5.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" - }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" - }, - "primeng": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-5.0.2.tgz", - "integrity": "sha1-BcSkUC79TDvF1QaqAa6Osw+XZNs=" - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=" - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.6" - } - }, - "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "1.4.0" - } - } - } - }, - "pumpify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", - "requires": { - "duplexify": "3.5.3", - "inherits": "2.0.3", - "pump": "2.0.1" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "1.4.0" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - } - } - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qjobs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "randomfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", - "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", - "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" - } - }, - "recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "requires": { - "ast-types": "0.9.6", - "esprima": "3.1.3", - "private": "0.1.8", - "source-map": "0.5.7" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "1.4.0" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "requires": { - "balanced-match": "0.4.2" - } - }, - "reflect-metadata": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha1-DDNtOYBVPXVcObWGrjsgqknIK38=" - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", - "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", - "requires": { - "extend-shallow": "2.0.1" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "requires": { - "jsesc": "0.5.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "requires": { - "is-finite": "1.0.2" - } - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" - }, - "request": { - "version": "2.79.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", - "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3", - "uuid": "3.1.0" - }, - "dependencies": { - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", - "requires": { - "expand-tilde": "1.2.2", - "global-modules": "0.2.3" - } - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "requires": { - "aproba": "1.2.0" - } - }, - "run-sequence": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", - "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", - "requires": { - "chalk": "1.1.3", - "fancy-log": "1.3.2", - "plugin-error": "0.1.2" - }, - "dependencies": { - "fancy-log": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", - "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", - "requires": { - "ansi-gray": "0.1.1", - "color-support": "1.1.3", - "time-stamp": "1.1.0" - } - }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" - } - } - }, - "rxjs": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz", - "integrity": "sha1-KNQD8AcRIZZ/GK1mVWMlXVQjasM=", - "requires": { - "symbol-observable": "1.0.4" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "requires": { - "glob": "7.1.2", - "lodash": "4.17.4", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" - } - }, - "sass-loader": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz", - "integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==", - "requires": { - "async": "2.5.0", - "clone-deep": "0.3.0", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "pify": "3.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "requires": { - "ajv": "5.2.4" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "requires": { - "js-base64": "2.3.2", - "source-map": "0.4.4" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "requires": { - "debug": "2.6.9", - "depd": "1.1.1", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" - } - }, - "sequencify": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", - "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=" - }, - "serialize-javascript": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", - "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=" - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "requires": { - "to-object-path": "0.3.0" - } - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" - }, - "sha.js": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", - "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shallow-clone": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", - "requires": { - "is-extendable": "0.1.1", - "kind-of": "2.0.1", - "lazy-cache": "0.2.7", - "mixin-object": "2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", - "requires": { - "is-buffer": "1.1.5" - } - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=" - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "snapdragon": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", - "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.16.3" - } - }, - "socket.io": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", - "dev": true, - "requires": { - "debug": "2.3.3", - "engine.io": "1.8.3", - "has-binary": "0.1.7", - "object-assign": "4.1.0", - "socket.io-adapter": "0.5.0", - "socket.io-client": "1.7.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "2.3.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.3", - "has-binary": "0.1.7", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", - "to-array": "0.1.4" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", - "dev": true, - "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha1-qqR0A/eyRakvvJfqCPJQ1gh+0IU=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha1-etD1k/IoFZjoVN+A8ZquS5LXoRo=", - "requires": { - "atob": "2.0.3", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "requires": { - "source-map": "0.5.7" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "sparkles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", - "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=" - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "requires": { - "spdx-license-ids": "1.2.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", - "requires": { - "extend-shallow": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "ssri": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.2.2.tgz", - "integrity": "sha512-hm46mN8YSzjGuJtVocXPjwo0yTRXobXqYuK/tV6gr557/tRck4yWXKPRW8OxyJgRvcL3QgX+5ng/kMHBMco7KA==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" - } - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - }, - "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", - "requires": { - "readable-stream": "2.3.3" - } - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "stream-consume": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", - "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=" - }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha1-joxGP5HaiZF3h2WHP+TZYNj2Fr0=", - "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "1.4.0" - } - } - } - }, - "stream-http": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", - "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", - "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", - "requires": { - "first-chunk-stream": "1.0.0", - "is-utf8": "0.2.1" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "requires": { - "get-stdin": "4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "style-loader": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.19.1.tgz", - "integrity": "sha1-WR/8gLzv4mi3fF2evAUF13Jhn4U=", - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" - } - }, - "symbol-observable": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", - "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - }, - "tildify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", - "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", - "requires": { - "os-homedir": "1.0.2" - } - }, - "time-stamp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", - "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=" - }, - "timers-browserify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", - "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", - "requires": { - "setimmediate": "1.0.5" - } - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", - "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", - "requires": { - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "regex-not": "1.0.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - } - }, - "to-string-loader": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/to-string-loader/-/to-string-loader-1.1.5.tgz", - "integrity": "sha1-e3qheJG3u0lHp6Eb+wO1/enG5pU=", - "requires": { - "loader-utils": "0.2.17" - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" - }, - "true-case-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", - "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", - "requires": { - "glob": "6.0.4" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - } - } - }, - "tryer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.0.tgz", - "integrity": "sha1-Antp+oIyJeVRys4+8DsR9qs3wdc=" - }, - "ts-node": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz", - "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", - "requires": { - "arrify": "1.0.1", - "chalk": "2.3.0", - "diff": "3.4.0", - "make-error": "1.3.0", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18", - "tsconfig": "6.0.0", - "v8flags": "3.0.1", - "yn": "2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - }, - "v8flags": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", - "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", - "requires": { - "homedir-polyfill": "1.0.1" - } - } - } - }, - "tsconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", - "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", - "requires": { - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - } - } - }, - "tslib": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", - "integrity": "sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg==" - }, - "tslint": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", - "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", - "requires": { - "babel-code-frame": "6.26.0", - "builtin-modules": "1.1.1", - "chalk": "2.3.1", - "commander": "2.14.1", - "diff": "3.4.0", - "glob": "7.1.2", - "js-yaml": "3.7.0", - "minimatch": "3.0.4", - "resolve": "1.4.0", - "semver": "5.5.0", - "tslib": "1.8.0", - "tsutils": "2.21.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "tslint-language-service": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/tslint-language-service/-/tslint-language-service-0.9.8.tgz", - "integrity": "sha1-Iqby+Sa3wKTK/tOuH2UCHoAI3JY=" - }, - "tsutils": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.21.1.tgz", - "integrity": "sha512-heMkdeQ9iUc90ynfiNo5Y+GXrEEGy86KMvnSTfHO+Q40AuNQ1lZGXcv58fuU9XTUxI0V7YIN9xPN+CO9b1Gn3w==", - "requires": { - "tslib": "1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==" - } - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-detect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", - "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", - "dev": true - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.17" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typescript": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.1.tgz", - "integrity": "sha512-bqB1yS6o9TNA9ZC/MJxM0FZzPnZdtHj0xWK/IZ5khzVqdpGul/R/EIiHRgFXlwTD7PSIaYVnGKq1QgMCu2mnqw==" - }, - "uglify-es": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.10.tgz", - "integrity": "sha512-rPzPisCzW68Okj1zNrfa2dR9uEm43SevDmpR6FChoZABFk9dANGnzzBMgHYUXI3609//63fnVkyQ1SQmAMyjww==", - "requires": { - "commander": "2.14.1", - "source-map": "0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" - }, - "uglifyjs-webpack-plugin": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.8.tgz", - "integrity": "sha512-XG8/QmR1pyPeE1kj2aigo5kos8umefB31zW+PMvAAytHSB0T/vQvN6sqt8+Sh+y0b0A7zlmxNi2dzRnj0wcqGA==", - "requires": { - "cacache": "10.0.2", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.5", - "serialize-javascript": "1.4.0", - "source-map": "0.6.1", - "uglify-es": "3.3.10", - "webpack-sources": "1.1.0", - "worker-farm": "1.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", - "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", - "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "requires": { - "ajv": "6.1.1", - "ajv-keywords": "3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" - } - } - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "requires": { - "macaddress": "0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "requires": { - "unique-slug": "2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "requires": { - "imurmurhash": "0.1.4" - } - }, - "unique-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", - "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "url-loader": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", - "integrity": "sha1-oAenEJYg6dmI0UvOZ3od7Lmpk/c=", - "requires": { - "loader-utils": "1.1.0", - "mime": "1.4.1", - "schema-utils": "0.3.0" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "use": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", - "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "requires": { - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" - }, - "useragent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", - "dev": true, - "requires": { - "lru-cache": "2.2.4", - "tmp": "0.0.31" - }, - "dependencies": { - "lru-cache": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", - "dev": true - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" - }, - "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "requires": { - "user-home": "1.1.1" - } - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", - "requires": { - "clone": "1.0.2", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } - }, - "vinyl-fs": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", - "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", - "requires": { - "defaults": "1.0.3", - "glob-stream": "3.1.18", - "glob-watcher": "0.0.6", - "graceful-fs": "3.0.11", - "mkdirp": "0.5.1", - "strip-bom": "1.0.0", - "through2": "0.6.5", - "vinyl": "0.4.6" - }, - "dependencies": { - "clone": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", - "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" - }, - "graceful-fs": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", - "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", - "requires": { - "natives": "1.1.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" - } - }, - "vinyl": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", - "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", - "requires": { - "clone": "0.2.0", - "clone-stats": "0.0.1" - } - } - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "requires": { - "indexof": "0.0.1" - } - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "watchpack": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", - "requires": { - "async": "2.5.0", - "chokidar": "1.7.0", - "graceful-fs": "4.1.11" - } - }, - "webpack": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz", - "integrity": "sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg==", - "requires": { - "acorn": "5.4.1", - "acorn-dynamic-import": "2.0.2", - "ajv": "6.1.1", - "ajv-keywords": "3.1.0", - "async": "2.5.0", - "enhanced-resolve": "3.4.1", - "escope": "3.6.0", - "interpret": "1.0.4", - "json-loader": "0.5.7", - "json5": "0.5.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "mkdirp": "0.5.1", - "node-libs-browser": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.5.0", - "tapable": "0.2.8", - "uglifyjs-webpack-plugin": "0.4.6", - "watchpack": "1.4.0", - "webpack-sources": "1.0.1", - "yargs": "8.0.2" - }, - "dependencies": { - "ajv": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", - "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", - "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "requires": { - "pify": "2.3.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "requires": { - "source-map": "0.5.7", - "uglify-js": "2.8.29", - "webpack-sources": "1.0.1" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - } - } - } - } - }, - "webpack-bundle-analyzer": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.10.0.tgz", - "integrity": "sha512-eA/9F/ZLFlVXfCLYqefHFbelJ3JcvyeFdmpAG6Vu3iJNcisj3KWNPqu00lCqK9caeaesipVrGb9alUSi2lEvAg==", - "requires": { - "acorn": "5.4.1", - "bfj-node4": "5.2.1", - "chalk": "2.3.1", - "commander": "2.14.1", - "ejs": "2.5.7", - "express": "4.16.2", - "filesize": "3.6.0", - "gzip-size": "4.1.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "opener": "1.4.3", - "ws": "4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" - } - }, - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "webpack-dev-middleware": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz", - "integrity": "sha1-007++y7dp+HTtdvgcolRMhllFwk=", - "requires": { - "memory-fs": "0.4.1", - "mime": "1.4.1", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "time-stamp": "2.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - } - } - }, - "webpack-hot-middleware": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.21.0.tgz", - "integrity": "sha512-P6xiOLy10QlSVSO7GanU9PLxN6zLLQ7RG16MPTvmFwf2KUG7jMp6m+fmdgsR7xoaVVLA7OlX3YO6JjoZEKjCuA==", - "requires": { - "ansi-html": "0.0.7", - "html-entities": "1.2.1", - "querystring": "0.2.0", - "strip-ansi": "3.0.1" - } - }, - "webpack-node-externals": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz", - "integrity": "sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0=" - }, - "webpack-sources": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", - "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==", - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.5.7" - } - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=" - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", - "requires": { - "string-width": "1.0.2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, - "worker-farm": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.2.tgz", - "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==", - "requires": { - "errno": "0.1.4", - "xtend": "4.0.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.0.0.tgz", - "integrity": "sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ==", - "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" - } - }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, - "xhr2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", - "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" - }, - "xml-char-classes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", - "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=" - }, - "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" - } - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "requires": { - "camelcase": "3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=" - }, - "zone.js": { - "version": "0.8.20", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.20.tgz", - "integrity": "sha512-FXlA37ErSXCMy5RNBcGFgCI/Zivqzr0D19GuvDxhcYIJc7xkFp6c29DKyODJu0Zo+EMyur/WPPgcBh1EHjB9jA==" - } - } -} diff --git a/src/Ombi/package.json b/src/Ombi/package.json index b595f233c..5fac0c31a 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -1,94 +1,94 @@ { "name": "ombi", + "version": "2.6.0", "private": true, - "version": "1.0.0", "scripts": { "vendor": "gulp vendor", - "main": "gulp main", + "lint": "tslint -p .", "publish": "gulp publish", - "lint": "tslint ClientApp/**/*.ts", - "restore": "dotnet restore && npm install" + "restore": "dotnet restore && yarn install", + "clean": "gulp clean" }, "dependencies": { - "@angular/animations": "^5.2.5", - "@angular/cdk": "^5.2.1", - "@angular/common": "^5.2.5", - "@angular/compiler": "^5.2.5", - "@angular/core": "^5.2.5", - "@angular/forms": "^5.2.5", - "@angular/http": "^5.2.5", - "@angular/material": "^5.2.1", - "@angular/platform-browser": "^5.2.5", - "@angular/platform-browser-dynamic": "^5.2.5", - "@angular/platform-server": "^5.2.5", - "@angular/router": "^5.2.5", - "@auth0/angular-jwt": "1.0.0-beta.9", - "@ng-bootstrap/ng-bootstrap": "^1.0.0", - "@ngu/carousel": "^1.4.8", - "@ngx-translate/core": "^8.0.0", - "@ngx-translate/http-loader": "^2.0.1", - "@types/core-js": "^0.9.46", - "@types/extract-text-webpack-plugin": "^3.0.1", - "@types/intro.js": "^2.4.3", - "@types/node": "^8.9.4", - "@types/webpack": "^3.8.7", - "angular-router-loader": "^0.8.2", - "angular2-moment": "^1.8.0", + "@angular/animations": "^6.0.7", + "@angular/cdk": "^6.3.1", + "@angular/common": "^6.0.7", + "@angular/compiler": "^6.0.7", + "@angular/compiler-cli": "^6.0.7", + "@angular/core": "^6.0.7", + "@angular/forms": "^6.0.7", + "@angular/http": "^6.0.7", + "@angular/material": "^6.3.1", + "@angular/platform-browser": "^6.0.7", + "@angular/platform-browser-dynamic": "^6.0.7", + "@angular/platform-server": "^6.0.7", + "@angular/router": "^6.0.7", + "@auth0/angular-jwt": "^2.0.0", + "@ng-bootstrap/ng-bootstrap": "^2.2.0", + "@ngtools/webpack": "^6.1.0-beta.2", + "@ngu/carousel": "^1.4.9-beta-2", + "@ngx-translate/core": "^10.0.2", + "@ngx-translate/http-loader": "^3.0.1", + "@types/core-js": "^2.5.0", + "@types/mini-css-extract-plugin": "^0.2.0", + "@types/node": "^10.5.1", + "@types/webpack": "^4.4.4", + "@types/webpack-bundle-analyzer": "^2.9.2", + "@types/webpack-merge": "^4.1.3", + "@yellowspot/ng-truncate": "^1.4.0", + "angular-router-loader": "^0.8.5", "angular2-template-loader": "^0.6.2", - "aspnet-webpack": "^2.0.3", - "awesome-typescript-loader": "^3.4.1", + "aspnet-webpack": "^3.0.0", + "awesome-typescript-loader": "^5.2.0", "bootstrap": "3.3.7", "bootswatch": "3.3.7", - "core-js": "^2.5.3", - "css": "^2.2.1", - "css-loader": "^0.28.9", + "copy-webpack-plugin": "^4.5.2", + "core-js": "^2.5.7", + "css": "^2.2.3", + "css-loader": "^0.28.11", "del": "^3.0.0", - "event-source-polyfill": "^0.0.11", - "expose-loader": "^0.7.4", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "^1.1.6", + "event-source-polyfill": "^0.0.12", + "expose-loader": "^0.7.5", + "file-loader": "^1.1.11", "font-awesome": "^4.7.0", "gulp": "^3.9.1", "gulp-run": "^1.7.1", "hammerjs": "^2.0.8", - "html-loader": "0.5.1", + "html-loader": "^0.5.5", "jquery": "^3.3.1", + "mini-css-extract-plugin": "^0.4.1", + "moment": "^2.22.2", "ng2-cookies": "^1.0.12", - "ngx-clipboard": "8.1.1", - "ngx-infinite-scroll": "^0.6.1", + "ngx-clipboard": "^11.1.1", + "ngx-infinite-scroll": "^6.0.1", + "ngx-moment": "^3.0.1", "ngx-order-pipe": "^2.0.1", - "node-sass": "^4.7.2", - "npm": "^5.6.0", + "node-sass": "^4.9.0", "pace-progress": "^1.0.2", - "primeng": "5.0.2", - "reflect-metadata": "0.1.10", + "primeng": "^6.0.0", + "raw-loader": "^0.5.1", + "reflect-metadata": "^0.1.12", "run-sequence": "^2.2.1", - "rxjs": "5.5.2", - "sass-loader": "^6.0.6", - "style-loader": "^0.19.1", + "rxjs": "^6.2.1", + "sass-loader": "^7.0.3", + "style-loader": "^0.21.0", "to-string-loader": "^1.1.5", - "ts-node": "^3.3.0", - "tslint": "^5.9.1", - "tslint-language-service": "^0.9.8", - "typescript": "^2.7.1", - "uglify-es": "^3.3.10", - "uglifyjs-webpack-plugin": "^1.1.8", - "url-loader": "^0.6.2", - "webpack": "^3.11.0", - "webpack-bundle-analyzer": "^2.10.0", - "webpack-hot-middleware": "^2.21.0", - "zone.js": "^0.8.20" + "ts-node": "^7.0.0", + "tslint": "^5.10.0", + "tslint-language-service": "^0.9.9", + "typescript": "2.7.2", + "uglify-es": "^3.3.9", + "uglifyjs-webpack-plugin": "^1.2.7", + "url-loader": "^1.0.1", + "webpack": "^4.14.0", + "webpack-bundle-analyzer": "^2.13.1", + "webpack-cli": "^3.0.8", + "webpack-dev-middleware": "^3.1.3", + "webpack-hot-middleware": "^2.22.2", + "webpack-merge": "^4.1.3", + "zone.js": "^0.8.26" }, - "devDependencies": { - "@types/chai": "4.0.4", - "@types/jasmine": "2.6.2", - "chai": "4.1.2", - "jasmine-core": "2.8.0", - "karma": "1.7.1", - "karma-chai": "0.1.0", - "karma-chrome-launcher": "2.2.0", - "karma-cli": "1.0.1", - "karma-jasmine": "1.1.0", - "karma-webpack": "2.0.5" + "resolutions": { + "@types/tapable": "1.0.0" } } diff --git a/src/Ombi/tsconfig.json b/src/Ombi/tsconfig.json index daff6b451..1256bed93 100644 --- a/src/Ombi/tsconfig.json +++ b/src/Ombi/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es5", "lib": [ - "es6", + "es2017", "dom" ], "moduleResolution": "node", @@ -11,7 +11,7 @@ "noUnusedLocals": true, "noImplicitThis": true, "noImplicitReturns": true, - "noImplicitAny": true, + "noImplicitAny": false, "suppressImplicitAnyIndexErrors": true, "alwaysStrict": true, "emitDecoratorMetadata": true, @@ -27,11 +27,15 @@ } ] }, - "exclude": [ - "bin", - "node_modules" + "include": [ + "ClientApp/**/*", + "typings/**/*", + "webpack.config.*" ], "angularCompilerOptions": { - "preserveWhitespaces": false - } -} \ No newline at end of file + "preserveWhitespaces": false, + "genDir": "./ClientApp/app/ngfactory", + "entryModule": "ClientApp/app/app.module#AppModule" + }, + "compileOnSave": false +} diff --git a/src/Ombi/tslint.json b/src/Ombi/tslint.json index 73a2ee413..6ab8b85ac 100644 --- a/src/Ombi/tslint.json +++ b/src/Ombi/tslint.json @@ -8,7 +8,7 @@ ], "max-line-length": [ true, - 200 + 250 ], "arrow-parens": false, "radix": false, @@ -16,22 +16,33 @@ "indent": [ false ], - "whitespace": [ - false - ], "no-unused-expression": [ true, "allow-new" ], - "no-trailing-whitespace": [ - false - ], "max-classes-per-file": [ false ], "no-shadowed-variable": false, "comment-format": [ false + ], + "no-namespace": [ + false + ], + "no-internal-module": [ + false + ], + "whitespace": [ + false, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "no-trailing-whitespace":[ + false ] } } \ No newline at end of file diff --git a/src/Ombi/typings/globals.d.ts b/src/Ombi/typings/globals.d.ts new file mode 100644 index 000000000..ac038ef26 --- /dev/null +++ b/src/Ombi/typings/globals.d.ts @@ -0,0 +1,4 @@ +// Globals + +declare module "pace-progress"; +declare var __webpack_public_path__: any; diff --git a/src/Ombi/typings/globals/globals.d.ts b/src/Ombi/typings/globals/globals.d.ts deleted file mode 100644 index 84ca1bc2d..000000000 --- a/src/Ombi/typings/globals/globals.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Globals - -declare var module: any; -declare var require: any; -declare var localStorage: any; -declare var introJs: any; - -declare var __webpack_public_path__: any; - -declare module "pace-progress"; -declare module "webpack-bundle-analyzer"; -declare module "uglifyjs-webpack-plugin"; \ No newline at end of file diff --git a/src/Ombi/typings/index.d.ts b/src/Ombi/typings/index.d.ts index d3394b68b..8447c6a9c 100644 --- a/src/Ombi/typings/index.d.ts +++ b/src/Ombi/typings/index.d.ts @@ -1 +1 @@ -/// \ No newline at end of file +/// diff --git a/src/Ombi/webpack.config.common.ts b/src/Ombi/webpack.config.common.ts new file mode 100644 index 000000000..54835ae2e --- /dev/null +++ b/src/Ombi/webpack.config.common.ts @@ -0,0 +1,84 @@ +"use strict"; +import { AngularCompilerPlugin } from "@ngtools/webpack"; +import * as MiniCssExtractPlugin from "mini-css-extract-plugin"; +import * as path from "path"; +import { Configuration, ContextReplacementPlugin, ProvidePlugin } from "webpack"; +import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"; + +export const outputDir = "./wwwroot/dist"; + +export function isProd(env: any) { + return env && env.prod as boolean; +} + +export function isAOT(env: any) { + return env && env.aot as boolean; +} + +export const WebpackCommonConfig = (env: any, type: string) => { + const prod = isProd(env); + const aot = isAOT(env); + const vendor = type === "vendor"; + console.log(`${prod ? "Production" : "Dev"} ${type} build`); + console.log(`Output directory: ${outputDir}`); + console.log(`${aot ? "Using" : "Not using"} AOT compiler`); + const analyse = env && env.analyse as boolean; + if (analyse) { console.log("Analysing build"); } + const cssLoader = prod ? "css-loader?minimize" : "css-loader"; + const bundleConfig: Configuration = { + mode: prod ? "production" : "development", + resolve: { + extensions: [".ts", ".js"], + alias: { + pace: "pace-progress", + }, + }, + output: { + path: path.resolve(outputDir), + filename: "[name].js", + chunkFilename: "[id].[hash].chunk.js", + publicPath: "/dist/", + }, + module: { + rules: [ + { test: /\.ts$/, loader: aot ? "@ngtools/webpack" : ["awesome-typescript-loader?silent=true", "angular2-template-loader", "angular-router-loader"] }, + { test: /\.html$/, use: "html-loader?minimize=false" }, + { test: /\.css$/, use: [MiniCssExtractPlugin.loader, cssLoader] }, + { test: /\.scss$/, exclude: /ClientApp/, use: [MiniCssExtractPlugin.loader, cssLoader, "sass-loader"] }, + { test: /\.scss$/, include: /ClientApp(\\|\/)app/, use: ["to-string-loader", cssLoader, "sass-loader"] }, + { test: /\.scss$/, include: /ClientApp(\\|\/)styles/, use: ["style-loader", cssLoader, "sass-loader"] }, + { test: /\.(png|woff|woff2|eot|ttf|svg|gif)(\?|$)/, use: "url-loader?limit=100000" }, + { test: /[\/\\]@angular[\/\\].+\.js$/, parser: { system: true } }, // ignore System.import warnings https://github.com/angular/angular/issues/21560 + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new ProvidePlugin({ $: "jquery", jQuery: "jquery", Hammer: "hammerjs/hammer" }), // Global identifiers + ].concat(aot && !vendor ? [ + new AngularCompilerPlugin({ + mainPath: "./ClientApp/main.ts", + tsConfigPath: "./tsconfig.json", + skipCodeGeneration: false, + compilerOptions: { + noEmit: false, + }, + }), + ] : [ + // AOT chunk splitting does not work while this is active but doesn't seem to be needed under AOT anyway https://github.com/angular/angular-cli/issues/4431 + new ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898 + ]).concat(analyse ? [ + new BundleAnalyzerPlugin({ + analyzerMode: "static", + reportFilename: `${type}.html`, + openAnalyzer: false, + }), + ] : []), + node: { + fs: "empty", + }, + }; + + return bundleConfig; +}; diff --git a/src/Ombi/webpack.config.ts b/src/Ombi/webpack.config.ts index dceb29992..0ab82c8dc 100644 --- a/src/Ombi/webpack.config.ts +++ b/src/Ombi/webpack.config.ts @@ -1,57 +1,27 @@ -import { CheckerPlugin } from "awesome-typescript-loader"; +"use strict"; import * as path from "path"; -import * as UglifyJSPlugin from "uglifyjs-webpack-plugin"; -import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"; +import { Configuration, DllReferencePlugin } from "webpack"; +import * as webpackMerge from "webpack-merge"; -import * as webpack from "webpack"; +import { isAOT, isProd, outputDir, WebpackCommonConfig } from "./webpack.config.common"; module.exports = (env: any) => { - const prod = env && env.prod as boolean; - console.log(prod ? "Production" : "Dev" + " main build"); - const analyse = env && env.analyse as boolean; - if (analyse) { console.log("Analysing build"); } - const cssLoader = prod ? "css-loader?-url&minimize" : "css-loader?-url"; - const outputDir = "./wwwroot/dist"; - const bundleConfig: webpack.Configuration = { - entry: { main: "./ClientApp/main.ts" }, - stats: { modules: false }, - context: __dirname, - resolve: { extensions: [".ts", ".js"] }, - devtool: prod ? "source-map" : "eval-source-map", - output: { - filename: "[name].js", - publicPath: "/dist/", - path: path.join(__dirname, outputDir), - }, - module: { - rules: [ - { test: /\.ts$/, include: /ClientApp/, use: ["awesome-typescript-loader?silent=true", "angular2-template-loader", "angular-router-loader"] }, - { test: /\.html$/, use: "html-loader?minimize=false" }, - { test: /\.css$/, use: ["to-string-loader", cssLoader] }, - { test: /\.scss$/, include: /ClientApp(\\|\/)app/, use: ["to-string-loader", cssLoader, "sass-loader"] }, - { test: /\.scss$/, include: /ClientApp(\\|\/)styles/, use: ["style-loader", cssLoader, "sass-loader"] }, - { test: /\.(png|jpg|jpeg|gif|svg)$/, use: "url-loader?limit=25000" }, - ], + const prod = isProd(env); + const aot = isAOT(env); + if (!prod && aot) { console.warn("Vendor dll bundle will not be used as AOT is enabled"); } + const bundleConfig: Configuration = webpackMerge(WebpackCommonConfig(env, "main"), { + entry: { + app: "./ClientApp/main.ts", }, - plugins: [ - new CheckerPlugin(), - new webpack.DllReferencePlugin({ + devtool: prod ? "source-map" : "eval-source-map", + plugins: prod || aot ? [] : [ + // AOT chunk splitting does not work while this is active https://github.com/angular/angular-cli/issues/4565 + new DllReferencePlugin({ context: __dirname, manifest: require(path.join(__dirname, outputDir, "vendor-manifest.json")), }), - ].concat(prod ? [ - // Plugins that apply in production builds only - new UglifyJSPlugin({ sourceMap: true }), - ] : [ - // Plugins that apply in development builds only - ]).concat(analyse ? [ - new BundleAnalyzerPlugin({ - analyzerMode: "static", - reportFilename: "main.html", - openAnalyzer: false, - }), - ] : []), - }; + ], + }); return bundleConfig; }; diff --git a/src/Ombi/webpack.config.vendor.ts b/src/Ombi/webpack.config.vendor.ts index b50e784f4..dc7c282bd 100644 --- a/src/Ombi/webpack.config.vendor.ts +++ b/src/Ombi/webpack.config.vendor.ts @@ -1,41 +1,26 @@ -import * as ExtractTextPlugin from "extract-text-webpack-plugin"; +"use strict"; import * as path from "path"; -import * as UglifyJSPlugin from "uglifyjs-webpack-plugin"; import * as webpack from "webpack"; -import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"; +import * as webpackMerge from "webpack-merge"; +import { isProd, outputDir, WebpackCommonConfig } from "./webpack.config.common"; module.exports = (env: any) => { - const extractCSS = new ExtractTextPlugin("vendor.css"); - const prod = env && env.prod as boolean; - console.log(prod ? "Production" : "Dev" + " vendor build"); - const analyse = env && env.analyse as boolean; - if (analyse) { console.log("Analysing build"); } - const outputDir = "./wwwroot/dist"; - const bundleConfig = { - stats: { modules: false }, - resolve: { - extensions: [".js"], - alias: { - pace: "pace-progress", - }, - }, - module: { - rules: [ - { test: /\.(png|woff|woff2|eot|ttf|svg|gif)(\?|$)/, use: "url-loader?limit=100000" }, - { test: /\.css(\?|$)/, use: extractCSS.extract({ use: prod ? "css-loader?minimize" : "css-loader" }) }, - { test: /\.scss(\?|$)/, use: extractCSS.extract({ use: [prod ? "css-loader?minimize" : "css-loader", "sass-loader"] }) }, - ], + const prod = isProd(env); + const bundleConfig = webpackMerge(WebpackCommonConfig(env, "vendor"), { + output: { + library: "[name]_[hash]", }, entry: { - vendor: [ + vendor: ([ // add any vendor styles here e.g. bootstrap/dist/css/bootstrap.min.css "pace-progress/themes/orange/pace-theme-flash.css", "primeng/resources/primeng.min.css", "@angular/material/prebuilt-themes/deeppurple-amber.css", "font-awesome/scss/font-awesome.scss", "bootswatch/superhero/bootstrap.min.css", - + ]).concat(prod ? [] : [ // used to speed up dev launch time "@angular/animations", "@angular/common", + "@angular/common/http", "@angular/compiler", "@angular/core", "@angular/forms", @@ -67,39 +52,15 @@ module.exports = (env: any) => { "@ngx-translate/core", "@ngx-translate/http-loader", "ngx-order-pipe", - ], - }, - output: { - publicPath: "/dist/", - filename: "[name].js", - library: "[name]_[hash]", - path: path.join(__dirname, outputDir), - }, - node: { - fs: "empty", + "@yellowspot/ng-truncate", + ]), }, - plugins: [ - new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", Hammer: "hammerjs/hammer" }), // Global identifiers - new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname, "./client")), // Workaround for https://github.com/angular/angular/issues/20357 - new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/11580 - new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898 - extractCSS, + plugins: prod ? [] : [ new webpack.DllPlugin({ path: path.join(__dirname, outputDir, "[name]-manifest.json"), name: "[name]_[hash]", }), - ].concat(prod ? [ - // Plugins that apply in production builds only - new UglifyJSPlugin(), - ] : [ - // Plugins that apply in development builds only - ]).concat(analyse ? [ - new BundleAnalyzerPlugin({ - analyzerMode: "static", - reportFilename: "vendor.html", - openAnalyzer: false, - }), - ] : []), - }; + ], + }); return bundleConfig; }; diff --git a/src/Ombi/webpack.dev.js b/src/Ombi/webpack.dev.js deleted file mode 100644 index 04e36fc16..000000000 --- a/src/Ombi/webpack.dev.js +++ /dev/null @@ -1,4 +0,0 @@ -// https://github.com/aspnet/JavaScriptServices/issues/1046 - -require('ts-node/register') -module.exports = require("./webpack.config.ts"); \ No newline at end of file diff --git a/src/Ombi/wwwroot/images/default-music-placeholder.png b/src/Ombi/wwwroot/images/default-music-placeholder.png new file mode 100644 index 000000000..f6decc9e0 Binary files /dev/null and b/src/Ombi/wwwroot/images/default-music-placeholder.png differ diff --git a/src/Ombi/wwwroot/images/default_movie_poster.png b/src/Ombi/wwwroot/images/default_movie_poster.png new file mode 100644 index 000000000..f5cec85a5 Binary files /dev/null and b/src/Ombi/wwwroot/images/default_movie_poster.png differ diff --git a/src/Ombi/wwwroot/images/default_tv_poster.png b/src/Ombi/wwwroot/images/default_tv_poster.png new file mode 100644 index 000000000..28e845ea5 Binary files /dev/null and b/src/Ombi/wwwroot/images/default_tv_poster.png differ diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index ec4b0bce5..17c671217 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Fortsæt", "Available": "Tilgængelig", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Ikke tilgængelig", "ProcessingRequest": "Behandler anmodning", "PendingApproval": "Afventer godkendelse", @@ -62,20 +64,26 @@ "Italian": "Italiensk", "Danish": "Dansk", "Dutch": "Hollandsk", - "Norwegian": "Norsk" + "Norwegian": "Norsk", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Åbn mobilapp" + "OpenMobileApp": "Åbn mobilapp", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Søg", "Paragraph": "Ønsker du at se noget, som er utilgængeligt? intet problem, bare søg efter det nedenfor og anmod om det!", "MoviesTab": "Film", "TvTab": "Tv-serier", + "MusicTab": "Music", "Suggestions": "Forslag", "NoResults": "Beklager, vi fandt ingen resultater!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Se på Plex", + "ViewOnEmby": "Se på Emby", "RequestAdded": "{{title}} er anmodet med succes", "Similar": "Similar", "Movies": { @@ -107,11 +115,14 @@ "Paragraph": "Herunder kan du se dine og alle andre anmodninger, samt status for download og godkendelse.", "MoviesTab": "Film", "TvTab": "Tv-serier", + "MusicTab": "Music", "RequestedBy": "Anmodet af:", "Status": "Status:", "RequestStatus": "Status for anmodning:", "Denied": " Afvist:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Dato for anmodning:", "QualityOverride": "Tilsidesæt kvalitet:", @@ -128,7 +139,21 @@ "GridStatus": "Status", "ReportIssue": "Rapportér problem", "Filter": "Filter", - "SeasonNumberHeading": "Sæson: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Sæson: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Problemer", @@ -153,6 +178,15 @@ "ClearFilter": "Nulstil filter", "FilterHeaderAvailability": "Tilgængelighed", "FilterHeaderRequestStatus": "Status", - "Approved": "Godkendt" + "Approved": "Godkendt", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index df8b6a724..2da1aab4e 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Weiter", "Available": "Verfügbar", + "PartiallyAvailable": "Teilweise verfügbar", + "Monitored": "Monitored", "NotAvailable": "Nicht verfügbar", "ProcessingRequest": "Anfrage wird bearbeitet", "PendingApproval": "Genehmigung ausstehend", @@ -62,20 +64,26 @@ "Italian": "Italienisch", "Danish": "Dänisch", "Dutch": "Niederländisch", - "Norwegian": "Norwegisch" + "Norwegian": "Norwegisch", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Mobile App" + "OpenMobileApp": "Mobile App", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Suche", "Paragraph": "Möchtest du etwas sehen, das nicht verfügbar ist? Kein Problem, benutze einfach die Suchbox und fordere es an!", "MoviesTab": "Filme", "TvTab": "Serien", + "MusicTab": "Music", "Suggestions": "Vorschläge", "NoResults": "Es tut uns leid, wir haben keine Ergebnisse gefunden!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "In Plex anschauen", + "ViewOnEmby": "In Emby anschauen", "RequestAdded": "Anfrage für {{title}} wurde erfolgreich hinzugefügt", "Similar": "Similar", "Movies": { @@ -104,14 +112,17 @@ }, "Requests": { "Title": "Anfragen", - "Paragraph": "Unten sehen Sie Ihre und alle anderen Anfragen, sowie deren Download-und Genehmigungsstatus.", + "Paragraph": "Unten sehen Sie Ihre und alle anderen Anfragen, sowie deren Download und Genehmigungsstatus.", "MoviesTab": "Filme", "TvTab": "Serien", + "MusicTab": "Music", "RequestedBy": "Angefordert von:", "Status": "Status:", "RequestStatus": "Anfrage Status:", "Denied": " Abgelehnt:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Veröffentlicht: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Datum der Anfrage:", "QualityOverride": "Qualitäts Überschreiben:", @@ -128,7 +139,21 @@ "GridStatus": "Status", "ReportIssue": "Problem melden", "Filter": "Filter", - "SeasonNumberHeading": "Staffel: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Staffel: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Probleme", @@ -153,6 +178,15 @@ "ClearFilter": "Filter zurücksetzen", "FilterHeaderAvailability": "Verfügbarkeit", "FilterHeaderRequestStatus": "Status", - "Approved": "Bestätigt" + "Approved": "Bestätigt", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 124c26fc5..cbe2c034e 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Continue", "Available": "Available", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Not Available", "ProcessingRequest": "Processing Request", "PendingApproval": "Pending Approval", @@ -65,7 +67,10 @@ "Italian": "Italian", "Danish": "Danish", "Dutch": "Dutch", - "Norwegian":"Norwegian" + "Norwegian":"Norwegian", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish":"Polish", + "Swedish":"Swedish" }, "OpenMobileApp":"Open Mobile App", "RecentlyAdded":"Recently Added" @@ -76,11 +81,13 @@ "Want to watch something that is not currently available? No problem, just search for it below and request it!", "MoviesTab": "Movies", "TvTab": "TV Shows", + "MusicTab":"Music", "Suggestions": "Suggestions", "NoResults": "Sorry, we didn't find any results!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease":"Theatrical Release: {{date}}", "ViewOnPlex": "View On Plex", + "ViewOnEmby": "View On Emby", "RequestAdded": "Request for {{title}} has been added successfully", "Similar":"Similar", "Movies": { @@ -113,11 +120,13 @@ "Below you can see yours and all other requests, as well as their download and approval status.", "MoviesTab": "Movies", "TvTab": "TV Shows", + "MusicTab":"Music", "RequestedBy": "Requested By:", "Status": "Status:", "RequestStatus": "Request status:", "Denied": " Denied:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Request Date:", @@ -137,10 +146,19 @@ "Filter":"Filter", "Sort":"Sort", "SeasonNumberHeading":"Season: {seasonNumber}", - "SortTitle":"Title", - "SortRequestDate": "Request Date", - "SortRequestedBy":"Requested By", - "SortStatus":"Status" + "SortTitleAsc":"Title ▲", + "SortTitleDesc":"Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc":"Status ▲", + "SortStatusDesc":"Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues":{ "Title":"Issues", @@ -167,5 +185,13 @@ "FilterHeaderRequestStatus":"Status", "Approved":"Approved", "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index 6b2f87874..a49ea5140 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Continuar", "Available": "Disponible", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Not Available", "ProcessingRequest": "Procesando solicitud", "PendingApproval": "Pendiente de aprobación", @@ -62,20 +64,26 @@ "Italian": "Italiano", "Danish": "Danés", "Dutch": "Holandés", - "Norwegian": "Norwegian" + "Norwegian": "Norwegian", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Open Mobile App" + "OpenMobileApp": "Open Mobile App", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Buscar", "Paragraph": "¿Quieres ver algo que no está disponible? No hay problema, búscalo y solicítalo!", "MoviesTab": "Películas", "TvTab": "Series", + "MusicTab": "Music", "Suggestions": "Sugerencias", "NoResults": "¡Lo sentimos, no encontramos ningún resultado!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Ver en Plex", + "ViewOnEmby": "Ver en Emby", "RequestAdded": "La solicitud de {{title}} se ha agregado con éxito", "Similar": "Similar", "Movies": { @@ -107,11 +115,14 @@ "Paragraph": "A continuación puedes ver tanto las tuyas como las demás solicitudes, así como su estado de descarga y aprobación.", "MoviesTab": "Películas", "TvTab": "Series", + "MusicTab": "Music", "RequestedBy": "Solicitado por:", "Status": "Estado:", "RequestStatus": "Estado de la solicitud:", "Denied": " Denegado:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Fecha de solicitud:", "QualityOverride": "Sobreescribir calidad:", @@ -128,7 +139,21 @@ "GridStatus": "Estado", "ReportIssue": "Report Issue", "Filter": "Filter", - "SeasonNumberHeading": "Season: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Season: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Incidencias", @@ -153,6 +178,15 @@ "ClearFilter": "Clear Filter", "FilterHeaderAvailability": "Availability", "FilterHeaderRequestStatus": "Status", - "Approved": "Approved" + "Approved": "Approved", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 983763a08..0834653f8 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -12,7 +12,9 @@ "Common": { "ContinueButton": "Continuer", "Available": "Disponible", - "NotAvailable": "Not Available", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", + "NotAvailable": "Non disponible", "ProcessingRequest": "En cours de traitement", "PendingApproval": "En attente d'approbation", "RequestDenied": "Demande refusée", @@ -21,7 +23,7 @@ "Request": "Demander", "Denied": "Refusé", "Approve": "Approuver", - "PartlyAvailable": "Partly Available", + "PartlyAvailable": "Partiellement disponible", "Errors": { "Validation": "Veuillez vérifier les valeurs entrées" } @@ -47,7 +49,7 @@ "UserManagement": "Gestion des utilisateurs", "Issues": "Problèmes", "Donate": "Faire un don !", - "DonateLibraryMaintainer": "Donate to Library Maintainer", + "DonateLibraryMaintainer": "Faire un don au mainteneur de la bibliothèque", "DonateTooltip": "C’est pour convaincre ma femme de me laisser passer mon temps libre à développer Ombi ;)", "UpdateAvailableTooltip": "Mise à jour disponible !", "Settings": "Paramètres", @@ -62,22 +64,28 @@ "Italian": "Italien", "Danish": "Danois", "Dutch": "Néerlandais", - "Norwegian": "Norvégien" + "Norwegian": "Norvégien", + "BrazillianPortuguese": "Portuguais brésilien", + "Polish": "Polonais", + "Swedish": "Swedish" }, - "OpenMobileApp": "Ouvrir l'application mobile" + "OpenMobileApp": "Ouvrir l'application mobile", + "RecentlyAdded": "Ajouts récents" }, "Search": { "Title": "Rechercher", "Paragraph": "Vous voulez regarder quelque chose qui n'est pas disponible actuellement ? Pas de problème, recherchez-le ci-dessous et demandez-le !", "MoviesTab": "Films", "TvTab": "TV", + "MusicTab": "Music", "Suggestions": "Suggestions", "NoResults": "Désolé, nous n'avons trouvé aucun résultat !", - "DigitalDate": "Digital Release: {{date}}", - "TheatricalRelease": "Theatrical Release: {{date}}", + "DigitalDate": "Sortie numérique: {{date}}", + "TheatricalRelease": "Sortie en salle: {{date}}", "ViewOnPlex": "Regarder sur Plex", + "ViewOnEmby": "Regarder sur Emby", "RequestAdded": "La demande pour {{title}} a été ajoutée avec succès", - "Similar": "Similar", + "Similar": "Similaires", "Movies": { "PopularMovies": "Films populaires", "UpcomingMovies": "Films à venir", @@ -87,19 +95,19 @@ "Trailer": "Bande-annonce" }, "TvShows": { - "Popular": "Popular", - "Trending": "Trending", - "MostWatched": "Most Watched", - "MostAnticipated": "Most Anticipated", - "Results": "Results", - "AirDate": "Air Date:", - "AllSeasons": "All Seasons", - "FirstSeason": "First Season", - "LatestSeason": "Latest Season", - "Select": "Select ...", - "SubmitRequest": "Submit Request", - "Season": "Season: {{seasonNumber}}", - "SelectAllInSeason": "Select All in Season {{seasonNumber}}" + "Popular": "Populaire", + "Trending": "Tendances", + "MostWatched": "Les plus visionnées", + "MostAnticipated": "Les plus attendus", + "Results": "Résultats", + "AirDate": "Date de diffusion:", + "AllSeasons": "Toutes les saisons", + "FirstSeason": "Première saison", + "LatestSeason": "Dernière saison", + "Select": "Sélectionner...", + "SubmitRequest": "Envoyer la demande", + "Season": "Saison: {{seasonNumber}}", + "SelectAllInSeason": "Tout sélectionner dans la saison {{seasonNumber}}" } }, "Requests": { @@ -107,12 +115,15 @@ "Paragraph": "Vous pouvez voir ci-dessous vos demandes et celles des autres, ainsi que leur statut de téléchargement et d'approbation.", "MoviesTab": "Films", "TvTab": "Émissions", + "MusicTab": "Music", "RequestedBy": "Demandé par :", "Status": "Statut :", "RequestStatus": "Statut de la demande :", "Denied": " Refusé :", - "TheatricalRelease": "Theatrical Release: {{date}}", - "DigitalRelease": "Digital Release: {{date}}", + "TheatricalRelease": "Sortie en salle: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Sortie en salle", + "DigitalRelease": "Sortie numérique: {{date}}", "RequestDate": "Date de la demande :", "QualityOverride": "Remplacement de la qualité :", "RootFolderOverride": "Remplacement du répertoire racine :", @@ -126,9 +137,23 @@ "GridTitle": "Titre", "AirDate": "Date de diffusion", "GridStatus": "Statut", - "ReportIssue": "Report Issue", - "Filter": "Filter", - "SeasonNumberHeading": "Season: {seasonNumber}" + "ReportIssue": "Signaler un problème", + "Filter": "Filtre", + "Sort": "Trier", + "SeasonNumberHeading": "Saison: {seasonNumber}", + "SortTitleAsc": "Titre ▲", + "SortTitleDesc": "Titre ▼", + "SortRequestDateAsc": "Date de la demande ▲", + "SortRequestDateDesc": "Date de la demande ▼", + "SortStatusAsc": "Statut ▲", + "SortStatusDesc": "Statut ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Problèmes", @@ -150,9 +175,18 @@ "ReportedBy": "Signalé par" }, "Filter": { - "ClearFilter": "Clear Filter", - "FilterHeaderAvailability": "Availability", - "FilterHeaderRequestStatus": "Status", - "Approved": "Approved" + "ClearFilter": "Effacer les filtres", + "FilterHeaderAvailability": "Disponibilité", + "FilterHeaderRequestStatus": "Statut", + "Approved": "Validée", + "PendingApproval": "En attente de validation" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 2081ce2a1..25f706303 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Continua", "Available": "Disponibile", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Not Available", "ProcessingRequest": "Richiesta in elaborazione", "PendingApproval": "In attesa di approvazione", @@ -62,20 +64,26 @@ "Italian": "Italiano", "Danish": "Danese", "Dutch": "Olandese", - "Norwegian": "Norvegese" + "Norwegian": "Norvegese", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Apri l'applicazione mobile" + "OpenMobileApp": "Apri l'applicazione mobile", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Cerca", "Paragraph": "Vuoi vedere qualcosa che non è attualmente disponibile? Nessun problema, basta cercare qui sotto e richiederlo!", "MoviesTab": "Film", "TvTab": "Serie TV", + "MusicTab": "Music", "Suggestions": "Suggerimenti", "NoResults": "Ci dispiace, non abbiamo trovato alcun risultato!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Guarda su Plex", + "ViewOnEmby": "Guarda su Emby", "RequestAdded": "La richiesta per {{title}} è stata aggiunta correttamente", "Similar": "Similar", "Movies": { @@ -107,11 +115,14 @@ "Paragraph": "Qui sotto puoi vedere le tue e tutte le altre richieste con il loro stato di download e approvazione.", "MoviesTab": "Film", "TvTab": "Serie TV", + "MusicTab": "Music", "RequestedBy": "Richiesta da:", "Status": "Stato:", "RequestStatus": "Stato della richiesta:", "Denied": " Rifiutato:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Data della richiesta:", "QualityOverride": "Sovrascrivi qualità:", @@ -128,7 +139,21 @@ "GridStatus": "Stato", "ReportIssue": "Report Issue", "Filter": "Filter", - "SeasonNumberHeading": "Season: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Season: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Problemi", @@ -153,6 +178,15 @@ "ClearFilter": "Clear Filter", "FilterHeaderAvailability": "Availability", "FilterHeaderRequestStatus": "Status", - "Approved": "Approved" + "Approved": "Approved", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index a9ba6b683..60d54fe8c 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Doorgaan", "Available": "Beschikbaar", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Niet Beschikbaar", "ProcessingRequest": "Verzoek wordt verwerkt", "PendingApproval": "Wacht op goedkeuring", @@ -62,20 +64,26 @@ "Italian": "Italiaans", "Danish": "Deens", "Dutch": "Nederlands", - "Norwegian": "Noors" + "Norwegian": "Noors", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Open Mobiele App" + "OpenMobileApp": "Open Mobiele App", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Zoeken", "Paragraph": "Wil je iets kijken dat momenteel niet beschikbaar is? Geen probleem, zoek het hieronder op en vraag het aan!", "MoviesTab": "Films", "TvTab": "TV Series", + "MusicTab": "Music", "Suggestions": "Suggesties", "NoResults": "Sorry, we hebben geen resultaten gevonden!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Bekijk op Plex", + "ViewOnEmby": "Bekijk op Emby", "RequestAdded": "Aanvraag voor {{title}} is succesvol toegevoegd", "Similar": "Similar", "Movies": { @@ -107,11 +115,14 @@ "Paragraph": "Hieronder zie je jouw en alle andere verzoeken, evenals hun download en goedkeuring status.", "MoviesTab": "Films", "TvTab": "TV Series", + "MusicTab": "Music", "RequestedBy": "Verzocht Door:", "Status": "Status:", "RequestStatus": "Aanvraagstatus:", "Denied": " Geweigerd:", "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Aanvraag Datum:", "QualityOverride": "Kwaliteit overschrijven:", @@ -128,7 +139,21 @@ "GridStatus": "Status", "ReportIssue": "Probleem Melden", "Filter": "Filter", - "SeasonNumberHeading": "Seizoen: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Seizoen: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Problemen", @@ -153,6 +178,15 @@ "ClearFilter": "Verwijder Filter", "FilterHeaderAvailability": "Beschikbaarheid", "FilterHeaderRequestStatus": "Status", - "Approved": "Goedgekeurd" + "Approved": "Goedgekeurd", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index cbaf1379a..87df0181f 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -12,6 +12,8 @@ "Common": { "ContinueButton": "Gå videre", "Available": "Tilgjengelig", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", "NotAvailable": "Ikke tilgjengelig", "ProcessingRequest": "Behandler forespørsel", "PendingApproval": "Venter på godkjenning", @@ -62,20 +64,26 @@ "Italian": "Italiensk", "Danish": "Dansk", "Dutch": "Nederlandsk", - "Norwegian": "Norsk" + "Norwegian": "Norsk", + "BrazillianPortuguese": "Brazillian Portuguese", + "Polish": "Polish", + "Swedish": "Swedish" }, - "OpenMobileApp": "Åpne mobilapp" + "OpenMobileApp": "Åpne mobilapp", + "RecentlyAdded": "Recently Added" }, "Search": { "Title": "Søk", "Paragraph": "Vil du se noe som foreløpig ikke er tilgjengelig? Ikke noe problem, bare søk etter det nedenfor og be om det!", "MoviesTab": "Filmer", "TvTab": "TV serier", + "MusicTab": "Music", "Suggestions": "Forslag", "NoResults": "Beklager, vi fant ingen resultater!", "DigitalDate": "Digital utgivelse: {{date}}", "TheatricalRelease": "Kinopremiere: {{date}}", "ViewOnPlex": "Spill av på Plex", + "ViewOnEmby": "Spill av på Emby", "RequestAdded": "Forespørsel om {{title}} er lagt til", "Similar": "Lignende", "Movies": { @@ -107,11 +115,14 @@ "Paragraph": "Nedenfor kan du se dine og alle andres forespørsler, du ser også status for nedlasting og godkjenning.", "MoviesTab": "Filmer", "TvTab": "TV serier", + "MusicTab": "Music", "RequestedBy": "Etterspurt av:", "Status": "Status:", "RequestStatus": "Status for forespørsel:", "Denied": " Avslått:", "TheatricalRelease": "Kinopremiere: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", "DigitalRelease": "Digital utgivelse: {{date}}", "RequestDate": "Dato for forespørsel:", "QualityOverride": "Overstyr kvalitet:", @@ -128,7 +139,21 @@ "GridStatus": "Status", "ReportIssue": "Rapportér en feil", "Filter": "Filter", - "SeasonNumberHeading": "Sesong: {seasonNumber}" + "Sort": "Sort", + "SeasonNumberHeading": "Sesong: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } }, "Issues": { "Title": "Mangler", @@ -153,6 +178,15 @@ "ClearFilter": "Tøm filter", "FilterHeaderAvailability": "Tilgjengelighet", "FilterHeaderRequestStatus": "Status", - "Approved": "Godkjent" + "Approved": "Godkjent", + "PendingApproval": "Pending Approval" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json new file mode 100644 index 000000000..8e180e5bb --- /dev/null +++ b/src/Ombi/wwwroot/translations/pl.json @@ -0,0 +1,192 @@ +{ + "Login": { + "SignInButton": "Zaloguj się", + "UsernamePlaceholder": "Nazwa użytkownika", + "PasswordPlaceholder": "Hasło", + "RememberMe": "Zapamiętaj mnie", + "ForgottenPassword": "Nie pamiętasz hasła?", + "Errors": { + "IncorrectCredentials": "Nieprawidłowa nazwa użytkownika lub hasło" + } + }, + "Common": { + "ContinueButton": "Dalej", + "Available": "Dostępny", + "PartiallyAvailable": "Partially Available", + "Monitored": "Monitored", + "NotAvailable": "Niedostępny", + "ProcessingRequest": "Przetwarzanie zgłoszenia", + "PendingApproval": "Oczekujące na zatwierdzenie", + "RequestDenied": "Zgłoszenie odrzucone", + "NotRequested": "Nie zgłoszone", + "Requested": "Zgłoszone", + "Request": "Zgłoszenie", + "Denied": "Odrzucone", + "Approve": "Zatwierdź", + "PartlyAvailable": "Dostępne częściowo", + "Errors": { + "Validation": "Sprawdź wprowadzone dane" + } + }, + "PasswordReset": { + "EmailAddressPlaceholder": "Adres e-mail", + "ResetPasswordButton": "Zresetuj hasło" + }, + "LandingPage": { + "OnlineHeading": "Dostępny", + "OnlineParagraph": "Serwer mediów jest dostępny", + "PartiallyOnlineHeading": "Dostępny częściowo", + "PartiallyOnlineParagraph": "Serwer mediów jest dostępny częściowo.", + "MultipleServersUnavailable": "{{serversUnavailable}} serwerów spośród {{totalServers}} jest niedostępnych.", + "SingleServerUnavailable": "Jeden serwer spośród {{totalServers}} jest niedostępny.", + "OfflineHeading": "Niedostępny", + "OfflineParagraph": "Serwer mediów jest niedostępny.", + "CheckPageForUpdates": "Tutaj znajdziesz aktualizacje dotyczące tej strony." + }, + "NavigationBar": { + "Search": "Szukaj", + "Requests": "Zgłoszenia", + "UserManagement": "Zarządzanie użytkownikami", + "Issues": "Problemy", + "Donate": "Wesprzyj!", + "DonateLibraryMaintainer": "Wesprzyj właściciela biblioteki", + "DonateTooltip": "W ten sposób przekonuję moją żonę by spędzać mój wolny czas rozwijając Ombi ;)", + "UpdateAvailableTooltip": "Dostępna aktualizacja!", + "Settings": "Ustawienia", + "Welcome": "Witaj {{username}}", + "UpdateDetails": "Podaj szczegóły", + "Logout": "Wyloguj", + "Language": { + "English": "Angielski", + "French": "Francuski", + "Spanish": "Hiszpański", + "German": "Niemiecki", + "Italian": "Włoski", + "Danish": "Duński", + "Dutch": "Holenderski", + "Norwegian": "Norweski", + "BrazillianPortuguese": "Brazylijski portugalski", + "Polish": "Polski", + "Swedish": "Swedish" + }, + "OpenMobileApp": "Otwórz aplikację mobilną", + "RecentlyAdded": "Ostatnio dodane" + }, + "Search": { + "Title": "Szukaj", + "Paragraph": "Chcesz obejrzeć coś, co nie jest obecnie dostępne? Żaden problem, po prostu wyszukaj poniżej i dodaj zgłoszenie!", + "MoviesTab": "Filmy", + "TvTab": "Seriale", + "MusicTab": "Music", + "Suggestions": "Sugestie", + "NoResults": "Niestety nic nie znaleziono!", + "DigitalDate": "Wydanie cyfrowe: {{date}}", + "TheatricalRelease": "Premiera kinowa: {{date}}", + "ViewOnPlex": "Obejrzyj w Plex", + "ViewOnEmby": "Obejrzyj na Emby", + "RequestAdded": "Zgłoszenie dla {{title}} zostało dodane", + "Similar": "Podobne", + "Movies": { + "PopularMovies": "Popularne filmy", + "UpcomingMovies": "Wkrótce w kinach", + "TopRatedMovies": "Najwyżej oceniane filmy", + "NowPlayingMovies": "W kinach", + "HomePage": "Strona główna", + "Trailer": "Zwiastun" + }, + "TvShows": { + "Popular": "Popularne", + "Trending": "Zyskujące popularność", + "MostWatched": "Najczęściej oglądane", + "MostAnticipated": "Najbardziej oczekiwane", + "Results": "Wyniki", + "AirDate": "Data emisji:", + "AllSeasons": "Wszystkie sezony", + "FirstSeason": "Pierwszy sezon", + "LatestSeason": "Najnowszy sezon", + "Select": "Wybierz…", + "SubmitRequest": "Dodaj zgłoszenie", + "Season": "Sezon: {{seasonNumber}}", + "SelectAllInSeason": "Wybierz wszystkie w sezonie {{seasonNumber}}" + } + }, + "Requests": { + "Title": "Zgłoszenia", + "Paragraph": "Poniżej są twoje i wszystkie inne zgłoszenia, a także ich status akceptacji i pobierania.", + "MoviesTab": "Filmy", + "TvTab": "Seriale", + "MusicTab": "Music", + "RequestedBy": "Zgłoszone przez:", + "Status": "Stan:", + "RequestStatus": "Stan zgłoszenia:", + "Denied": "Odrzucono:", + "TheatricalRelease": "Premiera kinowa: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Premiera kinowa", + "DigitalRelease": "Wydanie cyfrowe: {{date}}", + "RequestDate": "Data zgłoszenia:", + "QualityOverride": "Wymuszenie jakości:", + "RootFolderOverride": "Wymuszenie folderu głównego:", + "ChangeRootFolder": "Folder główny", + "ChangeQualityProfile": "Wybór jakości", + "MarkUnavailable": "Oznacz jako niedostępne", + "MarkAvailable": "Oznacz jako dostępne", + "Remove": "Usuń", + "Deny": "Odrzuć", + "Season": "Sezon:", + "GridTitle": "Tytuł", + "AirDate": "Data emisji", + "GridStatus": "Stan", + "ReportIssue": "Zgłoś problem", + "Filter": "Filtr", + "Sort": "Sortowanie", + "SeasonNumberHeading": "Sezon: {seasonNumber}", + "SortTitleAsc": "Tytuł ▲", + "SortTitleDesc": "Tytuł ▼", + "SortRequestDateAsc": "Data zgłoszenia ▲", + "SortRequestDateDesc": "Data zgłoszenia ▼", + "SortStatusAsc": "Stan ▲", + "SortStatusDesc": "Stan ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } + }, + "Issues": { + "Title": "Problemy", + "PendingTitle": "Oczekujące problemy", + "InProgressTitle": "Problemy w trakcie", + "ResolvedTitle": "Problemy rozwiązane", + "ColumnTitle": "Tytuł", + "Category": "Kategoria", + "Status": "Stan", + "Details": "Szczegóły", + "Description": "Opis", + "NoComments": "Brak komentarzy!", + "MarkInProgress": "Oznacz jako \"w trakcie\"", + "MarkResolved": "Oznacz jako rozwiązane", + "SendMessageButton": "Wyślij", + "Subject": "Temat", + "Comments": "Komentarze", + "WriteMessagePlaceholder": "Tutaj wpisz swoją wiadomość…", + "ReportedBy": "Zgłoszone przez" + }, + "Filter": { + "ClearFilter": "Wyczyść fltr", + "FilterHeaderAvailability": "Dostępność", + "FilterHeaderRequestStatus": "Stan", + "Approved": "Zatwierdzone", + "PendingApproval": "Oczekujące na zatwierdzenie" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" + } +} \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json new file mode 100644 index 000000000..e547d0066 --- /dev/null +++ b/src/Ombi/wwwroot/translations/pt.json @@ -0,0 +1,192 @@ +{ + "Login": { + "SignInButton": "Registrar", + "UsernamePlaceholder": "Nome de usuário", + "PasswordPlaceholder": "Senha", + "RememberMe": "Lembre-se de mim", + "ForgottenPassword": "Esqueceu sua senha?", + "Errors": { + "IncorrectCredentials": "Nome de usuário ou senha incorretos" + } + }, + "Common": { + "ContinueButton": "Continuar", + "Available": "Disponível", + "PartiallyAvailable": "Parcialmente Disponível", + "Monitored": "Monitorado", + "NotAvailable": "Inisponível", + "ProcessingRequest": "Processando Solicitação", + "PendingApproval": "Aprovação Pendente", + "RequestDenied": "Solicitação Negada", + "NotRequested": "Não Solicitado", + "Requested": "Solicitado", + "Request": "Solicitar", + "Denied": "Negado", + "Approve": "Aprovar", + "PartlyAvailable": "Parcialmente Disponível", + "Errors": { + "Validation": "Por favor, verifique os dados inseridos" + } + }, + "PasswordReset": { + "EmailAddressPlaceholder": "Endereço de e-mail", + "ResetPasswordButton": "Redefinir Senha" + }, + "LandingPage": { + "OnlineHeading": "Online Agora", + "OnlineParagraph": "O servidor de mídia está atualmente online", + "PartiallyOnlineHeading": "Parcialmente Online", + "PartiallyOnlineParagraph": "O servidor de mídia está parcialmente online.", + "MultipleServersUnavailable": "Existem {{serversUnavailable}} servidores offline em um total de {{totalServers}}.", + "SingleServerUnavailable": "Existe {{serversUnavailable}} servidor offline em um total de {{totalServers}}.", + "OfflineHeading": "Offline Agora", + "OfflineParagraph": "O servidor de mídia está atualmente offline.", + "CheckPageForUpdates": "Verifique esta página para atualizações contínuas do site." + }, + "NavigationBar": { + "Search": "Pesquisar", + "Requests": "Solicitações", + "UserManagement": "Gerenciador de Usuário", + "Issues": "Problemas", + "Donate": "Fazer uma doação!", + "DonateLibraryMaintainer": "Doar para o Dono da Biblioteca", + "DonateTooltip": "É assim que eu convenço a minha mulher a deixar-me passar o meu tempo livre desenvolvendo Ombi;)", + "UpdateAvailableTooltip": "Atualização Disponível!", + "Settings": "Configurações", + "Welcome": "Bem-vindo, {{username}}", + "UpdateDetails": "Detalhes da Atualização", + "Logout": "Sair", + "Language": { + "English": "Inglês", + "French": "Francês", + "Spanish": "Espanhol", + "German": "Alemão", + "Italian": "Italiano", + "Danish": "Dinamarquês", + "Dutch": "Holandês", + "Norwegian": "Norueguês", + "BrazillianPortuguese": "Português (Brasil)", + "Polish": "Polonês", + "Swedish": "Swedish" + }, + "OpenMobileApp": "Abrir aplicativo do celular", + "RecentlyAdded": "Recentemente adicionado" + }, + "Search": { + "Title": "Pesquisar", + "Paragraph": "Quer assistir a algo que não está disponível? Não há problema, basta procurar abaixo e solicitar!", + "MoviesTab": "Filmes", + "TvTab": "Séries", + "MusicTab": "Músicas", + "Suggestions": "Sugestões", + "NoResults": "Desculpe, não encontramos nenhum resultado!", + "DigitalDate": "Lançamento digital: {{date}}", + "TheatricalRelease": "Lançamento nos Cinemas: {{date}}", + "ViewOnPlex": "Assistir no Plex", + "ViewOnEmby": "Assistir no Emby", + "RequestAdded": "Pedido de {{title}} foi adicionado com sucesso", + "Similar": "Semelhante", + "Movies": { + "PopularMovies": "Filmes populares", + "UpcomingMovies": "Próximos filmes", + "TopRatedMovies": "Filmes mais votados", + "NowPlayingMovies": "Filmes em cartazes", + "HomePage": "Home Page", + "Trailer": "Trailer" + }, + "TvShows": { + "Popular": "Popular", + "Trending": "Mais populares", + "MostWatched": "Mais assistidos", + "MostAnticipated": "Mais esperados", + "Results": "Resultados", + "AirDate": "Data de exibição:", + "AllSeasons": "Todas as temporadas", + "FirstSeason": "Primeira temporada", + "LatestSeason": "Última temporada", + "Select": "Selecione...", + "SubmitRequest": "Enviar solicitação", + "Season": "Temporada: {{seasonNumber}}", + "SelectAllInSeason": "Selecione tudo na temporada {{seasonNumber}}" + } + }, + "Requests": { + "Title": "Solicitações", + "Paragraph": "Abaixo, você pode ver o seu e todos os outros pedidos, bem como o seu download e status de aprovação.", + "MoviesTab": "Filmes", + "TvTab": "Séries", + "MusicTab": "Músicas", + "RequestedBy": "Solicitado por:", + "Status": "Status:", + "RequestStatus": "Status da solicitação:", + "Denied": " Negados:", + "TheatricalRelease": "Lançamento nos Cinemas: {{date}}", + "ReleaseDate": "Lançado: {{date}}", + "TheatricalReleaseSort": "Lançamento nos Cinemas", + "DigitalRelease": "Lançamento digital: {{date}}", + "RequestDate": "Data da Solicitação:", + "QualityOverride": "Substituição de qualidade:", + "RootFolderOverride": "Substituição da pasta raiz:", + "ChangeRootFolder": "Pasta raiz", + "ChangeQualityProfile": "Perfil de qualidade", + "MarkUnavailable": "Marcar como Indisponível", + "MarkAvailable": "Marcar como Disponível", + "Remove": "Remover", + "Deny": "Negar", + "Season": "Temporada:", + "GridTitle": "Título", + "AirDate": "Data de exibição", + "GridStatus": "Status", + "ReportIssue": "Relatar Problema", + "Filter": "Filtro", + "Sort": "Ordenar por", + "SeasonNumberHeading": "Temporada: {seasonNumber}", + "SortTitleAsc": "Título ▲", + "SortTitleDesc": "Título ▼", + "SortRequestDateAsc": "Data da Solicitação", + "SortRequestDateDesc": "Data da Solicitação", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + } + }, + "Issues": { + "Title": "Problemas", + "PendingTitle": "Problemas pendentes", + "InProgressTitle": "Resolvendo Problemas", + "ResolvedTitle": "Problemas Resolvidos", + "ColumnTitle": "Título", + "Category": "Categoria", + "Status": "Status", + "Details": "Detalhes", + "Description": "Descrição", + "NoComments": "Sem Comentários!", + "MarkInProgress": "Marcar como em andamento", + "MarkResolved": "Marcar como resolvido", + "SendMessageButton": "Enviar", + "Subject": "Assunto", + "Comments": "Comentários", + "WriteMessagePlaceholder": "Escreva sua mensagem aqui...", + "ReportedBy": "Reportado por" + }, + "Filter": { + "ClearFilter": "Limpar Filtro", + "FilterHeaderAvailability": "Disponibilidade", + "FilterHeaderRequestStatus": "Status", + "Approved": "Aprovado", + "PendingApproval": "Aprovação Pendente" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" + } +} \ No newline at end of file diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index b750b5e0c..26a85fe10 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -12,7 +12,9 @@ "Common": { "ContinueButton": "Fortsätt", "Available": "Tillgänglig", - "NotAvailable": "Not Available", + "PartiallyAvailable": "Delvis tillgänliga", + "Monitored": "Övervakad", + "NotAvailable": "Finns ej", "ProcessingRequest": "Bearbetar förfrågan", "PendingApproval": "Väntar på godkännande", "RequestDenied": "Efterfrågan nekas", @@ -21,7 +23,7 @@ "Request": "Begär", "Denied": "Nekad", "Approve": "Godkän", - "PartlyAvailable": "Partly Available", + "PartlyAvailable": "Delvis tillgänglig", "Errors": { "Validation": "Vänligen kontrollera din angivna värden" } @@ -62,22 +64,28 @@ "Italian": "Italienska", "Danish": "Danska", "Dutch": "Holländska", - "Norwegian": "Norska" + "Norwegian": "Norska", + "BrazillianPortuguese": "Brazillian portugisiska", + "Polish": "Polska", + "Swedish": "Swedish" }, - "OpenMobileApp": "Öppna Mobil App" + "OpenMobileApp": "Öppna Mobil App", + "RecentlyAdded": "Nyligen tillagda" }, "Search": { "Title": "Sök", "Paragraph": "Vill du titta på något som inte är tillgängligt? Inga problem, Sök efter det nedan och önska det!", "MoviesTab": "Filmer", "TvTab": "TV-serier", + "MusicTab": "Musik", "Suggestions": "Förslag", "NoResults": "Tyvärr, hittade vi inte några resultat!", - "DigitalDate": "Digital Release: {{date}}", - "TheatricalRelease": "Theatrical Release: {{date}}", + "DigitalDate": "Digitalt släpp: {{date}}", + "TheatricalRelease": "Biopremiär: {{date}}", "ViewOnPlex": "Visa på Plex", + "ViewOnEmby": "Visa på Emby", "RequestAdded": "Efterfrågan om {{title}} har lagts till", - "Similar": "Similar", + "Similar": "Liknande", "Movies": { "PopularMovies": "Populära filmer", "UpcomingMovies": "Kommande filmer", @@ -87,19 +95,19 @@ "Trailer": "Trailer" }, "TvShows": { - "Popular": "Popular", - "Trending": "Trending", - "MostWatched": "Most Watched", - "MostAnticipated": "Most Anticipated", - "Results": "Results", - "AirDate": "Air Date:", - "AllSeasons": "All Seasons", - "FirstSeason": "First Season", - "LatestSeason": "Latest Season", - "Select": "Select ...", - "SubmitRequest": "Submit Request", - "Season": "Season: {{seasonNumber}}", - "SelectAllInSeason": "Select All in Season {{seasonNumber}}" + "Popular": "Populära", + "Trending": "Trendar", + "MostWatched": "Mest sedda", + "MostAnticipated": "Mest efterlängtade", + "Results": "Resultat", + "AirDate": "Sändningsdatum:", + "AllSeasons": "Alla Säsonger", + "FirstSeason": "Första säsongen", + "LatestSeason": "Senaste säsongen", + "Select": "Välj...", + "SubmitRequest": "Skicka förfrågan", + "Season": "Säsong: {{seasonNumber}}", + "SelectAllInSeason": "Välj alla i denna säsong {{seasonNumber}}" } }, "Requests": { @@ -107,12 +115,15 @@ "Paragraph": "Nedan kan du se din och andras efterfrågningar, samt nedladdnings och godkännande status.", "MoviesTab": "Filmer", "TvTab": "TV-serier", + "MusicTab": "Musik", "RequestedBy": "Efterfrågats av:", "Status": "Status:", "RequestStatus": "Status för efterfrågan:", "Denied": " Nekad:", - "TheatricalRelease": "Theatrical Release: {{date}}", - "DigitalRelease": "Digital Release: {{date}}", + "TheatricalRelease": "Biopremiär: {{date}}", + "ReleaseDate": "Släppt: {{date}}", + "TheatricalReleaseSort": "Biopremiär", + "DigitalRelease": "Digitalt Releasedatum: {{date}}", "RequestDate": "Datum för efterfrågan:", "QualityOverride": "Kvalité överskridande:", "RootFolderOverride": "Root mapp överskridande:", @@ -126,9 +137,23 @@ "GridTitle": "Titel", "AirDate": "Sändningsdatum", "GridStatus": "Status", - "ReportIssue": "Report Issue", - "Filter": "Filter", - "SeasonNumberHeading": "Season: {seasonNumber}" + "ReportIssue": "Rapportera Problem", + "Filter": "Filtrera", + "Sort": "Sortera", + "SeasonNumberHeading": "Säsong: {seasonNumber}", + "SortTitleAsc": "Titel ▲", + "SortTitleDesc": "Titel ▼", + "SortRequestDateAsc": "Efterfrågades ▲", + "SortRequestDateDesc": "Efterfrågades ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} återstående förfrågningar", + "NextDays": "En annan begäran kommer att läggas till om {{time}} Dagar", + "NextHours": "En annan begäran kommer att läggas till om {{time}} Timmar", + "NextMinutes": "En annan begäran kommer att läggas till om {{time}} Minuter", + "NextMinute": "En annan begäran kommer att läggas till om {{time}} Minut" + } }, "Issues": { "Title": "Problem", @@ -150,9 +175,18 @@ "ReportedBy": "Rapporterad av" }, "Filter": { - "ClearFilter": "Clear Filter", - "FilterHeaderAvailability": "Availability", + "ClearFilter": "Rensa filter", + "FilterHeaderAvailability": "Tillgänglighet", "FilterHeaderRequestStatus": "Status", - "Approved": "Approved" + "Approved": "Godkänd", + "PendingApproval": "Väntar på godkännande" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" } } \ No newline at end of file diff --git a/src/Ombi/yarn.lock b/src/Ombi/yarn.lock new file mode 100644 index 000000000..68381bf9d --- /dev/null +++ b/src/Ombi/yarn.lock @@ -0,0 +1,6223 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@angular-devkit/core@0.7.0-rc.0": + version "0.7.0-rc.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.7.0-rc.0.tgz#57f2f472f0f85eb8d8c251220a0bcad7b40c7a5c" + dependencies: + ajv "~6.4.0" + chokidar "^2.0.3" + rxjs "^6.0.0" + source-map "^0.5.6" + +"@angular/animations@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-6.0.7.tgz#af9fd317556f3bf425f77905eb6a7f3281dca12a" + dependencies: + tslib "^1.9.0" + +"@angular/cdk@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-6.3.1.tgz#88f65710b3f9fdc64c956a2862076eeb4e7a4d57" + dependencies: + tslib "^1.7.1" + +"@angular/common@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-6.0.7.tgz#ce741587d94fee81bb1c9b79fd45690fae2ea7a8" + dependencies: + tslib "^1.9.0" + +"@angular/compiler-cli@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-6.0.7.tgz#0f6ff586e7f7c5d37aa91d53521250bc02620a4f" + dependencies: + chokidar "^1.4.2" + minimist "^1.2.0" + reflect-metadata "^0.1.2" + tsickle "^0.29.0" + +"@angular/compiler@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-6.0.7.tgz#8313251be50a4446653a52279d806de6582012a3" + dependencies: + tslib "^1.9.0" + +"@angular/core@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.7.tgz#f4a577615ee284d47250a506f7ba98296c0f9d4e" + dependencies: + tslib "^1.9.0" + +"@angular/forms@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-6.0.7.tgz#1c6740b3b6b88fadcb1dc48862d102575c8d0f6e" + dependencies: + tslib "^1.9.0" + +"@angular/http@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-6.0.7.tgz#1d132eda965f4a0e3371c35cf1610f394537b996" + dependencies: + tslib "^1.9.0" + +"@angular/material@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-6.3.1.tgz#d1da2bc6d20a710a9affc36132e872206e67d698" + dependencies: + tslib "^1.7.1" + +"@angular/platform-browser-dynamic@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.7.tgz#1c5f63f62eef1ca564e4db62f8e8419f402f044b" + dependencies: + tslib "^1.9.0" + +"@angular/platform-browser@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-6.0.7.tgz#53470304671534b545c2b00385b9d6dfe5dbb722" + dependencies: + tslib "^1.9.0" + +"@angular/platform-server@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-6.0.7.tgz#90892ad02c268578c5e0d978ed0f8d30c986be9e" + dependencies: + domino "^2.0.1" + tslib "^1.9.0" + xhr2 "^0.1.4" + +"@angular/router@^6.0.7": + version "6.0.7" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-6.0.7.tgz#53ce5dba1bb0f033687caca1451a12489a615415" + dependencies: + tslib "^1.9.0" + +"@auth0/angular-jwt@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@auth0/angular-jwt/-/angular-jwt-2.0.0.tgz#c0b2861824a24fb62d9d2e1fdb03a326e8a1e203" + dependencies: + url "^0.11.0" + +"@ng-bootstrap/ng-bootstrap@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-2.2.0.tgz#acd514e878a1412f39d50eff691095ecc0882bf3" + +"@ngtools/webpack@^6.1.0-beta.2": + version "6.1.0-rc.0" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-6.1.0-rc.0.tgz#186ac69382092de56ea86edca7a666954b6d2aac" + dependencies: + "@angular-devkit/core" "0.7.0-rc.0" + tree-kill "^1.0.0" + webpack-sources "^1.1.0" + +"@ngu/carousel@^1.4.9-beta-2": + version "1.4.9-beta-2" + resolved "https://registry.yarnpkg.com/@ngu/carousel/-/carousel-1.4.9-beta-2.tgz#69da71805ab5dc45fd5ff9625f1970f84dbf8fcb" + +"@ngx-translate/core@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-10.0.2.tgz#5eeb78f47845b476a1e892fb2fb153dbbaf72850" + dependencies: + tslib "^1.9.0" + +"@ngx-translate/http-loader@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@ngx-translate/http-loader/-/http-loader-3.0.1.tgz#20b0f98bc6c25321129d3e3302ab3cc489c0a42a" + dependencies: + tslib "^1.9.0" + +"@types/core-js@^2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.0.tgz#35cc282488de6f10af1d92902899a3b8ca3fbc47" + +"@types/mini-css-extract-plugin@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@types/mini-css-extract-plugin/-/mini-css-extract-plugin-0.2.0.tgz#afb037dbbd76f6c13803927c2751d194188b5d47" + dependencies: + "@types/webpack" "*" + +"@types/node@*", "@types/node@^10.5.1": + version "10.5.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.1.tgz#d578446f4abff5c0b49ade9b4e5274f6badaadfc" + +"@types/tapable@*", "@types/tapable@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.0.tgz#b76254453021be05681f6213416766bac9afb99c" + +"@types/uglify-js@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.2.tgz#f30c75458d18e8ee885c792c04adcb78a13bc286" + dependencies: + source-map "^0.6.1" + +"@types/webpack-bundle-analyzer@^2.9.2": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.9.2.tgz#a3f9f4dd8bc3025550001a1704cfd31125773f40" + dependencies: + "@types/webpack" "*" + +"@types/webpack-merge@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/webpack-merge/-/webpack-merge-4.1.3.tgz#e6af0f2a0f20a86ac83d7da84a2e454063121ad4" + dependencies: + "@types/webpack" "*" + +"@types/webpack@*", "@types/webpack@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.4.tgz#f001a0a43cf5b433d57a1a22366fe1939d648957" + dependencies: + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + source-map "^0.6.0" + +"@webassemblyjs/ast@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.12.tgz#a9acbcb3f25333c4edfa1fdf3186b1ccf64e6664" + dependencies: + "@webassemblyjs/helper-module-context" "1.5.12" + "@webassemblyjs/helper-wasm-bytecode" "1.5.12" + "@webassemblyjs/wast-parser" "1.5.12" + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/floating-point-hex-parser@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.12.tgz#0f36044ffe9652468ce7ae5a08716a4eeff9cd9c" + +"@webassemblyjs/helper-api-error@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.12.tgz#05466833ff2f9d8953a1a327746e1d112ea62aaf" + +"@webassemblyjs/helper-buffer@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.12.tgz#1f0de5aaabefef89aec314f7f970009cd159c73d" + dependencies: + debug "^3.1.0" + +"@webassemblyjs/helper-code-frame@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.12.tgz#3cdc1953093760d1c0f0caf745ccd62bdb6627c7" + dependencies: + "@webassemblyjs/wast-printer" "1.5.12" + +"@webassemblyjs/helper-fsm@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.12.tgz#6bc1442b037f8e30f2e57b987cee5c806dd15027" + +"@webassemblyjs/helper-module-context@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.12.tgz#b5588ca78b33b8a0da75f9ab8c769a3707baa861" + dependencies: + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.12.tgz#d12a3859db882a448891a866a05d0be63785b616" + +"@webassemblyjs/helper-wasm-section@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.12.tgz#ff9fe1507d368ad437e7969d25e8c1693dac1884" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-buffer" "1.5.12" + "@webassemblyjs/helper-wasm-bytecode" "1.5.12" + "@webassemblyjs/wasm-gen" "1.5.12" + debug "^3.1.0" + +"@webassemblyjs/ieee754@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.12.tgz#ee9574bc558888f13097ce3e7900dff234ea19a4" + dependencies: + ieee754 "^1.1.11" + +"@webassemblyjs/leb128@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.12.tgz#0308eec652765ee567d8a5fa108b4f0b25b458e1" + dependencies: + leb "^0.3.0" + +"@webassemblyjs/utf8@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.12.tgz#d5916222ef314bf60d6806ed5ac045989bfd92ce" + +"@webassemblyjs/wasm-edit@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.12.tgz#821c9358e644a166f2c910e5af1b46ce795a17aa" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-buffer" "1.5.12" + "@webassemblyjs/helper-wasm-bytecode" "1.5.12" + "@webassemblyjs/helper-wasm-section" "1.5.12" + "@webassemblyjs/wasm-gen" "1.5.12" + "@webassemblyjs/wasm-opt" "1.5.12" + "@webassemblyjs/wasm-parser" "1.5.12" + "@webassemblyjs/wast-printer" "1.5.12" + debug "^3.1.0" + +"@webassemblyjs/wasm-gen@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.12.tgz#0b7ccfdb93dab902cc0251014e2e18bae3139bcb" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-wasm-bytecode" "1.5.12" + "@webassemblyjs/ieee754" "1.5.12" + "@webassemblyjs/leb128" "1.5.12" + "@webassemblyjs/utf8" "1.5.12" + +"@webassemblyjs/wasm-opt@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.12.tgz#bd758a8bc670f585ff1ae85f84095a9e0229cbc9" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-buffer" "1.5.12" + "@webassemblyjs/wasm-gen" "1.5.12" + "@webassemblyjs/wasm-parser" "1.5.12" + debug "^3.1.0" + +"@webassemblyjs/wasm-parser@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.12.tgz#7b10b4388ecf98bd7a22e702aa62ec2f46d0c75e" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-api-error" "1.5.12" + "@webassemblyjs/helper-wasm-bytecode" "1.5.12" + "@webassemblyjs/ieee754" "1.5.12" + "@webassemblyjs/leb128" "1.5.12" + "@webassemblyjs/utf8" "1.5.12" + +"@webassemblyjs/wast-parser@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.12.tgz#9cf5ae600ecae0640437b5d4de5dd6b6088d0d8b" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/floating-point-hex-parser" "1.5.12" + "@webassemblyjs/helper-api-error" "1.5.12" + "@webassemblyjs/helper-code-frame" "1.5.12" + "@webassemblyjs/helper-fsm" "1.5.12" + long "^3.2.0" + mamacro "^0.0.3" + +"@webassemblyjs/wast-printer@1.5.12": + version "1.5.12" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.12.tgz#563ca4d01b22d21640b2463dc5e3d7f7d9dac520" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/wast-parser" "1.5.12" + long "^3.2.0" + +"@webpack-contrib/schema-utils@^1.0.0-beta.0": + version "1.0.0-beta.0" + resolved "https://registry.yarnpkg.com/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chalk "^2.3.2" + strip-ansi "^4.0.0" + text-table "^0.2.0" + webpack-log "^1.1.2" + +"@yellowspot/ng-truncate@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@yellowspot/ng-truncate/-/ng-truncate-1.4.0.tgz#dcb40f5571ef71a9cf09f6a24e83e1f43b2d2a6c" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + +acorn-dynamic-import@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" + dependencies: + acorn "^5.0.0" + +acorn@^5.0.0, acorn@^5.3.0, acorn@^5.6.2: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + +ajv-keywords@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^6.1.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.1" + +ajv@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.4.0.tgz#d3aff78e9277549771daf0164cff48482b754fc6" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + uri-js "^3.0.2" + +alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +angular-router-loader@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/angular-router-loader/-/angular-router-loader-0.8.5.tgz#42b378d0914c1ce412b851c1d92b80dbbe4f2fc5" + dependencies: + loader-utils "^1.0.2" + +angular2-template-loader@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz#c0d44e90fff0fac95e8b23f043acda7fd1c51d7c" + dependencies: + loader-utils "^0.2.15" + +ansi-cyan@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" + dependencies: + ansi-wrap "0.1.0" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-escapes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + dependencies: + ansi-wrap "0.1.0" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + +ansi-red@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-wrap@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" + dependencies: + arr-flatten "^1.0.1" + array-slice "^0.2.3" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + +array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-slice@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1, array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +aspnet-webpack@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aspnet-webpack/-/aspnet-webpack-3.0.0.tgz#a7cf4462f4027652104a43f99193986fb1167a08" + dependencies: + connect "^3.4.1" + es6-promise "^3.1.2" + memory-fs "^0.3.0" + require-from-string "^1.1.0" + webpack-node-externals "^1.4.3" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +ast-types@0.9.6: + version "0.9.6" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + +autoprefixer@^6.3.1: + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + dependencies: + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.16" + postcss-value-parser "^3.2.3" + +awesome-typescript-loader@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-5.2.0.tgz#d7bccf4823c45096ec24da4c12a1507d276ba15a" + dependencies: + chalk "^2.4.1" + enhanced-resolve "^4.0.0" + loader-utils "^1.1.0" + lodash "^4.17.5" + micromatch "^3.1.9" + mkdirp "^0.5.1" + source-map-support "^0.5.3" + webpack-log "^1.2.0" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" + +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-polyfill@6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-runtime@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + dependencies: + tweetnacl "^0.14.3" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + +bfj-node4@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.3.1.tgz#e23d8b27057f1d0214fc561142ad9db998f26830" + dependencies: + bluebird "^3.5.1" + check-types "^7.3.0" + tryer "^1.0.0" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +body-parser@1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +bootstrap@3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" + +bootswatch@3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-3.3.7.tgz#eb6f9a9a8523b87a706ea91deec3e0d7eaa8ab1f" + +brace-expansion@^1.0.0, brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.1.tgz#3343124db6d7ad53e26a8826318712bdc8450f9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +buffer-from@^1.0.0, buffer-from@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +caller-id@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-id/-/caller-id-0.1.0.tgz#59bdac0893d12c3871408279231f97458364f07b" + dependencies: + stack-trace "~0.0.7" + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +caniuse-api@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + dependencies: + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000861" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000861.tgz#6f27840a130c10c0b1e00fab7729c1faf8f4ccd3" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + +chardet@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" + +check-types@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" + +chokidar@^1.4.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chokidar@^2.0.2, chokidar@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + dependencies: + tslib "^1.9.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clap@^1.0.9: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" + dependencies: + chalk "^1.1.3" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@4.1.x: + version "4.1.11" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a" + dependencies: + source-map "0.5.x" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" + dependencies: + for-own "^1.0.0" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + +clone@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" + +clone@^1.0.0, clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +coa@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.3.0, color-convert@^1.9.0: + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + dependencies: + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +color-name@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.x, commander@~2.15.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + +commander@^2.12.1, commander@^2.13.0, commander@^2.9.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" + +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect@^3.4.1: + version "3.6.6" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" + dependencies: + debug "2.6.9" + finalhandler "1.1.0" + parseurl "~1.3.2" + utils-merge "1.0.1" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +copy-webpack-plugin@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz#d53444a8fea2912d806e78937390ddd7e632ee5c" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + globby "^7.1.1" + is-glob "^4.0.0" + loader-utils "^1.1.0" + minimatch "^3.0.4" + p-limit "^1.0.0" + serialize-javascript "^1.4.0" + +core-js@^2.4.0, core-js@^2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-loader@^0.28.11: + version "0.28.11" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" + dependencies: + babel-code-frame "^6.26.0" + css-selector-tokenizer "^0.7.0" + cssnano "^3.10.0" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash.camelcase "^4.3.0" + object-assign "^4.1.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" + +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be" + dependencies: + inherits "^2.0.1" + source-map "^0.1.38" + source-map-resolve "^0.5.1" + urix "^0.1.0" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +cssnano@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +dateformat@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" + +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +defaults@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +del@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" + dependencies: + globby "^6.1.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + p-map "^1.1.1" + pify "^3.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +depd@~1.1.1, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +deprecated@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diff@^3.1.0, diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + +domino@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/domino/-/domino-2.0.2.tgz#fa2da6ace8381cf64089079470ee33c53901010f" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + dependencies: + readable-stream "~1.1.9" + +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +ejs@^2.5.7: + version "2.6.1" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" + +electron-to-chromium@^1.2.7: + version "1.3.50" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.50.tgz#7438b76f92b41b919f3fbdd350fbd0757dacddf7" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1, encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + +end-of-stream@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" + dependencies: + once "~1.3.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.45" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653" + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + next-tick "1" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promise@^3.1.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-templates@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz#5cb9ac9fb1ded6eb1239342b81d792bbb4078ee4" + dependencies: + recast "~0.11.12" + through "~2.3.6" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-scope@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esprima@^2.6.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +esprima@~3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +event-source-polyfill@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/event-source-polyfill/-/event-source-polyfill-0.0.12.tgz#e539cd67fdef2760a16aa5262fa98134df52e3af" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + dependencies: + homedir-polyfill "^1.0.1" + +expose-loader@^0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.5.tgz#e29ea2d9aeeed3254a3faa1b35f502db9f9c3f6f" + +express@^4.16.2: + version "4.16.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" + dependencies: + kind-of "^1.1.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +external-editor@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + +external-editor@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" + dependencies: + chardet "^0.5.0" + iconv-lite "^0.4.22" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fancy-log@^1.1.0, fancy-log@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + time-stamp "^1.0.0" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-loader@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8" + dependencies: + loader-utils "^1.0.2" + schema-utils "^0.4.5" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +filesize@^3.5.11: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + dependencies: + debug "2.6.9" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.3.1" + unpipe "~1.0.0" + +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-index@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +fined@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +first-chunk-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" + +flagged-respawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +flush-write-stream@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + +font-awesome@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0, fsevents@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +fstream@^1.0.0, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" + dependencies: + globule "~0.1.0" + +gaze@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-stream@^3.1.5: + version "3.1.18" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" + dependencies: + glob "^4.3.1" + glob2base "^0.0.12" + minimatch "^2.0.1" + ordered-read-streams "^0.1.0" + through2 "^0.6.1" + unique-stream "^1.0.0" + +glob-watcher@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" + dependencies: + gaze "^0.5.1" + +glob2base@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" + dependencies: + find-index "^0.1.1" + +glob@^4.3.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "^2.0.1" + once "^1.3.0" + +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" + dependencies: + graceful-fs "~1.2.0" + inherits "1" + minimatch "~0.2.11" + +global-modules-path@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globule@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" + dependencies: + glob "~7.1.1" + lodash "~4.17.10" + minimatch "~3.0.2" + +globule@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" + dependencies: + glob "~3.1.21" + lodash "~1.0.1" + minimatch "~0.2.11" + +glogg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" + dependencies: + sparkles "^1.0.0" + +graceful-fs@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" + dependencies: + natives "^1.1.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graceful-fs@~1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" + +gulp-run@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/gulp-run/-/gulp-run-1.7.1.tgz#e17c0acb7c30b6e2aeee23c04442a96c0caceffa" + dependencies: + gulp-util "^3.0.0" + lodash.defaults "^4.0.1" + lodash.template "^4.0.2" + vinyl "^0.4.6" + +gulp-util@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^2.0.0" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulp@^3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" + dependencies: + archy "^1.0.0" + chalk "^1.0.0" + deprecated "^0.0.1" + gulp-util "^3.0.0" + interpret "^1.0.0" + liftoff "^2.1.0" + minimist "^1.1.0" + orchestrator "^0.3.0" + pretty-hrtime "^1.0.0" + semver "^4.1.0" + tildify "^1.0.0" + v8flags "^2.0.2" + vinyl-fs "^0.3.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + dependencies: + glogg "^1.0.0" + +gzip-size@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c" + dependencies: + duplexer "^0.1.1" + pify "^3.0.0" + +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + dependencies: + sparkles "^1.0.0" + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.4.tgz#8b50e1f35d51bd01e5ed9ece4dbe3549ccfa0a3c" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.1.x: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +homedir-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.1.tgz#6e4cee78b01bb849dcf93527708c69fdbee410df" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +html-entities@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + +html-loader@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.5.5.tgz#6356dbeb0c49756d8ebd5ca327f16ff06ab5faea" + dependencies: + es6-templates "^0.2.3" + fastparse "^1.1.1" + html-minifier "^3.5.8" + loader-utils "^1.1.0" + object-assign "^4.1.1" + +html-minifier@^3.5.8: + version "3.5.17" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.17.tgz#fe9834c4288e4d5b4dfe18fbc7f3f811c108e5ea" + dependencies: + camel-case "3.0.x" + clean-css "4.1.x" + commander "2.15.x" + he "1.1.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +http-errors@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + dependencies: + postcss "^6.0.1" + +ieee754@^1.1.11, ieee754@^1.1.4: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +inquirer@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347" + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.1" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^2.0.0" + strip-ansi "^3.0.0" + through "^2.3.6" + +inquirer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.0.0.tgz#e8c20303ddc15bbfc2c12a6213710ccd9e1413d8" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.0" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.1.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +interpret@^1.0.0, interpret@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + +is-my-json-valid@^2.12.4: + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + dependencies: + is-unc-path "^1.0.0" + +is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + dependencies: + unc-path-regex "^0.1.2" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +jquery@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" + +js-base64@^2.1.8, js-base64@^2.1.9: + version "2.4.5" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.5.tgz#e293cd3c7c82f070d700fc7a1ca0a2e69f101f92" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.7.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +leb@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3" + +liftoff@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" + dependencies: + extend "^3.0.0" + findup-sync "^2.0.0" + fined "^1.0.1" + flagged-respawn "^1.0.0" + is-plain-object "^2.0.4" + object.map "^1.0.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^0.2.15, loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + +lodash._reinterpolate@^3.0.0, lodash._reinterpolate@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + dependencies: + lodash._root "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.mergewith@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + +lodash.tail@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.template@^4.0.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + dependencies: + lodash._reinterpolate "~3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash.templatesettings@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + dependencies: + lodash._reinterpolate "~3.0.0" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^4.0.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +lodash@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" + +log-symbols@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +loglevelnext@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" + dependencies: + es6-symbol "^3.1.1" + object.assign "^4.1.0" + +long@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + +loud-rejection@^1.0.0, loud-rejection@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +lru-cache@^4.0.1, lru-cache@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + +make-error@^1.1.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535" + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + dependencies: + kind-of "^6.0.2" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + +math-random@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" + +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +memory-fs@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.0.4, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + +mime@^2.0.3, mime@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +mini-css-extract-plugin@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.1.tgz#d2bcf77bb2596b8e4bd9257e43d3f9164c2e86cb" + dependencies: + "@webpack-contrib/schema-utils" "^1.0.0-beta.0" + loader-utils "^1.1.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimatch@^2.0.1: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimatch@~0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mock-require@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-2.0.2.tgz#1eaa71aad23013773d127dc7e91a3fbb4837d60d" + dependencies: + caller-id "^0.1.0" + +moment@^2.22.2: + version "2.22.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + dependencies: + duplexer2 "0.0.2" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nan@^2.10.0, nan@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natives@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.4.tgz#2f0f224fc9a7dd53407c7667c84cf8dbe773de58" + +needle@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" + +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +ng2-cookies@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/ng2-cookies/-/ng2-cookies-1.0.12.tgz#3f3e613e0137b0649b705c678074b4bd08149ccc" + +ngx-clipboard@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/ngx-clipboard/-/ngx-clipboard-11.1.1.tgz#80225c7de824aef4365a4efab0f49fe7b4fbb55d" + dependencies: + ngx-window-token "^1.0.0" + tslib "^1.9.0" + +ngx-infinite-scroll@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-6.0.1.tgz#571e54860ce32839451569bcf6e7a63cfae327bd" + dependencies: + opencollective "^1.0.3" + +ngx-moment@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ngx-moment/-/ngx-moment-3.0.1.tgz#e90f7bce155c3d7380cb0c4f6075bd61d486c9d4" + dependencies: + tslib "^1.9.0" + +ngx-order-pipe@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ngx-order-pipe/-/ngx-order-pipe-2.0.1.tgz#f8d5c70999df88332436f316f78cf4a50e4c2ba4" + +ngx-window-token@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ngx-window-token/-/ngx-window-token-1.0.0.tgz#12acb174fbbcffa5c60b3fea5a6ea78cc3304793" + dependencies: + tslib "^1.9.0" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + dependencies: + lower-case "^1.1.1" + +node-fetch@1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp@^3.3.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.7.0.tgz#789478e8f6c45e277aa014f3e28f958f286f9203" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request ">=2.9.0 <2.82.0" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.10.0: + version "0.10.2" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.2.tgz#e8945c20ef6795a20aac2b44f036eb13cf5146e3" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.0" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-sass@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.0.tgz#d1b8aa855d98ed684d6848db929a20771cc2ae52" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.10.0" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "~2.79.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-url@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + +npm-packlist@^1.1.6: + version "1.1.10" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-keys@^1.0.11, object-keys@^1.0.8: + version "1.0.12" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +opencollective@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1" + dependencies: + babel-polyfill "6.23.0" + chalk "1.1.3" + inquirer "3.0.6" + minimist "1.2.0" + node-fetch "1.6.3" + opn "4.0.2" + +opener@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + +opn@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +orchestrator@^0.3.0: + version "0.3.8" + resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" + dependencies: + end-of-stream "~0.1.5" + sequencify "~0.0.7" + stream-consume "~0.1.0" + +ordered-read-streams@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.0.0, p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + +pace-progress@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pace-progress/-/pace-progress-1.0.2.tgz#fdc565c57dd91725a3167b360bf2578d3c3b548d" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-filepath@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + dependencies: + path-root-regex "^0.1.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +plugin-error@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" + dependencies: + ansi-cyan "^0.1.1" + ansi-red "^0.1.1" + arr-diff "^1.0.1" + arr-union "^2.0.1" + extend-shallow "^1.1.2" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-colormin@^2.1.8: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + dependencies: + postcss "^5.0.4" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec" + dependencies: + postcss "^5.0.4" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0.1: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-hrtime@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + +primeng@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/primeng/-/primeng-6.0.0.tgz#73a3523098f2b95da0f18d3705d86cf66f6f0d0e" + +private@~0.1.5: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + +proxy-addr@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.6.0" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0, querystring@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +randomatic@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +raw-loader@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +"readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +recast@~0.11.12: + version "0.11.23" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" + dependencies: + ast-types "0.9.6" + esprima "~3.1.0" + private "~0.1.5" + source-map "~0.5.0" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +reflect-metadata@^0.1.12, reflect-metadata@^0.1.2: + version "0.1.12" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" + +regenerate@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + +regenerator-runtime@^0.10.0: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + +"request@>=2.9.0 <2.82.0": + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + +run-sequence@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495" + dependencies: + chalk "^1.1.3" + fancy-log "^1.3.2" + plugin-error "^0.1.2" + +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + +rxjs@^6.0.0, rxjs@^6.1.0, rxjs@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1" + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sass-loader@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.0.3.tgz#6ca10871a1cc7549f8143db5a9958242c4e4ca2a" + dependencies: + clone-deep "^2.0.1" + loader-utils "^1.0.1" + lodash.tail "^4.1.1" + neo-async "^2.5.0" + pify "^3.0.0" + +sax@^1.2.4, sax@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +semver@^4.1.0: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +sequencify@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" + +serialize-javascript@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" + dependencies: + is-extendable "^0.1.1" + kind-of "^5.0.0" + mixin-object "^2.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.0, source-map-support@^0.5.3, source-map-support@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.1.38: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +sparkles@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + safer-buffer "^2.0.2" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + dependencies: + safe-buffer "^5.1.1" + +stack-trace@~0.0.7: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +stdout-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + dependencies: + readable-stream "^2.0.1" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-consume@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48" + +stream-each@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringstream@~0.0.4: + version "0.0.6" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" + dependencies: + first-chunk-stream "^1.0.0" + is-utf8 "^0.2.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +style-loader@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.21.0.tgz#68c52e5eb2afc9ca92b6274be277ee59aea3a852" + dependencies: + loader-utils "^1.1.0" + schema-utils "^0.4.5" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +svgo@^0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.3.1" + js-yaml "~3.7.0" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +tapable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" + +tar@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +tar@^4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +through2@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +through@^2.3.6, through@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tildify@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" + dependencies: + os-homedir "^1.0.0" + +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + dependencies: + setimmediate "^1.0.4" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-string-loader@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/to-string-loader/-/to-string-loader-1.1.5.tgz#7b7aa17891b7bb4947a7a11bfb03b5fde9c6e695" + dependencies: + loader-utils "^0.2.16" + +tough-cookie@~2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tree-kill@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +tryer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + +ts-node@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.0.tgz#a94a13c75e5e1aa6b82814b84c68deb339ba7bff" + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" + +tsickle@^0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.29.0.tgz#812806554bb46c1aa16eb0fe2a051da95ca8f5a4" + dependencies: + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map "^0.6.0" + source-map-support "^0.5.0" + +tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tslint-language-service@^0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/tslint-language-service/-/tslint-language-service-0.9.9.tgz#f546dc38483979e6fb3cfa59584ad8525b3ad4da" + dependencies: + mock-require "^2.0.2" + +tslint@^5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.10.0.tgz#11e26bccb88afa02dd0d9956cae3d4540b5f54c3" + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.12.1" + +tsutils@^2.12.1: + version "2.27.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.1.tgz#ab0276ac23664f36ce8fd4414daec4aebf4373ee" + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-is@~1.6.15, type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +typescript@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" + +uglify-es@^3.3.4, uglify-es@^3.3.9: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + +uglify-js@3.4.x: + version "3.4.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.2.tgz#70511a390eb62423675ba63c374ba1abf045116c" + dependencies: + commander "~2.15.0" + source-map "~0.6.1" + +uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unique-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + +unique-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + +uri-js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-3.0.2.tgz#f90b858507f81dea4dcfbb3c4c3dbfa2b557faaa" + dependencies: + punycode "^2.1.0" + +uri-js@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +url-join@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a" + +url-loader@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.0.1.tgz#61bc53f1f184d7343da2728a1289ef8722ea45ee" + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^0.4.3" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + dependencies: + kind-of "^6.0.2" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + dependencies: + inherits "2.0.3" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +v8-compile-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" + +v8flags@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + dependencies: + user-home "^1.1.1" + +validate-npm-package-license@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + +vendors@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@^0.3.0: + version "0.3.14" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" + dependencies: + defaults "^1.0.0" + glob-stream "^3.1.5" + glob-watcher "^0.0.6" + graceful-fs "^3.0.0" + mkdirp "^0.5.0" + strip-bom "^1.0.0" + through2 "^0.6.1" + vinyl "^0.4.0" + +vinyl@^0.4.0, vinyl@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" + dependencies: + clone "^0.2.0" + clone-stats "^0.0.1" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +webpack-bundle-analyzer@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526" + dependencies: + acorn "^5.3.0" + bfj-node4 "^5.2.0" + chalk "^2.3.0" + commander "^2.13.0" + ejs "^2.5.7" + express "^4.16.2" + filesize "^3.5.11" + gzip-size "^4.1.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + opener "^1.4.3" + ws "^4.0.0" + +webpack-cli@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.8.tgz#90eddcf04a4bfc31aa8c0edc4c76785bc4f1ccd9" + dependencies: + chalk "^2.4.1" + cross-spawn "^6.0.5" + enhanced-resolve "^4.0.0" + global-modules-path "^2.1.0" + import-local "^1.0.0" + inquirer "^6.0.0" + interpret "^1.1.0" + loader-utils "^1.1.0" + supports-color "^5.4.0" + v8-compile-cache "^2.0.0" + yargs "^11.1.0" + +webpack-dev-middleware@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed" + dependencies: + loud-rejection "^1.6.0" + memory-fs "~0.4.1" + mime "^2.1.0" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + url-join "^4.0.0" + webpack-log "^1.0.1" + +webpack-hot-middleware@^2.22.2: + version "2.22.2" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.22.2.tgz#623b77ce591fcd4e1fb99f18167781443e50afac" + dependencies: + ansi-html "0.0.7" + html-entities "^1.2.0" + querystring "^0.2.0" + strip-ansi "^3.0.0" + +webpack-log@^1.0.1, webpack-log@^1.1.2, webpack-log@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" + dependencies: + chalk "^2.1.0" + log-symbols "^2.1.0" + loglevelnext "^1.0.1" + uuid "^3.1.0" + +webpack-merge@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.3.tgz#8aaff2108a19c29849bc9ad2a7fd7fce68e87c4a" + dependencies: + lodash "^4.17.5" + +webpack-node-externals@^1.4.3: + version "1.7.2" + resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz#6e1ee79ac67c070402ba700ef033a9b8d52ac4e3" + +webpack-sources@^1.0.1, webpack-sources@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.14.0.tgz#bbcc40dbf9a34129491b431574189d3802972243" + dependencies: + "@webassemblyjs/ast" "1.5.12" + "@webassemblyjs/helper-module-context" "1.5.12" + "@webassemblyjs/wasm-edit" "1.5.12" + "@webassemblyjs/wasm-opt" "1.5.12" + "@webassemblyjs/wasm-parser" "1.5.12" + acorn "^5.6.2" + acorn-dynamic-import "^3.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^3.7.1" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^0.4.4" + tapable "^1.0.0" + uglifyjs-webpack-plugin "^1.2.4" + watchpack "^1.5.0" + webpack-sources "^1.0.1" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@1, which@^1.2.14, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + dependencies: + errno "~0.1.7" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + +xhr2@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" + +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + dependencies: + camelcase "^4.1.0" + +yargs@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + +zone.js@^0.8.26: + version "0.8.26" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.26.tgz#7bdd72f7668c5a7ad6b118148b4ea39c59d08d2d"