mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
Merge branch 'develop' into mocking
This commit is contained in:
commit
70355e1fe7
195 changed files with 9066 additions and 1589 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -7,7 +7,7 @@
|
|||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
# User-specific files
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
|
@ -252,4 +252,4 @@ _Pvt_Extensions
|
|||
/src/Ombi/healthchecksdb
|
||||
/src/Ombi/ClientApp/package-lock.json
|
||||
/src/Ombi.Core/Properties/launchSettings.json
|
||||
.yarn
|
||||
.yarn
|
||||
|
|
2059
CHANGELOG.md
2059
CHANGELOG.md
File diff suppressed because it is too large
Load diff
148
README.md
148
README.md
|
@ -78,13 +78,6 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Jamie</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Roxedus">
|
||||
<img src="https://avatars.githubusercontent.com/u/7110194?v=4" width="50;" alt="Roxedus"/>
|
||||
<br />
|
||||
<sub><b>Roxedus</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/twanariens">
|
||||
<img src="https://avatars.githubusercontent.com/u/34845004?v=4" width="50;" alt="twanariens"/>
|
||||
|
@ -92,13 +85,6 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Twan Ariens</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Drewster727">
|
||||
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/>
|
||||
<br />
|
||||
<sub><b>Drew</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sephrat">
|
||||
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
|
||||
|
@ -112,8 +98,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Anojh Thayaparan</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Magikarplvl4">
|
||||
<img src="https://avatars.githubusercontent.com/u/2944704?v=4" width="50;" alt="Magikarplvl4"/>
|
||||
|
@ -127,7 +112,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>James Carty</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/smcpeck">
|
||||
<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="50;" alt="smcpeck"/>
|
||||
|
@ -155,8 +141,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Dhruv Bhavsar</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/joshuaboniface">
|
||||
<img src="https://avatars.githubusercontent.com/u/4031396?v=4" width="50;" alt="joshuaboniface"/>
|
||||
|
@ -170,7 +155,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Bruvv</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/louis-lau">
|
||||
<img src="https://avatars.githubusercontent.com/u/1346804?v=4" width="50;" alt="louis-lau"/>
|
||||
|
@ -198,8 +184,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Julien Loir</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ProtoJazz">
|
||||
<img src="https://avatars.githubusercontent.com/u/1490293?v=4" width="50;" alt="ProtoJazz"/>
|
||||
|
@ -213,7 +198,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Avi</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/kitzin">
|
||||
<img src="https://avatars.githubusercontent.com/u/3277321?v=4" width="50;" alt="kitzin"/>
|
||||
|
@ -241,8 +227,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Taylor Buchanan</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/fservida">
|
||||
<img src="https://avatars.githubusercontent.com/u/501958?v=4" width="50;" alt="fservida"/>
|
||||
|
@ -256,7 +241,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Patrick Collins</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/xweskingx">
|
||||
<img src="https://avatars.githubusercontent.com/u/6268446?v=4" width="50;" alt="xweskingx"/>
|
||||
|
@ -284,8 +270,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Stephen Panzer</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/aptalca">
|
||||
<img src="https://avatars.githubusercontent.com/u/541623?v=4" width="50;" alt="aptalca"/>
|
||||
|
@ -299,7 +284,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Dr3amer</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mhann">
|
||||
<img src="https://avatars.githubusercontent.com/u/17162399?v=4" width="50;" alt="mhann"/>
|
||||
|
@ -314,6 +300,13 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Ombi-bot</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Fire-Swan">
|
||||
<img src="https://avatars.githubusercontent.com/u/60622768?v=4" width="50;" alt="Fire-Swan"/>
|
||||
<br />
|
||||
<sub><b>Fire-Swan</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/snyk-bot">
|
||||
<img src="https://avatars.githubusercontent.com/u/19733683?v=4" width="50;" alt="snyk-bot"/>
|
||||
|
@ -327,15 +320,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Andrew Metzger</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/au5ton">
|
||||
<img src="https://avatars.githubusercontent.com/u/4109551?v=4" width="50;" alt="au5ton"/>
|
||||
<br />
|
||||
<sub><b>Austin Jackson</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/D34DC3N73R">
|
||||
<img src="https://avatars.githubusercontent.com/u/9123670?v=4" width="50;" alt="D34DC3N73R"/>
|
||||
|
@ -370,15 +363,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Jack Steel</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jpeters">
|
||||
<img src="https://avatars.githubusercontent.com/u/167401?v=4" width="50;" alt="jpeters"/>
|
||||
<br />
|
||||
<sub><b>Jeffrey Peters</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/MariusSchiffer">
|
||||
<img src="https://avatars.githubusercontent.com/u/183124?v=4" width="50;" alt="MariusSchiffer"/>
|
||||
|
@ -413,14 +406,21 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Javier Pastor</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/AbeKline">
|
||||
<img src="https://avatars.githubusercontent.com/u/8125653?v=4" width="50;" alt="AbeKline"/>
|
||||
<br />
|
||||
<sub><b>Abe Kline</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/aj3x">
|
||||
<img src="https://avatars.githubusercontent.com/u/15078358?v=4" width="50;" alt="aj3x"/>
|
||||
<br />
|
||||
<sub><b>Alexander Russell</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/XanderStrike">
|
||||
|
@ -522,6 +522,13 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Fish2</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Grygon">
|
||||
<img src="https://avatars.githubusercontent.com/u/647846?v=4" width="50;" alt="Grygon"/>
|
||||
<br />
|
||||
<sub><b>Grygon</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ketsapiwiq">
|
||||
<img src="https://avatars.githubusercontent.com/u/26697460?v=4" width="50;" alt="ketsapiwiq"/>
|
||||
|
@ -535,15 +542,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Haries Ramdhani</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/comigor">
|
||||
<img src="https://avatars.githubusercontent.com/u/735858?v=4" width="50;" alt="comigor"/>
|
||||
<br />
|
||||
<sub><b>Igor Borges</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ImgBotApp">
|
||||
<img src="https://avatars.githubusercontent.com/u/31427850?v=4" width="50;" alt="ImgBotApp"/>
|
||||
|
@ -578,15 +585,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Joe Harvey</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jonbloom">
|
||||
<img src="https://avatars.githubusercontent.com/u/492819?v=4" width="50;" alt="jonbloom"/>
|
||||
<br />
|
||||
<sub><b>Jon Bloom</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jonocairns">
|
||||
<img src="https://avatars.githubusercontent.com/u/182836?v=4" width="50;" alt="jonocairns"/>
|
||||
|
@ -608,13 +615,21 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Kyle Lucy</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/janderedev">
|
||||
<img src="https://avatars.githubusercontent.com/u/26145882?v=4" width="50;" alt="janderedev"/>
|
||||
<br />
|
||||
<sub><b>Lea</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Lixumos">
|
||||
<img src="https://avatars.githubusercontent.com/u/29160577?v=4" width="50;" alt="Lixumos"/>
|
||||
<br />
|
||||
<sub><b>Lightkeeper</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Lucane">
|
||||
<img src="https://avatars.githubusercontent.com/u/7999446?v=4" width="50;" alt="Lucane"/>
|
||||
|
@ -628,8 +643,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Madeleine Schönemann</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/marleypowell">
|
||||
<img src="https://avatars.githubusercontent.com/u/55280588?v=4" width="50;" alt="marleypowell"/>
|
||||
|
@ -657,7 +671,8 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Micky</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mvicomoya">
|
||||
<img src="https://avatars.githubusercontent.com/u/24613599?v=4" width="50;" alt="mvicomoya"/>
|
||||
|
@ -671,8 +686,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Nathan Miller</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/cqxmzz">
|
||||
<img src="https://avatars.githubusercontent.com/u/3071863?v=4" width="50;" alt="cqxmzz"/>
|
||||
|
@ -680,13 +694,6 @@ Here are some of the features Ombi has:
|
|||
<sub><b>Qiming Chen</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/randallbruder">
|
||||
<img src="https://avatars.githubusercontent.com/u/6447487?v=4" width="50;" alt="randallbruder"/>
|
||||
<br />
|
||||
<sub><b>Randall Bruder</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rob1998">
|
||||
<img src="https://avatars.githubusercontent.com/u/1560707?v=4" width="50;" alt="rob1998"/>
|
||||
|
@ -707,15 +714,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Sean Callinan</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/shoghicp">
|
||||
<img src="https://avatars.githubusercontent.com/u/516482?v=4" width="50;" alt="shoghicp"/>
|
||||
<br />
|
||||
<sub><b>Shoghi</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Teifun2">
|
||||
<img src="https://avatars.githubusercontent.com/u/7461832?v=4" width="50;" alt="Teifun2"/>
|
||||
|
@ -750,15 +757,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Torkil</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bybeet">
|
||||
<img src="https://avatars.githubusercontent.com/u/1662279?v=4" width="50;" alt="bybeet"/>
|
||||
<br />
|
||||
<sub><b>Travis Bybee</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Xirg">
|
||||
<img src="https://avatars.githubusercontent.com/u/6020502?v=4" width="50;" alt="Xirg"/>
|
||||
|
@ -793,15 +800,15 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Michael DiStaula</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/baikunz">
|
||||
<img src="https://avatars.githubusercontent.com/u/984911?v=4" width="50;" alt="baikunz"/>
|
||||
<br />
|
||||
<sub><b>Dorian ALKOUM</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/echel0n">
|
||||
<img src="https://avatars.githubusercontent.com/u/1128022?v=4" width="50;" alt="echel0n"/>
|
||||
|
@ -836,6 +843,14 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Mkgeeky</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ryan-c44">
|
||||
<img src="https://avatars.githubusercontent.com/u/54028283?v=4" width="50;" alt="ryan-c44"/>
|
||||
<br />
|
||||
<sub><b>Ryan-c44</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sir-marv">
|
||||
|
@ -843,8 +858,7 @@ Here are some of the features Ombi has:
|
|||
<br />
|
||||
<sub><b>Sirmarv</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tdorsey">
|
||||
<img src="https://avatars.githubusercontent.com/u/1218404?v=4" width="50;" alt="tdorsey"/>
|
||||
|
|
1233
assets/Ombi-icon.ai
Normal file
1233
assets/Ombi-icon.ai
Normal file
File diff suppressed because one or more lines are too long
|
@ -56,7 +56,7 @@ namespace Ombi.Api.Emby
|
|||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
|
||||
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri, string clientIpAddress)
|
||||
{
|
||||
var request = new Request("emby/users/authenticatebyname", baseUri, HttpMethod.Post);
|
||||
var body = new
|
||||
|
@ -71,6 +71,11 @@ namespace Ombi.Api.Emby
|
|||
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
if (!string.IsNullOrEmpty(clientIpAddress))
|
||||
{
|
||||
request.AddHeader("X-Forwarded-For", clientIpAddress);
|
||||
}
|
||||
|
||||
var obj = await Api.Request<EmbyUser>(request);
|
||||
return obj;
|
||||
}
|
||||
|
@ -248,5 +253,49 @@ namespace Ombi.Api.Emby
|
|||
req.AddContentHeader("Content-Type", "application/json");
|
||||
req.AddHeader("Device", "Ombi");
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
|
||||
await GetPlayed<EmbyMovie>("Movie", apiKey, userId, baseUri, startIndex, count, parentIdFilder, "ProviderIds");
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
|
||||
await GetPlayed<EmbyEpisodes>("Episode", apiKey, userId, baseUri, startIndex, count, parentIdFilder);
|
||||
|
||||
private async Task<EmbyItemContainer<T>> GetPlayed<T>(
|
||||
string type,
|
||||
string apiKey,
|
||||
string userId,
|
||||
string baseUri,
|
||||
int startIndex,
|
||||
int count,
|
||||
string parentIdFilder = default,
|
||||
string fields = default)
|
||||
{
|
||||
var request = new Request($"emby/items", baseUri, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("Recursive", true.ToString());
|
||||
request.AddQueryString("IncludeItemTypes", type);
|
||||
if (!string.IsNullOrEmpty(fields))
|
||||
{
|
||||
request.AddQueryString("Fields", fields);
|
||||
}
|
||||
request.AddQueryString("UserId", userId);
|
||||
request.AddQueryString("isPlayed", true.ToString());
|
||||
|
||||
// paginate and display recently played items first
|
||||
request.AddQueryString("sortBy", "DatePlayed");
|
||||
request.AddQueryString("SortOrder", "Descending");
|
||||
request.AddQueryString("startIndex", startIndex.ToString());
|
||||
request.AddQueryString("limit", count.ToString());
|
||||
|
||||
if (!string.IsNullOrEmpty(parentIdFilder))
|
||||
{
|
||||
request.AddQueryString("ParentId", parentIdFilder);
|
||||
}
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Ombi.Api.Emby
|
|||
{
|
||||
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
|
||||
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
|
||||
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
|
||||
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri, string clientIpAddress);
|
||||
|
||||
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
@ -32,5 +32,8 @@ namespace Ombi.Api.Emby
|
|||
Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||
|
||||
Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||
}
|
||||
}
|
|
@ -5,30 +5,8 @@ namespace Ombi.Api.Emby.Models.Movie
|
|||
public class EmbyMovie
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public object[] ProductionLocations { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public EmbyUserdata UserData { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public EmbyImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
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; }
|
||||
public EmbyMediastream[] MediaStreams { get; set; }
|
||||
}
|
||||
|
|
|
@ -5,30 +5,8 @@ namespace Ombi.Api.Jellyfin.Models.Movie
|
|||
public class JellyfinMovie
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public object[] ProductionLocations { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public int CriticRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Radarr.Models;
|
||||
using Ombi.Api.Radarr.Models.V3;
|
||||
|
@ -14,7 +15,8 @@ namespace Ombi.Api.Radarr
|
|||
Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl);
|
||||
Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl);
|
||||
Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl);
|
||||
Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
|
||||
Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability, List<int> tags);
|
||||
Task<List<Tag>> GetTags(string apiKey, string baseUrl);
|
||||
Task<Tag> CreateTag(string apiKey, string baseUrl, string tagName);
|
||||
}
|
||||
}
|
|
@ -29,5 +29,6 @@ namespace Ombi.Api.Radarr.Models
|
|||
public int year { get; set; }
|
||||
public string minimumAvailability { get; set; }
|
||||
public long sizeOnDisk { get; set; }
|
||||
public int[] tags { get; set; }
|
||||
}
|
||||
}
|
|
@ -72,7 +72,7 @@ namespace Ombi.Api.Radarr
|
|||
return await Api.Request<MovieResponse>(request);
|
||||
}
|
||||
|
||||
public async Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
|
||||
public async Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability, List<int> tags)
|
||||
{
|
||||
var request = new Request("/api/v3/movie", baseUrl, HttpMethod.Post);
|
||||
|
||||
|
@ -86,7 +86,8 @@ namespace Ombi.Api.Radarr
|
|||
monitored = true,
|
||||
year = year,
|
||||
minimumAvailability = minimumAvailability,
|
||||
sizeOnDisk = 0
|
||||
sizeOnDisk = 0,
|
||||
tags = tags.Any() ? tags.ToArray() : Enumerable.Empty<int>().ToArray()
|
||||
};
|
||||
|
||||
if (searchNow)
|
||||
|
@ -156,5 +157,14 @@ namespace Ombi.Api.Radarr
|
|||
{
|
||||
request.AddHeader("X-Api-Key", key);
|
||||
}
|
||||
|
||||
public Task<Tag> CreateTag(string apiKey, string baseUrl, string tagName)
|
||||
{
|
||||
var request = new Request($"/api/v3/tag", baseUrl, HttpMethod.Post);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(new { Label = tagName });
|
||||
|
||||
return Api.Request<Tag>(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,23 +3,6 @@ namespace Ombi.Api.Sonarr
|
|||
public class SystemStatus
|
||||
{
|
||||
public string version { get; set; }
|
||||
public string 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 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 branch { get; set; }
|
||||
public string authentication { get; set; }
|
||||
public string sqliteVersion { get; set; }
|
||||
public string urlBase { get; set; }
|
||||
public string runtimeVersion { get; set; }
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ namespace Ombi.Core.Tests.Engine
|
|||
_subject = _mocker.CreateInstance<MovieRequestEngine>();
|
||||
var list = DbHelper.GetQueryableMockDbSet(new RequestSubscription());
|
||||
_mocker.Setup<IRepository<RequestSubscription>, IQueryable<RequestSubscription>>(x => x.GetAll()).Returns(new List<RequestSubscription>().AsQueryable().BuildMock());
|
||||
_mocker.Setup<IUserPlayedMovieRepository, IQueryable<UserPlayedMovie>>(x => x.GetAll()).Returns(new List<UserPlayedMovie>().AsQueryable().BuildMock());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -46,8 +46,9 @@ namespace Ombi.Core.Tests.Engine.V2
|
|||
var requestSubs = new Mock<IRepository<RequestSubscription>>();
|
||||
var mediaCache = new Mock<IMediaCacheService>();
|
||||
var featureService = new Mock<IFeatureService>();
|
||||
var userPlayedMovieRepository = new Mock<IUserPlayedMovieRepository>();
|
||||
_engine = new MovieRequestEngine(movieApi.Object, requestService.Object, user.Object, notificationHelper.Object, rules.Object, movieSender.Object,
|
||||
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object);
|
||||
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object, userPlayedMovieRepository.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -173,6 +173,7 @@ namespace Ombi.Core.Tests.Services
|
|||
|
||||
|
||||
[Test]
|
||||
[Ignore("Flaky")]
|
||||
public async Task GetRecentlyRequested_HideUsernames()
|
||||
{
|
||||
_mocker.Setup<ISettingsService<CustomizationSettings>, Task<CustomizationSettings>>(x => x.GetSettingsAsync())
|
||||
|
@ -182,20 +183,25 @@ namespace Ombi.Core.Tests.Services
|
|||
var releaseDate = new DateTime(2019, 01, 01);
|
||||
var requestDate = DateTime.Now;
|
||||
|
||||
var movies = _fixture.CreateMany<MovieRequests>(10);
|
||||
var movies = _fixture.CreateMany<MovieRequests>(10).ToList();
|
||||
var albums = _fixture.CreateMany<AlbumRequest>(10);
|
||||
var chilRequests = _fixture.CreateMany<ChildRequests>(10);
|
||||
movies.Add(_fixture.Build<MovieRequests>().With(x => x.RequestedUserId, "a").With(x => x.Title, "unit").Create());
|
||||
|
||||
_mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(movies.AsQueryable().BuildMock());
|
||||
_mocker.Setup<IMusicRequestRepository, IQueryable<AlbumRequest>>(x => x.GetAll()).Returns(albums.AsQueryable().BuildMock());
|
||||
_mocker.Setup<ITvRequestRepository, IQueryable<ChildRequests>>(x => x.GetChild()).Returns(chilRequests.AsQueryable().BuildMock());
|
||||
_mocker.Setup<ICurrentUser, Task<OmbiUser>>(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "test", Alias = "alias", UserType = UserType.LocalUser });
|
||||
_mocker.Setup<ICurrentUser, Task<OmbiUser>>(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "test", Id = "a", Alias = "alias", UserType = UserType.LocalUser });
|
||||
_mocker.Setup<ICurrentUser, string>(x => x.Username).Returns("test");
|
||||
_mocker.Setup<OmbiUserManager, Task<bool>>(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false);
|
||||
|
||||
var result = await _subject.GetRecentlyRequested(CancellationToken.None);
|
||||
|
||||
CollectionAssert.IsEmpty(result.Where(x => !string.IsNullOrEmpty(x.Username) && !string.IsNullOrEmpty(x.UserId)));
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.Count, Is.EqualTo(1));
|
||||
Assert.That(result.First().Title, Is.EqualTo("unit"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ namespace Ombi.Core.Authentication
|
|||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||
private string _clientIpAddress;
|
||||
public string ClientIpAddress { get => _clientIpAddress; set => _clientIpAddress = value; }
|
||||
|
||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||
{
|
||||
|
@ -88,7 +90,7 @@ namespace Ombi.Core.Authentication
|
|||
}
|
||||
if (user.UserType == UserType.EmbyUser || user.UserType == UserType.EmbyConnectUser)
|
||||
{
|
||||
return await CheckEmbyPasswordAsync(user, password);
|
||||
return await CheckEmbyPasswordAsync(user, password, ClientIpAddress);
|
||||
}
|
||||
if (user.UserType == UserType.JellyfinUser)
|
||||
{
|
||||
|
@ -168,7 +170,7 @@ namespace Ombi.Core.Authentication
|
|||
/// <param name="user"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password)
|
||||
private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password, string clientIpAddress="")
|
||||
{
|
||||
var embySettings = await _embySettings.GetSettingsAsync();
|
||||
var client = _embyApi.CreateClient(embySettings);
|
||||
|
@ -196,7 +198,7 @@ namespace Ombi.Core.Authentication
|
|||
{
|
||||
try
|
||||
{
|
||||
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
|
||||
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri, clientIpAddress);
|
||||
if (result != null)
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -33,7 +33,8 @@ namespace Ombi.Core.Engine
|
|||
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
|
||||
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
|
||||
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
|
||||
IFeatureService featureService)
|
||||
IFeatureService featureService,
|
||||
IUserPlayedMovieRepository userPlayedMovieRepository)
|
||||
: base(user, requestService, r, manager, cache, ombiSettings, sub)
|
||||
{
|
||||
MovieApi = movieApi;
|
||||
|
@ -43,6 +44,7 @@ namespace Ombi.Core.Engine
|
|||
_requestLog = rl;
|
||||
_mediaCacheService = mediaCacheService;
|
||||
_featureService = featureService;
|
||||
_userPlayedMovieRepository = userPlayedMovieRepository;
|
||||
}
|
||||
|
||||
private IMovieDbApi MovieApi { get; }
|
||||
|
@ -52,6 +54,7 @@ namespace Ombi.Core.Engine
|
|||
private readonly IRepository<RequestLog> _requestLog;
|
||||
private readonly IMediaCacheService _mediaCacheService;
|
||||
private readonly IFeatureService _featureService;
|
||||
protected readonly IUserPlayedMovieRepository _userPlayedMovieRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Requests the movie.
|
||||
|
@ -77,7 +80,8 @@ namespace Ombi.Core.Engine
|
|||
var userDetails = await GetUser();
|
||||
var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
|
||||
|
||||
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|
||||
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|
||||
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
|
||||
if (canRequestOnBehalf && !isAdmin)
|
||||
{
|
||||
|
@ -252,7 +256,7 @@ namespace Ombi.Core.Engine
|
|||
var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
await CheckForSubscription(shouldHide.UserId, requests);
|
||||
await FillAdditionalFields(shouldHide, requests);
|
||||
return new RequestsViewModel<MovieRequests>
|
||||
{
|
||||
Collection = requests,
|
||||
|
@ -296,7 +300,7 @@ namespace Ombi.Core.Engine
|
|||
var total = requests.Count();
|
||||
requests = requests.Skip(position).Take(count).ToList();
|
||||
|
||||
await CheckForSubscription(shouldHide.UserId, requests);
|
||||
await FillAdditionalFields(shouldHide, requests);
|
||||
return new RequestsViewModel<MovieRequests>
|
||||
{
|
||||
Collection = requests,
|
||||
|
@ -381,7 +385,7 @@ namespace Ombi.Core.Engine
|
|||
// TODO fix this so we execute this on the server
|
||||
requests = requests.Skip(position).Take(count).ToList();
|
||||
|
||||
await CheckForSubscription(shouldHide.UserId, requests);
|
||||
await FillAdditionalFields(shouldHide, requests);
|
||||
return new RequestsViewModel<MovieRequests>
|
||||
{
|
||||
Collection = requests,
|
||||
|
@ -424,7 +428,7 @@ namespace Ombi.Core.Engine
|
|||
var total = requests.Count();
|
||||
requests = requests.Skip(position).Take(count).ToList();
|
||||
|
||||
await CheckForSubscription(shouldHide.UserId, requests);
|
||||
await FillAdditionalFields(shouldHide, requests);
|
||||
return new RequestsViewModel<MovieRequests>
|
||||
{
|
||||
Collection = requests,
|
||||
|
@ -506,18 +510,25 @@ namespace Ombi.Core.Engine
|
|||
allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide.UserId, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
public async Task<MovieRequests> GetRequest(int requestId)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
// TODO: this query should return the request only if the user is allowed to see it (see shouldHide implementations)
|
||||
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||
await CheckForSubscription((await GetUser()).Id, new List<MovieRequests> { request });
|
||||
await FillAdditionalFields(shouldHide, new List<MovieRequests> { request });
|
||||
|
||||
return request;
|
||||
}
|
||||
private async Task FillAdditionalFields(HideResult shouldHide, List<MovieRequests> requests)
|
||||
{
|
||||
await CheckForSubscription(shouldHide.UserId, requests);
|
||||
await CheckForPlayed(shouldHide, requests);
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(string UserId, List<MovieRequests> movieRequests)
|
||||
{
|
||||
|
@ -543,6 +554,23 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForPlayed(HideResult shouldHide, List<MovieRequests> movieRequests)
|
||||
{
|
||||
var theMovieDbIds = movieRequests.Select(x => x.TheMovieDbId);
|
||||
var plays = await _userPlayedMovieRepository.GetAll().Where(x =>
|
||||
theMovieDbIds.Contains(x.TheMovieDbId))
|
||||
.ToListAsync();
|
||||
foreach (var request in movieRequests)
|
||||
{
|
||||
request.WatchedByRequestedUser = plays.Exists(x => x.TheMovieDbId == request.TheMovieDbId && x.UserId == request.RequestedUserId);
|
||||
|
||||
if (!shouldHide.Hide)
|
||||
{
|
||||
request.PlayedByUsersCount = plays.Count(x => x.TheMovieDbId == request.TheMovieDbId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the movie request.
|
||||
|
@ -563,7 +591,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
|
||||
await CheckForSubscription(shouldHide.UserId, results);
|
||||
await FillAdditionalFields(shouldHide, results);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace Ombi.Core.Engine
|
|||
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
|
||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
|
||||
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
|
||||
IUserPlayedEpisodeRepository userPlayedEpisodeRepository) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
MovieDbApi = movApi;
|
||||
|
@ -44,6 +45,7 @@ namespace Ombi.Core.Engine
|
|||
TvSender = sender;
|
||||
_requestLog = rl;
|
||||
_mediaCacheService = mediaCacheService;
|
||||
_userPlayedEpisodeRepository = userPlayedEpisodeRepository;
|
||||
}
|
||||
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
|
@ -54,6 +56,7 @@ namespace Ombi.Core.Engine
|
|||
private readonly ILogger<TvRequestEngine> _logger;
|
||||
private readonly IRepository<RequestLog> _requestLog;
|
||||
private readonly IMediaCacheService _mediaCacheService;
|
||||
private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepository;
|
||||
|
||||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||
{
|
||||
|
@ -161,7 +164,7 @@ namespace Ombi.Core.Engine
|
|||
var user = await GetUser();
|
||||
var canRequestOnBehalf = tv.RequestOnBehalf.HasValue();
|
||||
|
||||
var isAdmin = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
|
||||
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase) || await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
|
||||
if (tv.RequestOnBehalf.HasValue() && !isAdmin)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
|
@ -292,7 +295,7 @@ namespace Ombi.Core.Engine
|
|||
.Skip(position).Take(count).ToListAsync();
|
||||
|
||||
}
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
{
|
||||
|
@ -328,7 +331,7 @@ namespace Ombi.Core.Engine
|
|||
return new RequestsViewModel<TvRequests>();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
{
|
||||
|
@ -351,7 +354,7 @@ namespace Ombi.Core.Engine
|
|||
allRequests = await TvRepository.Get().ToListAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
|
@ -396,7 +399,7 @@ namespace Ombi.Core.Engine
|
|||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
// Make sure we do not show duplicate child requests
|
||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||
|
@ -469,7 +472,7 @@ namespace Ombi.Core.Engine
|
|||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
// Make sure we do not show duplicate child requests
|
||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||
|
@ -523,7 +526,7 @@ namespace Ombi.Core.Engine
|
|||
allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
|
||||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
// Make sure we do not show duplicate child requests
|
||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||
|
@ -551,7 +554,7 @@ namespace Ombi.Core.Engine
|
|||
allRequests = await TvRepository.GetLite().ToListAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
|
@ -570,7 +573,7 @@ namespace Ombi.Core.Engine
|
|||
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, new List<TvRequests>{request});
|
||||
await FillAdditionalFields(shouldHide, new List<TvRequests>{request});
|
||||
return request;
|
||||
}
|
||||
|
||||
|
@ -624,7 +627,7 @@ namespace Ombi.Core.Engine
|
|||
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, allRequests);
|
||||
await FillAdditionalFields(shouldHide, allRequests);
|
||||
|
||||
return allRequests;
|
||||
}
|
||||
|
@ -643,7 +646,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||
|
||||
await CheckForSubscription(shouldHide, results);
|
||||
await FillAdditionalFields(shouldHide, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
@ -864,14 +867,20 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, List<TvRequests> x)
|
||||
private async Task FillAdditionalFields(HideResult shouldHide, List<TvRequests> x)
|
||||
{
|
||||
foreach (var tvRequest in x)
|
||||
{
|
||||
await CheckForSubscription(shouldHide, tvRequest.ChildRequests);
|
||||
await FillAdditionalFields(shouldHide, tvRequest.ChildRequests);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FillAdditionalFields(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||
{
|
||||
await CheckForSubscription(shouldHide, childRequests);
|
||||
CheckForPlayed(shouldHide, childRequests);
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||
{
|
||||
var sub = _subscriptionRepository.GetAll();
|
||||
|
@ -896,6 +905,52 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private class EpisodeKey
|
||||
{
|
||||
public int SeasonNumber;
|
||||
public int EpisodeNumber;
|
||||
}
|
||||
|
||||
private void CheckForPlayed(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||
{
|
||||
var theMovieDbIds = childRequests.Select(x => x.Id);
|
||||
foreach (var request in childRequests)
|
||||
{
|
||||
var requestedEpisodes = GetEpisodesKeys(request);
|
||||
|
||||
var playedEpisodes = _userPlayedEpisodeRepository
|
||||
.GetAll()
|
||||
.Where(x => x.TheMovieDbId == request.Id && x.UserId == request.RequestedUserId)
|
||||
.AsEnumerable()
|
||||
.Join(requestedEpisodes,
|
||||
played => new { played.SeasonNumber, played.EpisodeNumber },
|
||||
requested => new { requested.SeasonNumber, requested.EpisodeNumber },
|
||||
(played, requested) => new { played });
|
||||
|
||||
var playedCount = playedEpisodes.Count();
|
||||
var toWatchCount = requestedEpisodes.Count();
|
||||
request.RequestedUserPlayedProgress = 100 * playedCount / toWatchCount;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private List<EpisodeKey> GetEpisodesKeys(ChildRequests request)
|
||||
{
|
||||
List<EpisodeKey> result = new List<EpisodeKey>();
|
||||
foreach(var season in request.SeasonRequests)
|
||||
{
|
||||
foreach(var episode in season.Episodes)
|
||||
{
|
||||
result.Add(new EpisodeKey
|
||||
{
|
||||
SeasonNumber = season.SeasonNumber,
|
||||
EpisodeNumber = episode.EpisodeNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile)
|
||||
{
|
||||
// Add the child
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Ombi.Core.Models.Requests
|
|||
public string Overview { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public bool Denied { get; set; }
|
||||
public string MediaId { get; set; }
|
||||
|
||||
public string PosterPath { get; set; }
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
public bool IsValid { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string ExpectedSubDir { get; set; }
|
||||
public string AdditionalInformation { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ using Ombi.Store.Entities;
|
|||
using Ombi.Store.Repository;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Api.Radarr.Models;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Sonarr;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
|
@ -67,7 +69,7 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
if (radarrSettings.Enabled)
|
||||
{
|
||||
return await SendToRadarr(model, is4K, radarrSettings);
|
||||
return await SendToRadarr(model, radarrSettings);
|
||||
}
|
||||
|
||||
var dogSettings = await _dogNzbSettings.GetSettingsAsync();
|
||||
|
@ -131,7 +133,7 @@ namespace Ombi.Core.Senders
|
|||
return await _dogNzbApi.AddMovie(settings.ApiKey, id);
|
||||
}
|
||||
|
||||
private async Task<SenderResult> SendToRadarr(MovieRequests model, bool is4K, RadarrSettings settings)
|
||||
private async Task<SenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings)
|
||||
{
|
||||
var qualityToUse = int.Parse(settings.DefaultQualityProfile);
|
||||
|
||||
|
@ -154,6 +156,17 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
}
|
||||
|
||||
var tags = new List<int>();
|
||||
if (settings.Tag.HasValue)
|
||||
{
|
||||
tags.Add(settings.Tag.Value);
|
||||
}
|
||||
if (settings.SendUserTags)
|
||||
{
|
||||
var userTag = await GetOrCreateTag(model, settings);
|
||||
tags.Add(userTag.id);
|
||||
}
|
||||
|
||||
// Overrides on the request take priority
|
||||
if (model.QualityOverride > 0)
|
||||
{
|
||||
|
@ -174,7 +187,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
var result = await _radarrV3Api.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year,
|
||||
qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly,
|
||||
settings.MinimumAvailability);
|
||||
settings.MinimumAvailability, tags);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Error?.message))
|
||||
{
|
||||
|
@ -212,5 +225,17 @@ namespace Ombi.Core.Senders
|
|||
var selectedPath = paths.FirstOrDefault(x => x.id == overrideId);
|
||||
return selectedPath?.path ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<Tag> GetOrCreateTag(MovieRequests model, RadarrSettings s)
|
||||
{
|
||||
var tagName = model.RequestedUser.UserName;
|
||||
// Does tag exist?
|
||||
|
||||
var allTags = await _radarrV3Api.GetTags(s.ApiKey, s.FullUri);
|
||||
var existingTag = allTags.FirstOrDefault(x => x.label.Equals(tagName, StringComparison.InvariantCultureIgnoreCase));
|
||||
existingTag ??= await _radarrV3Api.CreateTag(s.ApiKey, s.FullUri, tagName);
|
||||
|
||||
return existingTag;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ namespace Ombi.Core.Senders
|
|||
addOptions = new Addoptions
|
||||
{
|
||||
monitored = true,
|
||||
monitor = MonitorTypes.None,
|
||||
monitor = MonitorTypes.Existing,
|
||||
searchForMissingAlbums = false,
|
||||
AlbumsToMonitor = new[] {model.ForeignAlbumId}
|
||||
},
|
||||
|
@ -199,4 +199,4 @@ namespace Ombi.Core.Senders
|
|||
return new SenderResult { Message = "Could not set album to monitored", Sent = false, Success = false };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ namespace Ombi.Core.Services
|
|||
var lang = await DefaultLanguageCode();
|
||||
foreach (var item in await recentMovieRequests.ToListAsync(cancellationToken))
|
||||
{
|
||||
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}movie{item.TheMovieDbId}", () => _movieDbApi.GetMovieImages(item.TheMovieDbId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1));
|
||||
model.Add(new RecentlyRequestedModel
|
||||
{
|
||||
|
@ -84,8 +88,9 @@ namespace Ombi.Core.Services
|
|||
Title = item.Title,
|
||||
Type = RequestType.Movie,
|
||||
Approved = item.Approved,
|
||||
UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
|
||||
Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
|
||||
Denied = item.Denied ?? false,
|
||||
UserId = item.RequestedUserId,
|
||||
Username = item.RequestedUser.UserAlias,
|
||||
MediaId = item.TheMovieDbId.ToString(),
|
||||
PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
|
||||
Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
|
||||
|
@ -94,24 +99,33 @@ namespace Ombi.Core.Services
|
|||
|
||||
foreach (var item in await recentMusicRequests.ToListAsync(cancellationToken))
|
||||
{
|
||||
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
model.Add(new RecentlyRequestedModel
|
||||
{
|
||||
RequestId = item.Id,
|
||||
Available = item.Available,
|
||||
Overview = item.ArtistName,
|
||||
Approved = item.Approved,
|
||||
Denied = item.Denied ?? false,
|
||||
ReleaseDate = item.ReleaseDate,
|
||||
RequestDate = item.RequestedDate,
|
||||
Title = item.Title,
|
||||
Type = RequestType.Album,
|
||||
UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
|
||||
Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
|
||||
UserId = item.RequestedUserId,
|
||||
Username = item.RequestedUser.UserAlias,
|
||||
MediaId = item.ForeignAlbumId,
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var item in await recentTvRequests.ToListAsync(cancellationToken))
|
||||
{
|
||||
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var providerId = item.ParentRequest.ExternalProviderId.ToString();
|
||||
var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}tv{providerId}", () => _movieDbApi.GetTvImages(providerId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1));
|
||||
|
||||
|
@ -123,12 +137,13 @@ namespace Ombi.Core.Services
|
|||
Overview = item.ParentRequest.Overview,
|
||||
ReleaseDate = item.ParentRequest.ReleaseDate,
|
||||
Approved = item.Approved,
|
||||
Denied = item.Denied ?? false,
|
||||
RequestDate = item.RequestedDate,
|
||||
TvPartiallyAvailable = partialAvailability,
|
||||
Title = item.ParentRequest.Title,
|
||||
Type = RequestType.TvShow,
|
||||
UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
|
||||
Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
|
||||
UserId = item.RequestedUserId,
|
||||
Username = item.RequestedUser.UserAlias,
|
||||
MediaId = providerId.ToString(),
|
||||
PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
|
||||
Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
|
||||
|
|
|
@ -197,6 +197,8 @@ namespace Ombi.DependencyInjection
|
|||
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
||||
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
services.AddScoped<IUserPlayedMovieRepository, UserPlayedMovieRepository>();
|
||||
services.AddScoped<IUserPlayedEpisodeRepository, UserPlayedEpisodeRepository>();
|
||||
|
||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
|
||||
|
@ -244,6 +246,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IPlexContentSync, PlexContentSync>();
|
||||
services.AddTransient<IPlexWatchlistImport, PlexWatchlistImport>();
|
||||
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
|
||||
services.AddTransient<IEmbyPlayedSync, EmbyPlayedSync>();
|
||||
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
|
||||
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
||||
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
using HealthChecks.Network;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.HealthChecks.Checks
|
||||
{
|
||||
public class OmbiPingHealthCheck
|
||||
: IHealthCheck
|
||||
{
|
||||
private readonly OmbiPingHealthCheckOptions _options;
|
||||
public OmbiPingHealthCheck(OmbiPingHealthCheckOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var configuredHosts = _options.ConfiguredHosts.Values;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var (host, timeout, status) in configuredHosts)
|
||||
{
|
||||
using (var ping = new Ping())
|
||||
{
|
||||
var pingReply = await ping.SendPingAsync(host, timeout);
|
||||
|
||||
if (pingReply.Status != IPStatus.Success)
|
||||
{
|
||||
return new HealthCheckResult(status, description: $"Ping check for host {host} is failed with status reply:{pingReply.Status}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HealthCheckResult.Healthy();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.HealthChecks.Checks
|
||||
{
|
||||
public class OmbiPingHealthCheckOptions
|
||||
{
|
||||
internal Dictionary<string, (string Host, int TimeOut, HealthStatus status)> ConfiguredHosts { get; } = new Dictionary<string, (string, int, HealthStatus)>();
|
||||
|
||||
public OmbiPingHealthCheckOptions AddHost(string host, int timeout, HealthStatus status)
|
||||
{
|
||||
ConfiguredHosts.Add(host, (host, timeout, status));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ namespace Ombi.HealthChecks.Checks
|
|||
var settings = await settingsProvider.GetSettingsAsync();
|
||||
if (settings == null)
|
||||
{
|
||||
return HealthCheckResult.Healthy("Plex is not confiured.");
|
||||
return HealthCheckResult.Healthy("Plex is not configured.");
|
||||
}
|
||||
|
||||
var taskResult = new List<Task<PlexStatus>>();
|
||||
|
|
|
@ -18,39 +18,8 @@ namespace Ombi.HealthChecks
|
|||
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<CouchPotatoHealthCheck>("CouchPotato", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<SickrageHealthCheck>("SickRage", tags: new string[] { "DVR" });
|
||||
builder.AddOmbiPingHealthCheck(options =>
|
||||
{
|
||||
options.AddHost("www.google.co.uk", 5000, HealthStatus.Unhealthy);
|
||||
options.AddHost("www.google.com", 3000, HealthStatus.Degraded);
|
||||
}, "External Ping", tags: new string[] { "System" });
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a health check for network ping.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
|
||||
/// <param name="setup">The action to configure the ping parameters.</param>
|
||||
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'ping' will be used for the name.</param>
|
||||
/// <param name="failureStatus">
|
||||
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
|
||||
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
|
||||
/// </param>
|
||||
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
|
||||
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
|
||||
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns></param>
|
||||
public static IHealthChecksBuilder AddOmbiPingHealthCheck(this IHealthChecksBuilder builder, Action<OmbiPingHealthCheckOptions> setup, string name = default, HealthStatus? failureStatus = default, IEnumerable<string> tags = default, TimeSpan? timeout = default)
|
||||
{
|
||||
var options = new OmbiPingHealthCheckOptions();
|
||||
setup?.Invoke(options);
|
||||
|
||||
return builder.Add(new HealthCheckRegistration(
|
||||
name,
|
||||
sp => new OmbiPingHealthCheck(options),
|
||||
failureStatus,
|
||||
tags,
|
||||
timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,13 +118,13 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NewAlbums" xml:space="preserve">
|
||||
<value>New Albums</value>
|
||||
<value>Új album</value>
|
||||
</data>
|
||||
<data name="NewMovies" xml:space="preserve">
|
||||
<value>New Movies</value>
|
||||
<value>Új film</value>
|
||||
</data>
|
||||
<data name="NewTV" xml:space="preserve">
|
||||
<value>New TV</value>
|
||||
<value>Új sorozat</value>
|
||||
</data>
|
||||
<data name="GenresLabel" xml:space="preserve">
|
||||
<value>Műfaj:</value>
|
||||
|
@ -139,18 +139,18 @@
|
|||
<value>Epizódok:</value>
|
||||
</data>
|
||||
<data name="PoweredBy" xml:space="preserve">
|
||||
<value>Powered by</value>
|
||||
<value>Biztosítja a(z)</value>
|
||||
</data>
|
||||
<data name="Unsubscribe" xml:space="preserve">
|
||||
<value>Unsubscribe</value>
|
||||
<value>Leiratkozás</value>
|
||||
</data>
|
||||
<data name="Album" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="Movie" xml:space="preserve">
|
||||
<value>Movie</value>
|
||||
<value>Film</value>
|
||||
</data>
|
||||
<data name="TvShow" xml:space="preserve">
|
||||
<value>TV Show</value>
|
||||
<value>Sorozat</value>
|
||||
</data>
|
||||
</root>
|
|
@ -148,9 +148,9 @@
|
|||
<value>Album</value>
|
||||
</data>
|
||||
<data name="Movie" xml:space="preserve">
|
||||
<value>Movie</value>
|
||||
<value>Film</value>
|
||||
</data>
|
||||
<data name="TvShow" xml:space="preserve">
|
||||
<value>TV Show</value>
|
||||
<value>TV-serie</value>
|
||||
</data>
|
||||
</root>
|
|
@ -118,16 +118,16 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NewAlbums" xml:space="preserve">
|
||||
<value>New Albums</value>
|
||||
<value>Nye Album</value>
|
||||
</data>
|
||||
<data name="NewMovies" xml:space="preserve">
|
||||
<value>New Movies</value>
|
||||
<value>Nye filmer</value>
|
||||
</data>
|
||||
<data name="NewTV" xml:space="preserve">
|
||||
<value>New TV</value>
|
||||
<value>Nye TV-serier</value>
|
||||
</data>
|
||||
<data name="GenresLabel" xml:space="preserve">
|
||||
<value>Genres:</value>
|
||||
<value>Sjangere:</value>
|
||||
</data>
|
||||
<data name="AlbumTypeLabel" xml:space="preserve">
|
||||
<value>Type:</value>
|
||||
|
@ -136,21 +136,21 @@
|
|||
<value>Sesong:</value>
|
||||
</data>
|
||||
<data name="EpisodesLabel" xml:space="preserve">
|
||||
<value>Episodes:</value>
|
||||
<value>Episoder:</value>
|
||||
</data>
|
||||
<data name="PoweredBy" xml:space="preserve">
|
||||
<value>Powered by</value>
|
||||
<value>Drevet av</value>
|
||||
</data>
|
||||
<data name="Unsubscribe" xml:space="preserve">
|
||||
<value>Unsubscribe</value>
|
||||
<value>Avslutt abonnement</value>
|
||||
</data>
|
||||
<data name="Album" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="Movie" xml:space="preserve">
|
||||
<value>Movie</value>
|
||||
<value>Film</value>
|
||||
</data>
|
||||
<data name="TvShow" xml:space="preserve">
|
||||
<value>TV Show</value>
|
||||
<value>TV-serie</value>
|
||||
</data>
|
||||
</root>
|
|
@ -107,7 +107,7 @@ namespace Ombi.Notifications.Agents
|
|||
var discordBody = new DiscordWebhookBody
|
||||
{
|
||||
content = model.Message,
|
||||
username = settings.Username,
|
||||
username = settings.Username ?? "Ombi",
|
||||
};
|
||||
|
||||
var fields = new List<DiscordField>();
|
||||
|
|
|
@ -153,6 +153,7 @@ namespace Ombi.Notifications
|
|||
RequestedUser = req?.RequestedUser?.UserName;
|
||||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
DetailsUrl = GetDetailsUrl(s, req);
|
||||
RequestedByAlias = req?.RequestedByAlias;
|
||||
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
|
@ -276,6 +277,7 @@ namespace Ombi.Notifications
|
|||
// User Defined
|
||||
public string RequestId { get; set; }
|
||||
public string RequestedUser { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string IssueUser => UserName;
|
||||
public string Alias { get; set; }
|
||||
|
@ -339,6 +341,7 @@ namespace Ombi.Notifications
|
|||
{ nameof(IssueUser), IssueUser },
|
||||
{ nameof(UserName), UserName },
|
||||
{ nameof(Alias), Alias },
|
||||
{ nameof(RequestedByAlias), RequestedByAlias },
|
||||
{ nameof(UserPreference), UserPreference },
|
||||
{ nameof(DenyReason), DenyReason },
|
||||
{ nameof(AvailableDate), AvailableDate },
|
||||
|
|
|
@ -4,6 +4,8 @@ using Moq.AutoMock;
|
|||
using NUnit.Framework;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Engine;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Models.Requests;
|
||||
|
@ -55,7 +57,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TerminatesWhenWatchlistIsNotEnabled()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = false });
|
||||
await _subject.Execute(null);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
|
@ -145,7 +147,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task NoPlexUsersWithToken()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser>
|
||||
{
|
||||
|
@ -170,7 +172,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MultipleUsers()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser>
|
||||
{
|
||||
|
@ -194,7 +196,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_NoGuid()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -245,7 +247,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoGuid()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -295,7 +297,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_AlreadyRequested()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -394,7 +396,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_NoTmdbGuid()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -433,6 +435,7 @@ namespace Ombi.Schedule.Tests
|
|||
});
|
||||
_mocker.Setup<IMovieRequestEngine, Task<RequestEngineResult>>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()))
|
||||
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
|
||||
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
@ -441,10 +444,195 @@ namespace Ombi.Schedule.Tests
|
|||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MovieRequestFromWatchList_NoTmdbGuid_LookupFromTdb()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlist
|
||||
{
|
||||
Metadata = new List<Metadata>
|
||||
{
|
||||
new Metadata
|
||||
{
|
||||
type = "movie",
|
||||
ratingKey = "abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PlexWatchlistMetadataContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlistMetadata
|
||||
{
|
||||
Metadata = new WatchlistMetadata[]
|
||||
{
|
||||
new WatchlistMetadata
|
||||
{
|
||||
Guid = new List<PlexGuids>
|
||||
{
|
||||
new PlexGuids
|
||||
{
|
||||
Id = "imdb://123"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
_mocker.Setup<IMovieRequestEngine, Task<RequestEngineResult>>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()))
|
||||
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
|
||||
_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.imdb_id)).ReturnsAsync(new FindResult
|
||||
{
|
||||
movie_results = new Movie_Results[]
|
||||
{
|
||||
new Movie_Results
|
||||
{
|
||||
id = 333
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.Is<MovieRequestViewModel>(x => x.TheMovieDbId == 333)), Times.Once);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
|
||||
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.imdb_id), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoTmdbGuid_LookupFromTdb()
|
||||
{
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, MonitorAll = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlist
|
||||
{
|
||||
Metadata = new List<Metadata>
|
||||
{
|
||||
new Metadata
|
||||
{
|
||||
type = "show",
|
||||
ratingKey = "abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PlexWatchlistMetadataContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlistMetadata
|
||||
{
|
||||
Metadata = new WatchlistMetadata[]
|
||||
{
|
||||
new WatchlistMetadata
|
||||
{
|
||||
Guid = new List<PlexGuids>
|
||||
{
|
||||
new PlexGuids
|
||||
{
|
||||
Id = "imdbid://123"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.imdb_id)).ReturnsAsync(new FindResult
|
||||
{
|
||||
tv_results = new TvResults[]
|
||||
{
|
||||
new TvResults
|
||||
{
|
||||
id = 333
|
||||
}
|
||||
}
|
||||
});
|
||||
_mocker.Setup<ITvRequestEngine, Task<RequestEngineResult>>(x => x.RequestTvShow(It.IsAny<TvRequestViewModelV2>()))
|
||||
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<ITvRequestEngine>(x => x.RequestTvShow(It.Is<TvRequestViewModelV2>(x => x.TheMovieDbId == 333 && x.LatestSeason == false && x.RequestAll == true)), Times.Once);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_mocker.Verify<ITvRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
|
||||
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.imdb_id), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoTmdbGuid_LookupFromTdb_ViaTvDb()
|
||||
{
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, MonitorAll = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlist
|
||||
{
|
||||
Metadata = new List<Metadata>
|
||||
{
|
||||
new Metadata
|
||||
{
|
||||
type = "show",
|
||||
ratingKey = "abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PlexWatchlistMetadataContainer
|
||||
{
|
||||
MediaContainer = new PlexWatchlistMetadata
|
||||
{
|
||||
Metadata = new WatchlistMetadata[]
|
||||
{
|
||||
new WatchlistMetadata
|
||||
{
|
||||
Guid = new List<PlexGuids>
|
||||
{
|
||||
new PlexGuids
|
||||
{
|
||||
Id = "thetvdb://123"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.tvdb_id)).ReturnsAsync(new FindResult
|
||||
{
|
||||
tv_results = new TvResults[]
|
||||
{
|
||||
new TvResults
|
||||
{
|
||||
id = 333
|
||||
}
|
||||
}
|
||||
});
|
||||
_mocker.Setup<ITvRequestEngine, Task<RequestEngineResult>>(x => x.RequestTvShow(It.IsAny<TvRequestViewModelV2>()))
|
||||
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<ITvRequestEngine>(x => x.RequestTvShow(It.Is<TvRequestViewModelV2>(x => x.TheMovieDbId == 333 && x.LatestSeason == false && x.RequestAll == true)), Times.Once);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_mocker.Verify<ITvRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
|
||||
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.tvdb_id), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoTmdbGuid()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -494,7 +682,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_AlreadyImported()
|
||||
{
|
||||
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
|
|
@ -79,11 +79,9 @@ namespace Ombi.Schedule.Jobs.Couchpotato
|
|||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM CouchPotatoCache");
|
||||
tran.Commit();
|
||||
}
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM CouchPotatoCache");
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
// Save
|
||||
|
@ -107,13 +105,11 @@ namespace Ombi.Schedule.Jobs.Couchpotato
|
|||
strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.CouchPotatoCache.AddRangeAsync(movieIds);
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.CouchPotatoCache.AddRangeAsync(movieIds);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
}
|
||||
await _ctx.SaveChangesAsync();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
await _notification.SendNotificationToAdmins("Couch Potato Sync Finished");
|
||||
|
|
|
@ -8,10 +8,12 @@ using Ombi.Api.Emby;
|
|||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Emby.Models.Media.Tv;
|
||||
using Ombi.Api.Emby.Models.Movie;
|
||||
using Ombi.Core.Services;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Quartz;
|
||||
|
@ -19,108 +21,43 @@ using MediaType = Ombi.Store.Entities.MediaType;
|
|||
|
||||
namespace Ombi.Schedule.Jobs.Emby
|
||||
{
|
||||
public class EmbyContentSync : IEmbyContentSync
|
||||
public class EmbyContentSync : EmbyLibrarySync, IEmbyContentSync
|
||||
{
|
||||
public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
|
||||
IEmbyContentRepository repo, INotificationHubService notification)
|
||||
public EmbyContentSync(
|
||||
ISettingsService<EmbySettings> settings,
|
||||
IEmbyApiFactory api,
|
||||
ILogger<EmbyContentSync> logger,
|
||||
IEmbyContentRepository repo,
|
||||
INotificationHubService notification,
|
||||
IFeatureService feature):
|
||||
base(settings, api, logger, notification)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
_apiFactory = api;
|
||||
_repo = repo;
|
||||
_notification = notification;
|
||||
_feature = feature;
|
||||
}
|
||||
|
||||
private readonly ILogger<EmbyContentSync> _logger;
|
||||
private readonly ISettingsService<EmbySettings> _settings;
|
||||
private readonly IEmbyApiFactory _apiFactory;
|
||||
private readonly IEmbyContentRepository _repo;
|
||||
private readonly INotificationHubService _notification;
|
||||
private readonly IFeatureService _feature;
|
||||
|
||||
private const int AmountToTake = 100;
|
||||
|
||||
private IEmbyApi Api { get; set; }
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
public async override Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
JobDataMap dataMap = context.JobDetail.JobDataMap;
|
||||
var recentlyAddedSearch = false;
|
||||
if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj))
|
||||
{
|
||||
recentlyAddedSearch = Convert.ToBoolean(recentlyAddedObj);
|
||||
}
|
||||
|
||||
var embySettings = await _settings.GetSettingsAsync();
|
||||
if (!embySettings.Enable)
|
||||
return;
|
||||
await base.Execute(context);
|
||||
|
||||
Api = _apiFactory.CreateClient(embySettings);
|
||||
|
||||
await _notification.SendNotificationToAdmins(recentlyAddedSearch ? "Emby Recently Added Started" : "Emby Content Sync Started");
|
||||
|
||||
foreach (var server in embySettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
await StartServerCache(server, recentlyAddedSearch);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _notification.SendNotificationToAdmins("Emby Content Sync Failed");
|
||||
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
|
||||
}
|
||||
}
|
||||
|
||||
await _notification.SendNotificationToAdmins("Emby Content Sync Finished");
|
||||
// Episodes
|
||||
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAdded.ToString() } }));
|
||||
|
||||
|
||||
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAddedSearch.ToString() } }));
|
||||
}
|
||||
|
||||
|
||||
private async Task StartServerCache(EmbyServers server, bool recentlyAdded)
|
||||
{
|
||||
if (!ValidateSettings(server))
|
||||
// Played state
|
||||
var isPlayedSyncEnabled = await _feature.FeatureEnabled(FeatureNames.PlayedSync);
|
||||
if(isPlayedSyncEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (server.EmbySelectedLibraries.Any() && server.EmbySelectedLibraries.Any(x => x.Enabled))
|
||||
{
|
||||
var movieLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "movies");
|
||||
|
||||
foreach (var movieParentIdFilder in movieLibsToFilter)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'");
|
||||
await ProcessMovies(server, recentlyAdded, movieParentIdFilder.Key);
|
||||
}
|
||||
|
||||
var tvLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "tvshows");
|
||||
foreach (var tvParentIdFilter in tvLibsToFilter)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{tvParentIdFilter.Title}'");
|
||||
await ProcessTv(server, recentlyAdded, tvParentIdFilter.Key);
|
||||
}
|
||||
|
||||
|
||||
var mixedLibs = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "mixed");
|
||||
foreach (var m in mixedLibs)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{m.Title}'");
|
||||
await ProcessTv(server, recentlyAdded, m.Key);
|
||||
await ProcessMovies(server, recentlyAdded, m.Key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ProcessMovies(server, recentlyAdded);
|
||||
await ProcessTv(server, recentlyAdded);
|
||||
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyPlayedSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAdded.ToString() } }));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessTv(EmbyServers server, bool recentlyAdded, string parentId = default)
|
||||
|
||||
protected async override Task ProcessTv(EmbyServers server, string parentId = default)
|
||||
{
|
||||
// TV Time
|
||||
var mediaToAdd = new HashSet<EmbyContent>();
|
||||
|
@ -196,7 +133,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
await _repo.AddRange(mediaToAdd);
|
||||
}
|
||||
|
||||
private async Task ProcessMovies(EmbyServers server, bool recentlyAdded, string parentId = default)
|
||||
protected override async Task ProcessMovies(EmbyServers server, string parentId = default)
|
||||
{
|
||||
EmbyItemContainer<EmbyMovie> movies;
|
||||
if (recentlyAdded)
|
||||
|
@ -263,7 +200,12 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
// Check if it exists
|
||||
var existingMovie = await _repo.GetByEmbyId(movieInfo.Id);
|
||||
var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id);
|
||||
if (existingMovie == null && !alreadyGoingToAdd)
|
||||
if (alreadyGoingToAdd)
|
||||
{
|
||||
_logger.LogDebug($"Detected duplicate for {movieInfo.Name}");
|
||||
return;
|
||||
}
|
||||
if (existingMovie == null)
|
||||
{
|
||||
if (!movieInfo.ProviderIds.Any())
|
||||
{
|
||||
|
@ -319,36 +261,6 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
content.Quality = has4K ? null : quality;
|
||||
content.Has4K = has4K;
|
||||
}
|
||||
|
||||
private bool ValidateSettings(EmbyServers server)
|
||||
{
|
||||
if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey))
|
||||
{
|
||||
_logger.LogInformation(LoggingEvents.EmbyContentCacher, $"Server {server?.Name} is not configured correctly");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
//_settings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
146
src/Ombi.Schedule/Jobs/Emby/EmbyLibrarySync.cs
Normal file
146
src/Ombi.Schedule/Jobs/Emby/EmbyLibrarySync.cs
Normal file
|
@ -0,0 +1,146 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Emby
|
||||
{
|
||||
public abstract class EmbyLibrarySync
|
||||
{
|
||||
public EmbyLibrarySync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
|
||||
INotificationHubService notification)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
_apiFactory = api;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
protected readonly ILogger<EmbyContentSync> _logger;
|
||||
protected readonly ISettingsService<EmbySettings> _settings;
|
||||
protected readonly IEmbyApiFactory _apiFactory;
|
||||
protected bool recentlyAdded;
|
||||
protected readonly INotificationHubService _notification;
|
||||
|
||||
protected const int AmountToTake = 100;
|
||||
|
||||
protected IEmbyApi Api { get; set; }
|
||||
|
||||
public virtual async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
|
||||
JobDataMap dataMap = context.MergedJobDataMap;
|
||||
if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj))
|
||||
{
|
||||
recentlyAdded = Convert.ToBoolean(recentlyAddedObj);
|
||||
}
|
||||
|
||||
await _notification.SendNotificationToAdmins(recentlyAdded ? "Emby Recently Added Started" : "Emby Content Sync Started");
|
||||
|
||||
|
||||
var embySettings = await _settings.GetSettingsAsync();
|
||||
if (!embySettings.Enable)
|
||||
return;
|
||||
|
||||
Api = _apiFactory.CreateClient(embySettings);
|
||||
|
||||
foreach (var server in embySettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
await StartServerCache(server);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _notification.SendNotificationToAdmins("Emby Content Sync Failed");
|
||||
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
|
||||
}
|
||||
}
|
||||
|
||||
await _notification.SendNotificationToAdmins("Emby Content Sync Finished");
|
||||
}
|
||||
|
||||
|
||||
private async Task StartServerCache(EmbyServers server)
|
||||
{
|
||||
if (!ValidateSettings(server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (server.EmbySelectedLibraries.Any() && server.EmbySelectedLibraries.Any(x => x.Enabled))
|
||||
{
|
||||
var movieLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "movies");
|
||||
|
||||
foreach (var movieParentIdFilder in movieLibsToFilter)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'");
|
||||
await ProcessMovies(server, movieParentIdFilder.Key);
|
||||
}
|
||||
|
||||
var tvLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "tvshows");
|
||||
foreach (var tvParentIdFilter in tvLibsToFilter)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{tvParentIdFilter.Title}'");
|
||||
await ProcessTv(server, tvParentIdFilter.Key);
|
||||
}
|
||||
|
||||
|
||||
var mixedLibs = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "mixed");
|
||||
foreach (var m in mixedLibs)
|
||||
{
|
||||
_logger.LogInformation($"Scanning Lib '{m.Title}'");
|
||||
await ProcessTv(server, m.Key);
|
||||
await ProcessMovies(server, m.Key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ProcessMovies(server);
|
||||
await ProcessTv(server);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Task ProcessTv(EmbyServers server, string parentId = default);
|
||||
|
||||
protected abstract Task ProcessMovies(EmbyServers server, string parentId = default);
|
||||
|
||||
private bool ValidateSettings(EmbyServers server)
|
||||
{
|
||||
if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey))
|
||||
{
|
||||
_logger.LogInformation(LoggingEvents.EmbyContentCacher, $"Server {server?.Name} is not configured correctly");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
//_settings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
229
src/Ombi.Schedule/Jobs/Emby/EmbyPlayedSync.cs
Normal file
229
src/Ombi.Schedule/Jobs/Emby/EmbyPlayedSync.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Emby.Models.Media.Tv;
|
||||
using Ombi.Api.Emby.Models.Movie;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Emby
|
||||
{
|
||||
public class EmbyPlayedSync : EmbyLibrarySync, IEmbyPlayedSync
|
||||
{
|
||||
public EmbyPlayedSync(
|
||||
ISettingsService<EmbySettings> settings,
|
||||
IEmbyApiFactory api,
|
||||
ILogger<EmbyContentSync> logger,
|
||||
IUserPlayedMovieRepository movieRepo,
|
||||
IUserPlayedEpisodeRepository episodeRepo,
|
||||
IEmbyContentRepository contentRepo,
|
||||
INotificationHubService notification,
|
||||
OmbiUserManager user) : base(settings, api, logger, notification)
|
||||
{
|
||||
_userManager = user;
|
||||
_movieRepo = movieRepo;
|
||||
_contentRepo = contentRepo;
|
||||
_episodeRepo = episodeRepo;
|
||||
}
|
||||
private OmbiUserManager _userManager { get; }
|
||||
|
||||
private readonly IUserPlayedMovieRepository _movieRepo;
|
||||
private readonly IUserPlayedEpisodeRepository _episodeRepo;
|
||||
private readonly IEmbyContentRepository _contentRepo;
|
||||
|
||||
protected async override Task ProcessTv(EmbyServers server, string parentId = default)
|
||||
{
|
||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||
foreach (var user in allUsers)
|
||||
{
|
||||
await ProcessTvUser(server, user, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
protected async override Task ProcessMovies(EmbyServers server, string parentId = default)
|
||||
{
|
||||
|
||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||
foreach (var user in allUsers)
|
||||
{
|
||||
await ProcessMoviesUser(server, user, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ProcessMoviesUser(EmbyServers server, OmbiUser user, string parentId = default)
|
||||
{
|
||||
EmbyItemContainer<EmbyMovie> movies;
|
||||
if (recentlyAdded)
|
||||
{
|
||||
var recentlyAddedAmountToTake = 5; // to be adjusted?
|
||||
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, user.ProviderUserId, server.FullUri);
|
||||
// Setting this so we don't attempt to grab more than we need
|
||||
if (movies.TotalRecordCount > recentlyAddedAmountToTake)
|
||||
{
|
||||
movies.TotalRecordCount = recentlyAddedAmountToTake;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, 0, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||
}
|
||||
var totalCount = movies.TotalRecordCount;
|
||||
var processed = 0;
|
||||
var mediaToAdd = new HashSet<UserPlayedMovie>();
|
||||
|
||||
while (processed < totalCount)
|
||||
{
|
||||
foreach (var movie in movies.Items)
|
||||
{
|
||||
await ProcessMovie(movie, user, mediaToAdd, server);
|
||||
processed++;
|
||||
}
|
||||
|
||||
// Get the next batch
|
||||
// Recently Added should never be checked as the TotalRecords should equal the amount to take
|
||||
if (!recentlyAdded)
|
||||
{
|
||||
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||
}
|
||||
await _movieRepo.AddRange(mediaToAdd);
|
||||
mediaToAdd.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessMovie(EmbyMovie movieInfo, OmbiUser user, ICollection<UserPlayedMovie> content, EmbyServers server)
|
||||
{
|
||||
if (movieInfo.ProviderIds.Tmdb.IsNullOrEmpty())
|
||||
{
|
||||
_logger.LogWarning($"Movie {movieInfo.Name} has no relevant metadata. Skipping.");
|
||||
return;
|
||||
}
|
||||
var userPlayedMovie = new UserPlayedMovie()
|
||||
{
|
||||
TheMovieDbId = int.Parse(movieInfo.ProviderIds.Tmdb),
|
||||
UserId = user.Id
|
||||
};
|
||||
// Check if it exists
|
||||
var existingMovie = await _movieRepo.Get(userPlayedMovie.TheMovieDbId, userPlayedMovie.UserId);
|
||||
var alreadyGoingToAdd = content.Any(x => x.TheMovieDbId == userPlayedMovie.TheMovieDbId && x.UserId == userPlayedMovie.UserId);
|
||||
if (existingMovie == null && !alreadyGoingToAdd)
|
||||
{
|
||||
content.Add(userPlayedMovie);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessTvUser(EmbyServers server, OmbiUser user, string parentId = default)
|
||||
{
|
||||
EmbyItemContainer<EmbyEpisodes> episodes;
|
||||
if (recentlyAdded)
|
||||
{
|
||||
var recentlyAddedAmountToTake = 10; // to be adjusted?
|
||||
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, user.ProviderUserId, server.FullUri);
|
||||
// Setting this so we don't attempt to grab more than we need
|
||||
if (episodes.TotalRecordCount > recentlyAddedAmountToTake)
|
||||
{
|
||||
episodes.TotalRecordCount = recentlyAddedAmountToTake;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, 0, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||
}
|
||||
var totalCount = episodes.TotalRecordCount;
|
||||
var processed = 0;
|
||||
var mediaToAdd = new HashSet<UserPlayedEpisode>();
|
||||
|
||||
while (processed < totalCount)
|
||||
{
|
||||
foreach (var episode in episodes.Items)
|
||||
{
|
||||
await ProcessTv(episode, user, mediaToAdd, server);
|
||||
processed++;
|
||||
}
|
||||
|
||||
// Get the next batch
|
||||
// Recently Added should never be checked as the TotalRecords should equal the amount to take
|
||||
if (!recentlyAdded)
|
||||
{
|
||||
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||
}
|
||||
await _episodeRepo.AddRange(mediaToAdd);
|
||||
mediaToAdd.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ProcessTv(EmbyEpisodes episode, OmbiUser user, ICollection<UserPlayedEpisode> content, EmbyServers server)
|
||||
{
|
||||
|
||||
var parent = await _contentRepo.GetByEmbyId(episode.SeriesId);
|
||||
if (parent == null)
|
||||
{
|
||||
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this",
|
||||
episode.Name);
|
||||
return;
|
||||
}
|
||||
if (parent.TheMovieDbId.IsNullOrEmpty())
|
||||
{
|
||||
_logger.LogWarning($"Episode {episode.Name} is not linked to a TMDB series. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
await AddToContent(content, new UserPlayedEpisode()
|
||||
{
|
||||
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
||||
SeasonNumber = episode.ParentIndexNumber,
|
||||
EpisodeNumber = episode.IndexNumber,
|
||||
UserId = user.Id
|
||||
});
|
||||
|
||||
if (episode.IndexNumberEnd.HasValue && episode.IndexNumberEnd.Value != episode.IndexNumber)
|
||||
{
|
||||
int episodeNumber = episode.IndexNumber;
|
||||
do
|
||||
{
|
||||
_logger.LogDebug($"Multiple-episode file detected. Adding episode ${episodeNumber}");
|
||||
episodeNumber++;
|
||||
|
||||
await AddToContent(content, new UserPlayedEpisode()
|
||||
{
|
||||
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
||||
SeasonNumber = episode.ParentIndexNumber,
|
||||
EpisodeNumber = episodeNumber,
|
||||
UserId = user.Id
|
||||
});
|
||||
|
||||
|
||||
} while (episodeNumber < episode.IndexNumberEnd.Value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddToContent(ICollection<UserPlayedEpisode> content, UserPlayedEpisode episode)
|
||||
{
|
||||
|
||||
// Check if it exists
|
||||
var existingEpisode = await _episodeRepo.Get(episode.TheMovieDbId, episode.SeasonNumber, episode.EpisodeNumber, episode.UserId);
|
||||
var alreadyGoingToAdd = content.Any(x =>
|
||||
x.TheMovieDbId == episode.TheMovieDbId
|
||||
&& x.SeasonNumber == episode.SeasonNumber
|
||||
&& x.EpisodeNumber == episode.EpisodeNumber
|
||||
&& x.UserId == episode.UserId);
|
||||
if (existingEpisode == null && !alreadyGoingToAdd)
|
||||
{
|
||||
content.Add(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
6
src/Ombi.Schedule/Jobs/Emby/IEmbyPlayedSync.cs
Normal file
6
src/Ombi.Schedule/Jobs/Emby/IEmbyPlayedSync.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Ombi.Schedule.Jobs.Emby
|
||||
{
|
||||
public interface IEmbyPlayedSync : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
|
@ -228,7 +228,12 @@ namespace Ombi.Schedule.Jobs.Jellyfin
|
|||
// Check if it exists
|
||||
var existingMovie = await _repo.GetByJellyfinId(movieInfo.Id);
|
||||
var alreadyGoingToAdd = content.Any(x => x.JellyfinId == movieInfo.Id);
|
||||
if (existingMovie == null && !alreadyGoingToAdd)
|
||||
if (alreadyGoingToAdd)
|
||||
{
|
||||
_logger.LogDebug($"Detected duplicate for {movieInfo.Name}");
|
||||
return;
|
||||
}
|
||||
if (existingMovie == null)
|
||||
{
|
||||
if (!movieInfo.ProviderIds.Any())
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM LidarrAlbumCache");
|
||||
tran.Commit();
|
||||
await tran.CommitAsync();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
await _ctx.LidarrAlbumCache.AddRangeAsync(albumCache);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
await tran.CommitAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,11 +52,9 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM LidarrArtistCache");
|
||||
tran.Commit();
|
||||
}
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM LidarrArtistCache");
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
var artistCache = new List<LidarrArtistCache>();
|
||||
|
@ -76,13 +74,11 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.LidarrArtistCache.AddRangeAsync(artistCache);
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.LidarrArtistCache.AddRangeAsync(artistCache);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
}
|
||||
await _ctx.SaveChangesAsync();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,24 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
||||
{
|
||||
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
|
||||
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IJellyfinContentRepository jellyfinRepo,
|
||||
ISettingsService<EmbySettings> embySettings, ISettingsService<JellyfinSettings> jellyfinSettings)
|
||||
public MediaDatabaseRefresh(
|
||||
ISettingsService<PlexSettings> s,
|
||||
ILogger<MediaDatabaseRefresh> log,
|
||||
IPlexContentRepository plexRepo,
|
||||
IEmbyContentRepository embyRepo,
|
||||
IJellyfinContentRepository jellyfinRepo,
|
||||
IUserPlayedMovieRepository userPlayedMovieRepo,
|
||||
IUserPlayedEpisodeRepository userPlayedEpisodeRepo,
|
||||
ISettingsService<EmbySettings> embySettings,
|
||||
ISettingsService<JellyfinSettings> jellyfinSettings)
|
||||
{
|
||||
_plexSettings = s;
|
||||
_log = log;
|
||||
_plexRepo = plexRepo;
|
||||
_embyRepo = embyRepo;
|
||||
_jellyfinRepo = jellyfinRepo;
|
||||
_userPlayedMovieRepo = userPlayedMovieRepo;
|
||||
_userPlayedEpisodeRepo = userPlayedEpisodeRepo;
|
||||
_embySettings = embySettings;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_plexSettings.ClearCache();
|
||||
|
@ -34,6 +43,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly IPlexContentRepository _plexRepo;
|
||||
private readonly IEmbyContentRepository _embyRepo;
|
||||
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||
private readonly IUserPlayedMovieRepository _userPlayedMovieRepo;
|
||||
private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepo;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
|
||||
|
@ -41,6 +52,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
try
|
||||
{
|
||||
await RemovePlayedData();
|
||||
await RemovePlexData();
|
||||
await RemoveEmbyData();
|
||||
await RemoveJellyfinData();
|
||||
|
@ -52,6 +64,23 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
}
|
||||
|
||||
private async Task RemovePlayedData()
|
||||
{
|
||||
try
|
||||
{
|
||||
const string movieSql = "DELETE FROM UserPlayedMovie";
|
||||
await _userPlayedMovieRepo.ExecuteSql(movieSql);
|
||||
|
||||
const string episodeSql = "DELETE FROM UserPlayedEpisode";
|
||||
await _userPlayedEpisodeRepo.ExecuteSql(episodeSql);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Played Data Failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task RemoveEmbyData()
|
||||
{
|
||||
try
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
|
@ -14,6 +16,7 @@ using Ombi.Store.Entities;
|
|||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Quartz;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -30,13 +33,15 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
private readonly IMovieRequestEngine _movieRequestEngine;
|
||||
private readonly ITvRequestEngine _tvRequestEngine;
|
||||
private readonly INotificationHubService _notificationHubService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly Microsoft.Extensions.Logging.ILogger _logger;
|
||||
private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo;
|
||||
private readonly IRepository<PlexWatchlistUserError> _userError;
|
||||
private readonly IMovieDbApi _movieDbApi;
|
||||
|
||||
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
|
||||
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService,
|
||||
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError)
|
||||
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError,
|
||||
IMovieDbApi movieDbApi)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_settings = settings;
|
||||
|
@ -47,6 +52,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
_logger = logger;
|
||||
_watchlistRepo = watchlistRepo;
|
||||
_userError = userError;
|
||||
_movieDbApi = movieDbApi;
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
|
@ -109,9 +115,16 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
var providerIds = await GetProviderIds(user.MediaServerToken, item, context?.CancellationToken ?? CancellationToken.None);
|
||||
if (!providerIds.TheMovieDb.HasValue())
|
||||
{
|
||||
_logger.LogWarning($"No TheMovieDb Id found for {item.title}, could not import via Plex WatchList");
|
||||
// We need a MovieDbId to support this;
|
||||
continue;
|
||||
// Try and use another Id to figure out TheMovieDB
|
||||
var movieDbId = await FindTmdbIdFromAlternateSources(providerIds, item.type);
|
||||
if (string.IsNullOrEmpty(movieDbId))
|
||||
{
|
||||
_logger.LogWarning($"No TheMovieDb Id found for {item.title} for user {user.UserName}, could not import via Plex WatchList");
|
||||
// We need a MovieDbId to support this;
|
||||
continue;
|
||||
}
|
||||
|
||||
providerIds.TheMovieDb = movieDbId;
|
||||
}
|
||||
|
||||
// Check to see if we have already imported this item
|
||||
|
@ -143,6 +156,43 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
await NotifyClient("Finished Watchlist Import");
|
||||
}
|
||||
|
||||
private async Task<string> FindTmdbIdFromAlternateSources(ProviderId providerId, string type)
|
||||
{
|
||||
FindResult result = null;
|
||||
var hasResult = false;
|
||||
var movie = type == "movie";
|
||||
if (!string.IsNullOrEmpty(providerId.TheTvDb))
|
||||
{
|
||||
result = await _movieDbApi.Find(providerId.TheTvDb, ExternalSource.tvdb_id);
|
||||
hasResult = movie ? result?.movie_results?.Length > 0 : result?.tv_results?.Length > 0;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(providerId.ImdbId) && !hasResult)
|
||||
{
|
||||
result = await _movieDbApi.Find(providerId.ImdbId, ExternalSource.imdb_id);
|
||||
if (movie)
|
||||
{
|
||||
hasResult = result?.movie_results?.Length > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasResult = result?.tv_results?.Length > 0;
|
||||
}
|
||||
}
|
||||
if (hasResult)
|
||||
{
|
||||
if (movie)
|
||||
{
|
||||
return result.movie_results?[0]?.id.ToString() ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return result.tv_results?[0]?.id.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async Task ProcessMovie(int theMovieDbId, OmbiUser user)
|
||||
{
|
||||
_movieRequestEngine.SetUser(user);
|
||||
|
|
|
@ -40,18 +40,14 @@ namespace Ombi.Schedule.Jobs.Radarr
|
|||
{
|
||||
try
|
||||
{
|
||||
var strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM RadarrCache");
|
||||
tran.Commit();
|
||||
});
|
||||
// Let's remove the old cached data
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM RadarrCache");
|
||||
await tran.CommitAsync();
|
||||
|
||||
var radarrSettings = _radarrSettings.GetSettingsAsync();
|
||||
var radarr4kSettings = _radarr4kSettings.GetSettingsAsync();
|
||||
await Process(await radarrSettings);
|
||||
var radarr4kSettings = _radarr4kSettings.GetSettingsAsync();
|
||||
await Process(await radarr4kSettings);
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
|
@ -49,11 +49,9 @@ namespace Ombi.Schedule.Jobs.SickRage
|
|||
var strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SickRageCache");
|
||||
tran.Commit();
|
||||
}
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SickRageCache");
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
var entites = ids.Select(id => new SickRageCache { TvDbId = id }).ToList();
|
||||
|
@ -84,12 +82,10 @@ namespace Ombi.Schedule.Jobs.SickRage
|
|||
strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.SickRageEpisodeCache.AddRangeAsync(episodesToAdd);
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
}
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.SickRageEpisodeCache.AddRangeAsync(episodesToAdd);
|
||||
await _ctx.SaveChangesAsync();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
|
|||
{
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SonarrCache");
|
||||
tran.Commit();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
var sonarrCacheToSave = new HashSet<SonarrCache>();
|
||||
|
@ -97,7 +97,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
|
|||
{
|
||||
using var tran = await _ctx.Database.BeginTransactionAsync();
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SonarrEpisodeCache");
|
||||
tran.Commit();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
|
||||
foreach (var s in ids)
|
||||
|
@ -156,7 +156,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
|
|||
await _ctx.SonarrEpisodeCache.AddRangeAsync(episodesToAdd);
|
||||
_log.LogDebug("Commiting the transaction");
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
await tran.CommitAsync();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace Ombi.Schedule
|
|||
await OmbiQuartz.Instance.AddJob<IEmbyContentSync>(nameof(IEmbyContentSync), "Emby", JobSettingsHelper.EmbyContent(s));
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyContentSync>(nameof(IEmbyContentSync) + "RecentlyAdded", "Emby", JobSettingsHelper.EmbyRecentlyAddedSync(s), new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, "true" } });
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyEpisodeSync>(nameof(IEmbyEpisodeSync), "Emby", null);
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyPlayedSync>(nameof(IEmbyPlayedSync), "Emby", null);
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyUserImporter>(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s));
|
||||
}
|
||||
|
|
|
@ -15,5 +15,6 @@ namespace Ombi.Settings.Settings.Models
|
|||
public bool EnableOAuth { get; set; } // Plex OAuth
|
||||
public bool EnableHeaderAuth { get; set; } // Header SSO
|
||||
public string HeaderAuthVariable { get; set; } // Header SSO
|
||||
public bool HeaderAuthCreateUser { get; set; } // Header SSO
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
public bool AddOnly { get; set; }
|
||||
public string MinimumAvailability { get; set; }
|
||||
public bool ScanForAvailability { get; set; }
|
||||
public int? Tag { get; set; }
|
||||
public bool SendUserTags { get; set; }
|
||||
}
|
||||
|
||||
public class Radarr4KSettings : RadarrSettings
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
public string RootPathAnime { get; set; }
|
||||
public int? AnimeTag { get; set; }
|
||||
public int? Tag { get; set; }
|
||||
public bool SendUserTags { get; set; }
|
||||
public bool AddOnly { get; set; }
|
||||
public int LanguageProfile { get; set; }
|
||||
public int LanguageProfileAnime { get; set; }
|
||||
public bool ScanForAvailability { get; set; }
|
||||
|
||||
public bool SendUserTags { get; set; }
|
||||
}
|
||||
}
|
|
@ -21,5 +21,6 @@ namespace Ombi.Settings.Settings.Models
|
|||
{
|
||||
public const string Movie4KRequests = nameof(Movie4KRequests);
|
||||
public const string OldTrendingSource = nameof(OldTrendingSource);
|
||||
public const string PlayedSync = nameof(PlayedSync);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Ombi.Helpers;
|
||||
using Quartz;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Settings.Settings.Models
|
||||
{
|
||||
|
@ -104,7 +105,9 @@ namespace Ombi.Settings.Settings.Models
|
|||
|
||||
private static string ValidateCron(string cron)
|
||||
{
|
||||
if (CronExpression.IsValidExpression(cron))
|
||||
CronExpression expression = new CronExpression(cron);
|
||||
DateTimeOffset? nextFireUTCTime = expression.GetNextValidTimeAfter(DateTime.Now);
|
||||
if (CronExpression.IsValidExpression(cron) && nextFireUTCTime != null)
|
||||
{
|
||||
return cron;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace Ombi.Store.Context
|
|||
public DbSet<SonarrEpisodeCache> SonarrEpisodeCache { get; set; }
|
||||
public DbSet<SickRageCache> SickRageCache { get; set; }
|
||||
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||
public DbSet<UserPlayedMovie> UserPlayedMovie { get; set; }
|
||||
public DbSet<UserPlayedEpisode> UserPlayedEpisode { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
|
|
@ -61,22 +61,20 @@ namespace Ombi.Store.Context
|
|||
var strat = Database.CreateExecutionStrategy();
|
||||
strat.Execute(() =>
|
||||
{
|
||||
using (var tran = Database.BeginTransaction())
|
||||
using var tran = Database.BeginTransaction();
|
||||
// Make sure we have the API User
|
||||
var apiUserExists = Users.ToList().Any(x => x.NormalizedUserName == "API");
|
||||
if (!apiUserExists)
|
||||
{
|
||||
// Make sure we have the API User
|
||||
var apiUserExists = Users.ToList().Any(x => x.NormalizedUserName == "API");
|
||||
if (!apiUserExists)
|
||||
Users.Add(new OmbiUser
|
||||
{
|
||||
Users.Add(new OmbiUser
|
||||
{
|
||||
UserName = "Api",
|
||||
UserType = UserType.SystemUser,
|
||||
NormalizedUserName = "API",
|
||||
StreamingCountry = "US"
|
||||
});
|
||||
SaveChanges();
|
||||
tran.Commit();
|
||||
}
|
||||
UserName = "Api",
|
||||
UserType = UserType.SystemUser,
|
||||
NormalizedUserName = "API",
|
||||
StreamingCountry = "US"
|
||||
});
|
||||
SaveChanges();
|
||||
tran.Commit();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -233,11 +231,9 @@ namespace Ombi.Store.Context
|
|||
{
|
||||
strat.Execute(() =>
|
||||
{
|
||||
using (var tran = Database.BeginTransaction())
|
||||
{
|
||||
SaveChanges();
|
||||
tran.Commit();
|
||||
}
|
||||
using var tran = Database.BeginTransaction();
|
||||
SaveChanges();
|
||||
tran.Commit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,9 @@ namespace Ombi.Store.Entities.Requests
|
|||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public int RequestedUserPlayedProgress { get; set; }
|
||||
}
|
||||
|
||||
public enum SeriesType
|
||||
|
|
|
@ -84,5 +84,10 @@ namespace Ombi.Store.Entities.Requests
|
|||
|
||||
[NotMapped]
|
||||
public override bool CanApprove => !Approved && !Available || !Approved4K && !Available4K;
|
||||
|
||||
[NotMapped]
|
||||
public bool WatchedByRequestedUser { get; set; }
|
||||
[NotMapped]
|
||||
public int PlayedByUsersCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
10
src/Ombi.Store/Entities/UserPlayedEpisode.cs
Normal file
10
src/Ombi.Store/Entities/UserPlayedEpisode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Store.Entities
|
||||
{
|
||||
public class UserPlayedEpisode : Entity
|
||||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Store/Entities/UserPlayedMovie.cs
Normal file
8
src/Ombi.Store/Entities/UserPlayedMovie.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Store.Entities
|
||||
{
|
||||
public class UserPlayedMovie : Entity
|
||||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
}
|
566
src/Ombi.Store/Migrations/ExternalMySql/20230406152218_MovieUserPlayed.Designer.cs
generated
Normal file
566
src/Ombi.Store/Migrations/ExternalMySql/20230406152218_MovieUserPlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,566 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.MySql;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
[DbContext(typeof(ExternalMySqlContext))]
|
||||
[Migration("20230406152218_MovieUserPlayed")]
|
||||
partial class MovieUserPlayed
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.9")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("decimal(65,30)");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("GrandparentKey")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PlexContentId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SeasonKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TmdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexWatchlistHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("HasRegular")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("MovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
public partial class MovieUserPlayed : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserPlayedMovie",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
TheMovieDbId = table.Column<int>(type: "int", nullable: false),
|
||||
UserId = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserPlayedMovie", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserPlayedMovie");
|
||||
}
|
||||
}
|
||||
}
|
589
src/Ombi.Store/Migrations/ExternalMySql/20230515182204_MovieEpisodePlayed.Designer.cs
generated
Normal file
589
src/Ombi.Store/Migrations/ExternalMySql/20230515182204_MovieEpisodePlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,589 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.MySql;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
[DbContext(typeof(ExternalMySqlContext))]
|
||||
[Migration("20230515182204_MovieEpisodePlayed")]
|
||||
partial class MovieEpisodePlayed
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.9")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("decimal(65,30)");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("GrandparentKey")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PlexContentId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SeasonKey")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TmdbId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexWatchlistHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("HasRegular")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("MovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
public partial class MovieEpisodePlayed : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserPlayedEpisode",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
TheMovieDbId = table.Column<int>(type: "int", nullable: false),
|
||||
SeasonNumber = table.Column<int>(type: "int", nullable: false),
|
||||
EpisodeNumber = table.Column<int>(type: "int", nullable: false),
|
||||
UserId = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserPlayedEpisode", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserPlayedEpisode");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.0")
|
||||
.HasAnnotation("ProductVersion", "6.0.9")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
|
@ -488,6 +488,46 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
|
|
564
src/Ombi.Store/Migrations/ExternalSqlite/20230310130339_MovieUserPlayed.Designer.cs
generated
Normal file
564
src/Ombi.Store/Migrations/ExternalSqlite/20230310130339_MovieUserPlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,564 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.Sqlite;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
[DbContext(typeof(ExternalSqliteContext))]
|
||||
[Migration("20230310130339_MovieUserPlayed")]
|
||||
partial class MovieUserPlayed
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("GrandparentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PlexContentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SeasonKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TmdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexWatchlistHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasRegular")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
public partial class MovieUserPlayed : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserPlayedMovie",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
TheMovieDbId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserPlayedMovie", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserPlayedMovie");
|
||||
}
|
||||
}
|
||||
}
|
587
src/Ombi.Store/Migrations/ExternalSqlite/20230515161757_EpisodeUserPlayed.Designer.cs
generated
Normal file
587
src/Ombi.Store/Migrations/ExternalSqlite/20230515161757_EpisodeUserPlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,587 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.Sqlite;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
[DbContext(typeof(ExternalSqliteContext))]
|
||||
[Migration("20230515161757_EpisodeUserPlayed")]
|
||||
partial class EpisodeUserPlayed
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("GrandparentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ParentKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PlexContentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SeasonKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TmdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexWatchlistHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Has4K")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasRegular")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
public partial class EpisodeUserPlayed : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserPlayedEpisode",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
TheMovieDbId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeasonNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
EpisodeNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserPlayedEpisode", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserPlayedEpisode");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.0");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
|
@ -486,6 +486,46 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserPlayedMovie");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
|
|
13
src/Ombi.Store/Repository/IUserPlayedEpisodeRepository.cs
Normal file
13
src/Ombi.Store/Repository/IUserPlayedEpisodeRepository.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public interface IUserPlayedEpisodeRepository : IExternalRepository<UserPlayedEpisode>
|
||||
{
|
||||
Task<UserPlayedEpisode> Get(int theMovieDbId, int seasonNumber, int episodeNumber, string userId);
|
||||
}
|
||||
}
|
13
src/Ombi.Store/Repository/IUserPlayedMovieRepository.cs
Normal file
13
src/Ombi.Store/Repository/IUserPlayedMovieRepository.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public interface IUserPlayedMovieRepository : IExternalRepository<UserPlayedMovie>
|
||||
{
|
||||
Task<UserPlayedMovie> Get(int theMovieDbId, string userId);
|
||||
}
|
||||
}
|
26
src/Ombi.Store/Repository/UserPlayedEpisodeRepository.cs
Normal file
26
src/Ombi.Store/Repository/UserPlayedEpisodeRepository.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public class UserPlayedEpisodeRepository : ExternalRepository<UserPlayedEpisode>, IUserPlayedEpisodeRepository
|
||||
{
|
||||
protected ExternalContext Db { get; }
|
||||
public UserPlayedEpisodeRepository(ExternalContext db) : base(db)
|
||||
{
|
||||
Db = db;
|
||||
}
|
||||
|
||||
public async Task<UserPlayedEpisode> Get(int theMovieDbId, int seasonNumber, int episodeNumber, string userId)
|
||||
{
|
||||
return await Db.UserPlayedEpisode.FirstOrDefaultAsync(x => x.TheMovieDbId == theMovieDbId && x.SeasonNumber == seasonNumber && x.EpisodeNumber == episodeNumber && x.UserId == userId);
|
||||
}
|
||||
}
|
||||
}
|
27
src/Ombi.Store/Repository/UserPlayedMovieRepository.cs
Normal file
27
src/Ombi.Store/Repository/UserPlayedMovieRepository.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public class UserPlayedMovieRepository : ExternalRepository<UserPlayedMovie>, IUserPlayedMovieRepository
|
||||
{
|
||||
protected ExternalContext Db { get; }
|
||||
public UserPlayedMovieRepository(ExternalContext db) : base(db)
|
||||
{
|
||||
Db = db;
|
||||
}
|
||||
|
||||
public async Task<UserPlayedMovie> Get(int theMovieDbId, string userId)
|
||||
{
|
||||
return await Db.UserPlayedMovie.FirstOrDefaultAsync(x => x.TheMovieDbId == theMovieDbId && x.UserId == userId);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
101
src/Ombi.Tests/Middlewear/ApiKeyMiddlewearTests.cs
Normal file
101
src/Ombi.Tests/Middlewear/ApiKeyMiddlewearTests.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Moq.AutoMock;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Test.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Tests.Middlewear
|
||||
{
|
||||
[TestFixture]
|
||||
public class ApiKeyMiddlewearTests
|
||||
{
|
||||
private AutoMocker _mocker;
|
||||
private ApiKeyMiddlewear _subject;
|
||||
private Mock<IServiceProvider> _serviceProviderMock;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_mocker = new AutoMocker();
|
||||
_serviceProviderMock = new Mock<IServiceProvider>();
|
||||
_mocker.Use(_serviceProviderMock);
|
||||
_subject = _mocker.CreateInstance<ApiKeyMiddlewear>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NonApiAccess()
|
||||
{
|
||||
var context = GetContext();
|
||||
context.Request.Path = "/notanapi";
|
||||
await _subject.Invoke(context);
|
||||
|
||||
_mocker.Verify<IServiceProvider>(x => x.GetService(It.IsAny<Type>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ValidateUserAccessToken()
|
||||
{
|
||||
var context = GetContext();
|
||||
context.Request.Path = "/api";
|
||||
context.Request.Headers.Add("UserAccessToken", new Microsoft.Extensions.Primitives.StringValues("test"));
|
||||
var user = new Store.Entities.OmbiUser
|
||||
{
|
||||
UserAccessToken = "test",
|
||||
UserName = "unit test"
|
||||
};
|
||||
var umMock = MockHelper.MockUserManager(new List<Store.Entities.OmbiUser>
|
||||
{
|
||||
user
|
||||
});
|
||||
umMock.Setup(x => x.GetRolesAsync(user)).ReturnsAsync(new List<string> { "Admin" });
|
||||
_mocker.Setup<IServiceProvider, object?>(x => x.GetService(typeof(OmbiUserManager)))
|
||||
.Returns(umMock.Object);
|
||||
|
||||
|
||||
await _subject.Invoke(context);
|
||||
|
||||
_mocker.Verify<IServiceProvider>(x => x.GetService(It.IsAny<Type>()), Times.Once);
|
||||
umMock.Verify(x => x.UpdateAsync(user), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ValidateUserAccessToken_Token_Invalid()
|
||||
{
|
||||
var context = GetContext();
|
||||
context.Request.Path = "/api";
|
||||
context.Request.Headers.Add("UserAccessToken", new Microsoft.Extensions.Primitives.StringValues("invalid"));
|
||||
var user = new Store.Entities.OmbiUser
|
||||
{
|
||||
UserAccessToken = "test",
|
||||
UserName = "unit test"
|
||||
};
|
||||
var umMock = MockHelper.MockUserManager(new List<Store.Entities.OmbiUser>
|
||||
{
|
||||
user
|
||||
});
|
||||
umMock.Setup(x => x.GetRolesAsync(user)).ReturnsAsync(new List<string> { "Admin" });
|
||||
_mocker.Setup<IServiceProvider, object?>(x => x.GetService(typeof(OmbiUserManager)))
|
||||
.Returns(umMock.Object);
|
||||
|
||||
|
||||
await _subject.Invoke(context);
|
||||
|
||||
Assert.That(context.Response.StatusCode, Is.EqualTo(401));
|
||||
umMock.Verify(x => x.UpdateAsync(user), Times.Never);
|
||||
}
|
||||
|
||||
private HttpContext GetContext()
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
context.RequestServices = _serviceProviderMock.Object;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.9" />
|
||||
<PackageReference Include="Moq" Version="4.18.2" />
|
||||
<PackageReference Include="Moq.AutoMock" Version="3.4.0" />
|
||||
<PackageReference Include="Nunit" Version="3.13.3" />
|
||||
<PackageReference Include="Hangfire" Version="1.7.31" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
|
||||
|
@ -18,6 +19,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Test.Common\Ombi.Test.Common.csproj" />
|
||||
<ProjectReference Include="..\Ombi\Ombi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
3
src/Ombi/.vscode/settings.json
vendored
3
src/Ombi/.vscode/settings.json
vendored
|
@ -24,7 +24,8 @@
|
|||
"details",
|
||||
"requests",
|
||||
"sonarr",
|
||||
"plex"
|
||||
"plex",
|
||||
"wizard"
|
||||
],
|
||||
"rpc.enabled": true
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
<style>
|
||||
.test-class {
|
||||
background-color: purple;
|
||||
}
|
||||
<style>
|
||||
.test-class {
|
||||
background-color: purple;
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.json"
|
||||
"src/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
|
|
|
@ -13,26 +13,26 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.1",
|
||||
"@angular/animations": "^15.0.4",
|
||||
"@angular/cdk": "^14.2.7",
|
||||
"@angular/common": "^15.0.1",
|
||||
"@angular/compiler": "^15.0.1",
|
||||
"@angular/core": "^15.0.1",
|
||||
"@angular/forms": "^15.0.1",
|
||||
"@angular/localize": "^15.0.1",
|
||||
"@angular/common": "^15.0.4",
|
||||
"@angular/compiler": "^15.0.4",
|
||||
"@angular/core": "^15.0.4",
|
||||
"@angular/forms": "^15.0.4",
|
||||
"@angular/localize": "^15.0.4",
|
||||
"@angular/material": "^14.2.7",
|
||||
"@angular/platform-browser": "^15.0.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.1",
|
||||
"@angular/platform-server": "^15.0.1",
|
||||
"@angular/router": "^15.0.1",
|
||||
"@angular/platform-browser": "^15.0.4",
|
||||
"@angular/platform-browser-dynamic": "^15.0.4",
|
||||
"@angular/platform-server": "^15.0.4",
|
||||
"@angular/router": "^15.0.4",
|
||||
"@angularclass/hmr": "^3.0.0",
|
||||
"@auth0/angular-jwt": "^5.0.2",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@microsoft/signalr": "^6.0.7",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"@ngx-translate/http-loader": "^7.0.0",
|
||||
"@ngxs/devtools-plugin": "^3.7.3",
|
||||
"@ngxs/store": "^3.7.3",
|
||||
"@ngxs/devtools-plugin": "3.7.3",
|
||||
"@ngxs/store": "3.7.3",
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@yellowspot/ng-truncate": "^2.0.0",
|
||||
"angular-router-loader": "^0.8.5",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"moment": "^2.29.1",
|
||||
"ng2-cookies": "^1.0.12",
|
||||
"ngx-clipboard": "^12.1.0",
|
||||
"ngx-infinite-scroll": "^14.0.0",
|
||||
"ngx-infinite-scroll": "^9.0.0",
|
||||
"ngx-moment": "^3.0.1",
|
||||
"ngx-order-pipe": "^2.2.0",
|
||||
"popper.js": "^1.14.3",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.2",
|
||||
"@angular/cli": "^15.0.2",
|
||||
"@angular/compiler-cli": "^15.0.1",
|
||||
"@angular/compiler-cli": "^15.0.4",
|
||||
"@babel/core": "^7.18.9",
|
||||
"@compodoc/compodoc": "^1.1.19",
|
||||
"@storybook/angular": "^6.5.9",
|
||||
|
@ -69,7 +69,5 @@
|
|||
"protractor": "~5.4.0",
|
||||
"ts-node": "~5.0.1",
|
||||
"tslint": "^5.12.0"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"_id": "ombi@3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { APP_BASE_HREF, CommonModule, PlatformLocation } from "@angular/common";
|
||||
import { CustomPageService, ImageService, RequestService, SettingsService, SonarrService } from "./services";
|
||||
import { CustomPageService, ImageService, LidarrService, RequestService, SettingsService, SonarrService } from "./services";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from "@angular/common/http";
|
||||
import { IdentityService, IssuesService, JobService, MessageService, PlexTvService, SearchService, StatusService } from "./services";
|
||||
|
@ -14,6 +14,7 @@ import { BrowserModule } from "@angular/platform-browser";
|
|||
import { ButtonModule } from "primeng/button";
|
||||
import { CUSTOMIZATION_INITIALIZER } from "./state/customization/customization-initializer";
|
||||
import { SONARR_INITIALIZER } from "./state/sonarr/sonarr-initializer";
|
||||
import { RADARR_INITIALIZER } from "./state/radarr/radarr-initializer";
|
||||
import { ConfirmDialogModule } from "primeng/confirmdialog";
|
||||
import { CookieComponent } from "./auth/cookie.component";
|
||||
import { CookieService } from "ng2-cookies";
|
||||
|
@ -24,6 +25,7 @@ import { DialogModule } from "primeng/dialog";
|
|||
import { FEATURES_INITIALIZER } from "./state/features/features-initializer";
|
||||
import { FeatureState } from "./state/features";
|
||||
import { SonarrSettingsState } from "./state/sonarr";
|
||||
import { RadarrSettingsState } from "./state/radarr";
|
||||
import { JwtModule } from "@auth0/angular-jwt";
|
||||
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
||||
import { LandingPageService } from "./services";
|
||||
|
@ -44,6 +46,7 @@ import { MatNativeDateModule } from '@angular/material/core';
|
|||
import { MatPaginatorI18n } from "./localization/MatPaginatorI18n";
|
||||
import { MatPaginatorIntl } from "@angular/material/paginator";
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
|
@ -148,6 +151,7 @@ export function JwtTokenGetter() {
|
|||
OverlayModule,
|
||||
MatCheckboxModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
JwtModule.forRoot({
|
||||
config: {
|
||||
tokenGetter: JwtTokenGetter,
|
||||
|
@ -162,7 +166,7 @@ export function JwtTokenGetter() {
|
|||
}),
|
||||
SidebarModule,
|
||||
MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, LayoutModule, MatSlideToggleModule,
|
||||
NgxsModule.forRoot([CustomizationState, FeatureState, SonarrSettingsState], {
|
||||
NgxsModule.forRoot([CustomizationState, FeatureState, SonarrSettingsState, RadarrSettingsState], {
|
||||
developmentMode: !environment.production,
|
||||
}),
|
||||
...environment.production ? [] :
|
||||
|
@ -207,10 +211,12 @@ export function JwtTokenGetter() {
|
|||
StorageService,
|
||||
RequestService,
|
||||
SonarrService,
|
||||
LidarrService,
|
||||
SignalRNotificationService,
|
||||
FEATURES_INITIALIZER,
|
||||
SONARR_INITIALIZER,
|
||||
CUSTOMIZATION_INITIALIZER,
|
||||
RADARR_INITIALIZER,
|
||||
{
|
||||
provide: APP_BASE_HREF,
|
||||
useValue: window["baseHref"]
|
||||
|
|
|
@ -37,7 +37,7 @@ export class AuthService extends ServiceHelpers {
|
|||
}
|
||||
|
||||
public loggedIn() {
|
||||
const token: string = this.jwtHelperService.tokenGetter();
|
||||
const token = this.jwtHelperService.tokenGetter() as string;
|
||||
|
||||
if (!token) {
|
||||
return false;
|
||||
|
|
|
@ -22,7 +22,23 @@
|
|||
</div>
|
||||
<div class="row action-items">
|
||||
<div class="col-12" *ngIf="isAdmin">
|
||||
<button *ngIf="!request.approved" id="detailed-request-approve-{{request.mediaId}}" color="accent" mat-raised-button (click)="approve()">{{'Common.Approve' | translate}}</button>
|
||||
<div *ngIf="!request.approved && !request.denied">
|
||||
<button
|
||||
id="detailed-request-approve-{{request.mediaId}}"
|
||||
color="accent"
|
||||
mat-raised-button
|
||||
(click)="approve()"
|
||||
matTooltip="{{'Common.Approve' | translate}}">
|
||||
<mat-icon>check-circle</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
id="detailed-request-deny-{{request.mediaId}}"
|
||||
color="accent"
|
||||
mat-raised-button
|
||||
(click)="deny()"
|
||||
matTooltip="{{'Requests.Deny' | translate}}">
|
||||
<mat-icon>cancel</mat-icon></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -50,5 +50,9 @@
|
|||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.action-items button {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
|
|||
@Input() public isAdmin: boolean = false;
|
||||
@Output() public onClick: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Output() public onApprove: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Output() public onDeny: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
public RequestType = RequestType;
|
||||
public loading: false;
|
||||
|
@ -41,6 +42,9 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
public getStatus(request: IRecentlyRequested) {
|
||||
if (request.denied) {
|
||||
return "Common.Denied";
|
||||
}
|
||||
if (request.available) {
|
||||
return "Common.Available";
|
||||
}
|
||||
|
@ -62,7 +66,14 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
|
|||
this.onApprove.emit();
|
||||
}
|
||||
|
||||
public deny() {
|
||||
this.onDeny.emit();
|
||||
}
|
||||
|
||||
public getClass(request: IRecentlyRequested) {
|
||||
if (request.denied) {
|
||||
return "danger";
|
||||
}
|
||||
if (request.available || request.tvPartiallyAvailable) {
|
||||
return "success";
|
||||
}
|
||||
|
|
|
@ -74,4 +74,4 @@ export class ImageComponent {
|
|||
return this.baseUrl + this.defaultMusic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -157,7 +157,7 @@ export class DiscoverCardComponent implements OnInit {
|
|||
AdminRequestDialogComponent,
|
||||
{
|
||||
width: "700px",
|
||||
data: { type: RequestType.movie, id: this.result.id },
|
||||
data: { type: RequestType.movie, id: this.result.id, is4k: is4k },
|
||||
panelClass: "modal-panel",
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, Input, ViewChild, Output, EventEmitter } from "@angular/core";
|
||||
import { Component, OnInit, Input, ViewChild, Output, EventEmitter, Inject } from "@angular/core";
|
||||
import { DiscoverOption, IDiscoverCardResult } from "../../interfaces";
|
||||
import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces";
|
||||
import { SearchV2Service } from "../../../services";
|
||||
|
@ -6,6 +6,7 @@ import { StorageService } from "../../../shared/storage/storage-service";
|
|||
import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
||||
import { Carousel } from 'primeng/carousel';
|
||||
import { FeaturesFacade } from "../../../state/features/features.facade";
|
||||
import { APP_BASE_HREF } from "@angular/common";
|
||||
|
||||
export enum DiscoverType {
|
||||
Upcoming,
|
||||
|
@ -44,10 +45,18 @@ export class CarouselListComponent implements OnInit {
|
|||
};
|
||||
private amountToLoad = 17;
|
||||
private currentlyLoaded = 0;
|
||||
private baseUrl: string = "";
|
||||
|
||||
|
||||
constructor(private searchService: SearchV2Service,
|
||||
private storageService: StorageService,
|
||||
private featureFacade: FeaturesFacade) {
|
||||
private featureFacade: FeaturesFacade,
|
||||
@Inject(APP_BASE_HREF) private href: string) {
|
||||
|
||||
if (this.href.length > 1) {
|
||||
this.baseUrl = this.href;
|
||||
}
|
||||
|
||||
Carousel.prototype.onTouchMove = () => { },
|
||||
this.responsiveOptions = [
|
||||
{
|
||||
|
@ -294,7 +303,7 @@ export class CarouselListComponent implements OnInit {
|
|||
this.movies.forEach(m => {
|
||||
tempResults.push({
|
||||
available: m.available,
|
||||
posterPath: m.posterPath ? `https://image.tmdb.org/t/p/w500/${m.posterPath}` : "../../../images/default_movie_poster.png",
|
||||
posterPath: m.posterPath ? `https://image.tmdb.org/t/p/w500/${m.posterPath}` : this.baseUrl + "/images/default_movie_poster.png",
|
||||
requested: m.requested,
|
||||
title: m.title,
|
||||
type: RequestType.movie,
|
||||
|
@ -316,7 +325,7 @@ export class CarouselListComponent implements OnInit {
|
|||
this.tvShows.forEach(m => {
|
||||
tempResults.push({
|
||||
available: m.fullyAvailable,
|
||||
posterPath: m.backdropPath ? `https://image.tmdb.org/t/p/w500/${m.backdropPath}` : "../../../images/default_tv_poster.png",
|
||||
posterPath: m.backdropPath ? `https://image.tmdb.org/t/p/w500/${m.backdropPath}` : this.baseUrl + "/images/default_tv_poster.png",
|
||||
requested: m.requested,
|
||||
title: m.title,
|
||||
type: RequestType.tvShow,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<div class="small-middle-container">
|
||||
|
||||
<div class="section">
|
||||
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{'Discovery.Genres' | translate}}</h2>
|
||||
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>{{'Discovery.RecentlyRequestedTab' | translate}}</h2>
|
||||
<div>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<div class="genre-container" *ngIf="movieGenreList$ | async as movies">
|
||||
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of movies"
|
||||
class="discover-filter-buttons-group">
|
||||
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
<div class="genre-container" *ngIf="tvGenreList$ | async as tv">
|
||||
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of tv"
|
||||
class="discover-filter-buttons-group">
|
||||
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
<mat-spinner *ngIf="isLoading" [diameter]="30"></mat-spinner>
|
||||
</div>
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
h2{
|
||||
margin-top:40px;
|
||||
margin-left:40px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.discover-filter-buttons-group {
|
||||
border: 1px solid #293a4c;
|
||||
border-radius: 15px;
|
||||
color:#fff;
|
||||
margin-bottom:5px;
|
||||
margin-right: 5px;
|
||||
|
||||
.discover-filter-button{
|
||||
|
||||
transform: scale(0.9);
|
||||
background:inherit;
|
||||
color:inherit;
|
||||
padding:0 0px;
|
||||
border-radius: 30px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border-left:none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-active{
|
||||
background:#293a4c;
|
||||
}
|
||||
|
||||
.genre-container {
|
||||
margin-left: 35px;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { SearchV2Service } from "../../../services";
|
||||
import { MatButtonToggleChange } from "@angular/material/button-toggle";
|
||||
import { RequestType } from "../../../interfaces";
|
||||
import { AdvancedSearchDialogDataService } from "app/shared/advanced-search-dialog/advanced-search-dialog-data.service";
|
||||
import { Router } from "@angular/router";
|
||||
import { map, Observable } from "rxjs";
|
||||
|
||||
interface IGenreSelect {
|
||||
name: string;
|
||||
id: number;
|
||||
type: "movie"|"tv";
|
||||
}
|
||||
@Component({
|
||||
selector: "genre-button-select",
|
||||
templateUrl: "./genre-button-select.component.html",
|
||||
styleUrls: ["./genre-button-select.component.scss"],
|
||||
})
|
||||
export class GenreButtonSelectComponent implements OnInit {
|
||||
public movieGenreList$: Observable<IGenreSelect[]> = null;
|
||||
public tvGenreList$: Observable<IGenreSelect[]> = null;
|
||||
|
||||
isLoading: boolean = false;
|
||||
|
||||
constructor(private searchService: SearchV2Service,
|
||||
private advancedSearchService: AdvancedSearchDialogDataService,
|
||||
private router: Router) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.movieGenreList$ = this.searchService.getGenres("movie").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "movie" }))));
|
||||
this.tvGenreList$ = this.searchService.getGenres("tv").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "tv" }))));
|
||||
}
|
||||
|
||||
public async toggleChanged(event: MatButtonToggleChange, type: "movie"|"tv") {
|
||||
this.isLoading = true;
|
||||
|
||||
const genres: number[] = [event.value];
|
||||
const data = await this.searchService.advancedSearch({
|
||||
watchProviders: [],
|
||||
genreIds: genres,
|
||||
keywordIds: [],
|
||||
type: type,
|
||||
}, 0, 30);
|
||||
|
||||
this.advancedSearchService.setData(data, type == "movie" ? RequestType.movie : RequestType.tvShow);
|
||||
this.advancedSearchService.setOptions([], genres, [], null, type == "movie" ? RequestType.movie : RequestType.tvShow, 30);
|
||||
this.router.navigate([`discover/advanced/search`]);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import { MatDialog } from "@angular/material/dialog";
|
|||
import { RequestServiceV2 } from "../../services/requestV2.service";
|
||||
import { Routes } from "@angular/router";
|
||||
import { DetailedCardComponent } from "app/components";
|
||||
import { GenreButtonSelectComponent } from "./genre/genre-button-select.component";
|
||||
|
||||
export const components: any[] = [
|
||||
DiscoverComponent,
|
||||
|
@ -22,6 +23,7 @@ export const components: any[] = [
|
|||
CarouselListComponent,
|
||||
RecentlyRequestedListComponent,
|
||||
DetailedCardComponent,
|
||||
GenreButtonSelectComponent
|
||||
];
|
||||
|
||||
export const providers: any[] = [
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
<p-carousel #carousel [value]="requests" [numVisible]="3" [numScroll]="1"
|
||||
[responsiveOptions]="responsiveOptions" [page]="0">
|
||||
<ng-template let-result pTemplate="item">
|
||||
<ombi-detailed-card [request]="result" [isAdmin]="isAdmin" (onClick)="navigate(result)"
|
||||
(onApprove)="approve(result)"></ombi-detailed-card>
|
||||
<ombi-detailed-card
|
||||
[request]="result"
|
||||
[isAdmin]="isAdmin"
|
||||
(onClick)="navigate(result)"
|
||||
(onApprove)="approve(result)"
|
||||
(onDeny)="deny(result)">
|
||||
</ombi-detailed-card>
|
||||
</ng-template>
|
||||
</p-carousel>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,8 @@ import { Router } from "@angular/router";
|
|||
import { AuthService } from "app/auth/auth.service";
|
||||
import { NotificationService, RequestService } from "app/services";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { DenyDialogComponent } from '../../../media-details/components/shared/deny-dialog/deny-dialog.component';
|
||||
import { MatDialog } from "@angular/material/dialog";
|
||||
|
||||
export enum DiscoverType {
|
||||
Upcoming,
|
||||
|
@ -42,7 +44,8 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
|
|||
private router: Router,
|
||||
private authService: AuthService,
|
||||
private notificationService: NotificationService,
|
||||
private translateService: TranslateService) {
|
||||
private translateService: TranslateService,
|
||||
public dialog: MatDialog) {
|
||||
Carousel.prototype.onTouchMove = () => {};
|
||||
this.responsiveOptions = ResponsiveOptions;
|
||||
}
|
||||
|
@ -81,6 +84,20 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
public deny(request: IRecentlyRequested) {
|
||||
const dialogRef = this.dialog.open(DenyDialogComponent, {
|
||||
width: '250px',
|
||||
data: { requestId: request.requestId, is4K: false, requestType: request.type }
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.notificationService.success(this.translateService.instant("Requests.SuccessfullyDenied"));
|
||||
request.denied = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private handleApproval(result: IRequestEngineResult, request: IRecentlyRequested) {
|
||||
if (result.result) {
|
||||
this.notificationService.success(this.translateService.instant("Requests.SuccessfullyApproved"));
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[infiniteScrollDistance]="3"
|
||||
[infiniteScrollThrottle]="200"
|
||||
(scrolled)="onScroll()">
|
||||
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
|
||||
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding card-container" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
|
||||
<discover-card [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
|
||||
.loading-spinner {
|
||||
margin: 10%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export interface IRecentlyRequested {
|
|||
overview: string;
|
||||
releaseDate: Date;
|
||||
approved: boolean;
|
||||
denied: boolean;
|
||||
mediaId: string;
|
||||
type: RequestType;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ export interface IMovieRequests extends IFullBaseRequest {
|
|||
deniedReason4K: string;
|
||||
requestedDate4k: Date;
|
||||
requestedDate: Date;
|
||||
watchedByRequestedUser: boolean;
|
||||
playedByUsersCount: number;
|
||||
|
||||
// For the UI
|
||||
rootPathOverrideTitle: string;
|
||||
|
@ -130,6 +132,7 @@ export interface ITvRequests {
|
|||
background: any;
|
||||
totalSeasons: number;
|
||||
tvDbId: number; // NO LONGER USED
|
||||
requestedUserPlayedProgress: number;
|
||||
|
||||
open: boolean; // THIS IS FOR THE UI
|
||||
|
||||
|
@ -212,4 +215,4 @@ export class BaseRequestOptions {
|
|||
requestOnBehalf: string | undefined;
|
||||
rootFolderOverride: number | undefined;
|
||||
qualityPathOverride: number | undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,6 +160,8 @@ export interface IRadarrSettings extends IExternalSettings {
|
|||
addOnly: boolean;
|
||||
minimumAvailability: string;
|
||||
scanForAvailability: boolean;
|
||||
tag: number | null;
|
||||
sendUserTags: boolean;
|
||||
}
|
||||
|
||||
export interface IRadarrCombined {
|
||||
|
@ -245,6 +247,7 @@ export interface IAuthenticationSettings extends ISettings {
|
|||
enableOAuth: boolean;
|
||||
enableHeaderAuth: boolean;
|
||||
headerAuthVariable: string;
|
||||
headerAuthCreateUser: boolean;
|
||||
}
|
||||
|
||||
export interface ICustomPage extends ISettings {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue