Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
659fb65256 | ||
|
|
2d36e7aa6e | ||
|
|
a19d972ea2 | ||
|
|
b3d4806809 |
@ -68,11 +68,8 @@ namespace Lampac.Controllers
|
|||||||
#region migration storage to sql
|
#region migration storage to sql
|
||||||
if (AppInit.conf.sync_user.version != 1 && !string.IsNullOrEmpty(requestInfo.user_uid))
|
if (AppInit.conf.sync_user.version != 1 && !string.IsNullOrEmpty(requestInfo.user_uid))
|
||||||
{
|
{
|
||||||
string profile_id = getProfileid(requestInfo, HttpContext);
|
string profileId = getProfileid(requestInfo, HttpContext);
|
||||||
string id = requestInfo.user_uid + profile_id;
|
var storageFile = StorageManager.GetFilePath("sync_favorite", false, requestInfo.user_uid, profileId);
|
||||||
|
|
||||||
string md5key = AppInit.conf.storage.md5name ? CrypTo.md5(id) : Regex.Replace(id, "[^a-z0-9\\-]", "");
|
|
||||||
string storageFile = $"database/storage/sync_favorite/{md5key.Substring(0, 2)}/{md5key.Substring(2)}";
|
|
||||||
|
|
||||||
if (System.IO.File.Exists(storageFile) && !System.IO.File.Exists($"{storageFile}.migration"))
|
if (System.IO.File.Exists(storageFile) && !System.IO.File.Exists($"{storageFile}.migration"))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@ -9,7 +8,6 @@ using Shared.Engine.Utilities;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using IO = System.IO;
|
using IO = System.IO;
|
||||||
@ -18,14 +16,6 @@ namespace Lampac.Controllers
|
|||||||
{
|
{
|
||||||
public class StorageController : BaseController
|
public class StorageController : BaseController
|
||||||
{
|
{
|
||||||
#region StorageController
|
|
||||||
static StorageController()
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory("database/storage");
|
|
||||||
Directory.CreateDirectory("database/storage/temp");
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region backup.js
|
#region backup.js
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
@ -83,7 +73,7 @@ namespace Lampac.Controllers
|
|||||||
if (!AppInit.conf.storage.enable)
|
if (!AppInit.conf.storage.enable)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"disabled\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"disabled\"}");
|
||||||
|
|
||||||
string outFile = getFilePath(path, pathfile, false);
|
string outFile = StorageManager.GetFilePath(path, false, requestInfo, pathfile);
|
||||||
if (outFile == null || !IO.File.Exists(outFile))
|
if (outFile == null || !IO.File.Exists(outFile))
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
||||||
|
|
||||||
@ -135,7 +125,7 @@ namespace Lampac.Controllers
|
|||||||
if (HttpContext.Request.ContentLength > AppInit.conf.storage.max_size)
|
if (HttpContext.Request.ContentLength > AppInit.conf.storage.max_size)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"max_size\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"max_size\"}");
|
||||||
|
|
||||||
string outFile = getFilePath(path, pathfile, true);
|
string outFile = StorageManager.GetFilePath(path, true, requestInfo, pathfile);
|
||||||
if (outFile == null)
|
if (outFile == null)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
||||||
|
|
||||||
@ -165,8 +155,10 @@ namespace Lampac.Controllers
|
|||||||
await semaphore.WaitAsync();
|
await semaphore.WaitAsync();
|
||||||
|
|
||||||
using (var fileStream = new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None, PoolInvk.bufferSize))
|
using (var fileStream = new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None, PoolInvk.bufferSize))
|
||||||
|
{
|
||||||
await memoryStream.CopyToAsync(fileStream, PoolInvk.bufferSize);
|
await memoryStream.CopyToAsync(fileStream, PoolInvk.bufferSize);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
HttpContext.Response.StatusCode = 503;
|
HttpContext.Response.StatusCode = 503;
|
||||||
@ -216,7 +208,7 @@ namespace Lampac.Controllers
|
|||||||
if (!AppInit.conf.storage.enable)
|
if (!AppInit.conf.storage.enable)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"disabled\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"disabled\"}");
|
||||||
|
|
||||||
string outFile = getFilePath("temp", null, false, user_uid: key);
|
string outFile = StorageManager.GetFilePath("temp", false, key);
|
||||||
if (outFile == null || !IO.File.Exists(outFile))
|
if (outFile == null || !IO.File.Exists(outFile))
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
||||||
|
|
||||||
@ -268,7 +260,7 @@ namespace Lampac.Controllers
|
|||||||
if (HttpContext.Request.ContentLength > AppInit.conf.storage.max_size)
|
if (HttpContext.Request.ContentLength > AppInit.conf.storage.max_size)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"max_size\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"max_size\"}");
|
||||||
|
|
||||||
string outFile = getFilePath("temp", null, true, user_uid: key);
|
string outFile = StorageManager.GetFilePath("temp", true, key);
|
||||||
if (outFile == null)
|
if (outFile == null)
|
||||||
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
return ContentTo("{\"success\": false, \"msg\": \"outFile\"}");
|
||||||
|
|
||||||
@ -322,35 +314,5 @@ namespace Lampac.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region getFilePath
|
|
||||||
string getFilePath(string path, string pathfile, bool createDirectory, string user_uid = null)
|
|
||||||
{
|
|
||||||
if (path == "temp" && string.IsNullOrEmpty(user_uid))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
path = Regex.Replace(path, "[^a-z0-9\\-]", "", RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
string id = user_uid ?? requestInfo.user_uid;
|
|
||||||
if (string.IsNullOrEmpty(id))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
id += pathfile;
|
|
||||||
string md5key = AppInit.conf.storage.md5name ? CrypTo.md5(id) : Regex.Replace(id, "[^a-z0-9\\-]", "");
|
|
||||||
|
|
||||||
if (path == "temp")
|
|
||||||
{
|
|
||||||
return $"database/storage/{path}/{md5key}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (createDirectory)
|
|
||||||
Directory.CreateDirectory($"database/storage/{path}/{md5key.Substring(0, 2)}");
|
|
||||||
|
|
||||||
return $"database/storage/{path}/{md5key.Substring(0, 2)}/{md5key.Substring(2)}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,14 +51,14 @@ namespace Catalog.Controllers
|
|||||||
|
|
||||||
html = rch.enable
|
html = rch.enable
|
||||||
? await rch.Post(url, init.card_parse.postData, headers, useDefaultHeaders: init.useDefaultHeaders)
|
? await rch.Post(url, init.card_parse.postData, headers, useDefaultHeaders: init.useDefaultHeaders)
|
||||||
: await Http.Post(url, httpdata, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
|
: await Http.Post(url, httpdata, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.GetHttpVersion(), useDefaultHeaders: init.useDefaultHeaders);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
html = rch.enable
|
html = rch.enable
|
||||||
? await rch.Get(url, headers, useDefaultHeaders: init.useDefaultHeaders)
|
? await rch.Get(url, headers, useDefaultHeaders: init.useDefaultHeaders)
|
||||||
: init.priorityBrowser == "playwright" ? await PlaywrightBrowser.Get(init, url, headers, proxy.data, cookies: init.cookies)
|
: init.priorityBrowser == "playwright" ? await PlaywrightBrowser.Get(init, url, headers, proxy.data, cookies: init.cookies)
|
||||||
: await Http.Get(url, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
|
: await Http.Get(url, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.GetHttpVersion(), useDefaultHeaders: init.useDefaultHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (html == null)
|
if (html == null)
|
||||||
|
|||||||
@ -99,14 +99,14 @@ namespace Catalog.Controllers
|
|||||||
|
|
||||||
html = rch.enable
|
html = rch.enable
|
||||||
? await rch.Post(url.Replace("{page}", page.ToString()), data, headers, useDefaultHeaders: init.useDefaultHeaders)
|
? await rch.Post(url.Replace("{page}", page.ToString()), data, headers, useDefaultHeaders: init.useDefaultHeaders)
|
||||||
: await Http.Post(url.Replace("{page}", page.ToString()), httpdata, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
|
: await Http.Post(url.Replace("{page}", page.ToString()), httpdata, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.GetHttpVersion(), useDefaultHeaders: init.useDefaultHeaders);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
html = rch.enable
|
html = rch.enable
|
||||||
? await rch.Get(url.Replace("{page}", page.ToString()), headers, useDefaultHeaders: init.useDefaultHeaders)
|
? await rch.Get(url.Replace("{page}", page.ToString()), headers, useDefaultHeaders: init.useDefaultHeaders)
|
||||||
: init.priorityBrowser == "playwright" ? await PlaywrightBrowser.Get(init, url.Replace("{page}", page.ToString()), headers, proxy.data, cookies: init.cookies)
|
: init.priorityBrowser == "playwright" ? await PlaywrightBrowser.Get(init, url.Replace("{page}", page.ToString()), headers, proxy.data, cookies: init.cookies)
|
||||||
: await Http.Get(url.Replace("{page}", page.ToString()), headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
|
: await Http.Get(url.Replace("{page}", page.ToString()), headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.GetHttpVersion(), useDefaultHeaders: init.useDefaultHeaders);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -19,7 +18,9 @@ namespace Lampac.Controllers
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[Route("/version")]
|
[Route("/version")]
|
||||||
public ActionResult Version() => Content($"{appversion}.{minorversion}");
|
public ActionResult Version() => string.IsNullOrEmpty(versionTag)
|
||||||
|
? Content($"{appversion}.{minorversion}")
|
||||||
|
: Content($"{appversion}.{minorversion}-{versionTag}");
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
|
|||||||
@ -317,13 +317,17 @@ namespace Lampac.Engine
|
|||||||
|
|
||||||
if (name == "devices")
|
if (name == "devices")
|
||||||
{
|
{
|
||||||
var evc = event_clients.Where(i => i.Value == uid).ToArray();
|
var uidClients = event_clients
|
||||||
|
.Where(i => i.Value == uid)
|
||||||
|
.ToDictionary();
|
||||||
|
|
||||||
var devices = _connections
|
var devices = _connections
|
||||||
.Where(i => i.Value.ConnectionId != connection.ConnectionId)
|
.Where(i => i.Value.ConnectionId != connection.ConnectionId)
|
||||||
.Where(i => i.Value.Ip == connection.Ip || event_clients.Values.Contains(uid))
|
.Where(i =>
|
||||||
|
(!AppInit.conf.accsdb.enable && i.Value.Ip == connection.Ip)
|
||||||
|
|| uidClients.Keys.Contains(i.Key))
|
||||||
.Select(i => new {
|
.Select(i => new {
|
||||||
uid = event_clients.TryGetValue(i.Value.ConnectionId, out var _uid) ? _uid : null,
|
uid = uidClients.TryGetValue(i.Value.ConnectionId, out var targetUid) ? targetUid : null,
|
||||||
i.Value.ConnectionId,
|
i.Value.ConnectionId,
|
||||||
i.Value.RequestInfo.UserAgent
|
i.Value.RequestInfo.UserAgent
|
||||||
})
|
})
|
||||||
@ -517,7 +521,7 @@ namespace Lampac.Engine
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
var cutoff = now.AddSeconds(-125); // ping êàæäûå 40 ñåêóíä
|
var cutoff = now.AddSeconds(-125);
|
||||||
|
|
||||||
foreach (var connection in _connections)
|
foreach (var connection in _connections)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -192,11 +192,17 @@ namespace Lampac
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region vers.txt
|
#region vers.txt
|
||||||
|
Directory.CreateDirectory("data");
|
||||||
|
|
||||||
if (!File.Exists("data/vers.txt"))
|
if (!File.Exists("data/vers.txt"))
|
||||||
File.WriteAllText("data/vers.txt", BaseController.appversion);
|
File.WriteAllText("data/vers.txt", BaseController.appversion);
|
||||||
|
|
||||||
if (!File.Exists("data/vers-minor.txt"))
|
if (!File.Exists("data/vers-minor.txt"))
|
||||||
File.WriteAllText("data/vers-minor.txt", "1");
|
File.WriteAllText("data/vers-minor.txt", BaseController.minorversion);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(BaseController.versionTag) && !File.Exists("data/vers-tag.txt"))
|
||||||
|
File.WriteAllText("data/vers-tag.txt", BaseController.versionTag);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SQL
|
#region SQL
|
||||||
@ -421,6 +427,7 @@ namespace Lampac
|
|||||||
if (AppInit.modules?.FirstOrDefault(i => i.dll == "DLNA.dll" && i.enable) != null)
|
if (AppInit.modules?.FirstOrDefault(i => i.dll == "DLNA.dll" && i.enable) != null)
|
||||||
TrackersCron.Run();
|
TrackersCron.Run();
|
||||||
|
|
||||||
|
Directory.CreateDirectory("wwwroot");
|
||||||
LampaCron.Run();
|
LampaCron.Run();
|
||||||
|
|
||||||
appReload = new AppReload();
|
appReload = new AppReload();
|
||||||
|
|||||||
@ -639,7 +639,7 @@ namespace Lampac
|
|||||||
AppInit.conf.BaseModule.EnableControllers.Length == 0)
|
AppInit.conf.BaseModule.EnableControllers.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WebLogEnableController = AppInit.conf.BaseModule.EnableControllers.Contains("WebLogController.cs");
|
WebLogEnableController = AppInit.conf.BaseModule.EnableControllers.Contains("WebLog");
|
||||||
|
|
||||||
var syntaxTree = new List<SyntaxTree>();
|
var syntaxTree = new List<SyntaxTree>();
|
||||||
|
|
||||||
|
|||||||
@ -364,7 +364,7 @@ namespace Online.Controllers
|
|||||||
);
|
);
|
||||||
|
|
||||||
var result = await Http.Post<JObject>("https://api.cdnlibs.org/api/auth/oauth/token", content,
|
var result = await Http.Post<JObject>("https://api.cdnlibs.org/api/auth/oauth/token", content,
|
||||||
httpversion: init.httpversion, timeoutSeconds: init.httptimeout, headers: headers, useDefaultHeaders: false
|
httpversion: init.GetHttpVersion(), timeoutSeconds: init.GetHttpTimeout(), headers: headers, useDefaultHeaders: false
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace Online.Controllers
|
|||||||
async public Task<ActionResult> Pro()
|
async public Task<ActionResult> Pro()
|
||||||
{
|
{
|
||||||
string uri = $"{init.corsHost()}/api/v2/token_request?user_dev_apk=2.0.1&user_dev_id=&user_dev_name=Xiaomi&user_dev_os=11&user_dev_vendor=Xiaomi&user_dev_token=";
|
string uri = $"{init.corsHost()}/api/v2/token_request?user_dev_apk=2.0.1&user_dev_id=&user_dev_name=Xiaomi&user_dev_os=11&user_dev_vendor=Xiaomi&user_dev_token=";
|
||||||
var token_request = await Http.Get<JObject>(uri, httpversion: init.httpversion, proxy: proxy, useDefaultHeaders: false);
|
var token_request = await Http.Get<JObject>(uri, httpversion: init.GetHttpVersion(), timeoutSeconds: init.GetHttpTimeout(), proxy: proxy, useDefaultHeaders: false);
|
||||||
|
|
||||||
if (token_request == null)
|
if (token_request == null)
|
||||||
return ContentTo($"нет доступа к {init.corsHost()}");
|
return ContentTo($"нет доступа к {init.corsHost()}");
|
||||||
|
|||||||
@ -122,7 +122,7 @@ namespace Online.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var rtk = await Http.Get<JObject>($"{init.corsHost()}/api-fx/request-token",
|
var rtk = await Http.Get<JObject>($"{init.corsHost()}/api-fx/request-token",
|
||||||
proxy: proxy, httpversion: init.httpversion, timeoutSeconds: 30
|
proxy: proxy, httpversion: init.GetHttpVersion(), timeoutSeconds: 30
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rtk == null || !rtk.ContainsKey("token"))
|
if (rtk == null || !rtk.ContainsKey("token"))
|
||||||
@ -146,14 +146,14 @@ namespace Online.Controllers
|
|||||||
string refreshToken = Regex.Match(F.ReadAllText(authFile), "\"refreshToken\": ?\"([^\"]+)\"").Groups[1].Value;
|
string refreshToken = Regex.Match(F.ReadAllText(authFile), "\"refreshToken\": ?\"([^\"]+)\"").Groups[1].Value;
|
||||||
|
|
||||||
root_auth = await Http.Get<JObject>($"{init.corsHost()}/api-fx/refresh?refreshToken={HttpUtility.UrlEncode(refreshToken)}",
|
root_auth = await Http.Get<JObject>($"{init.corsHost()}/api-fx/refresh?refreshToken={HttpUtility.UrlEncode(refreshToken)}",
|
||||||
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.httpversion, timeoutSeconds: 30
|
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.GetHttpVersion(), timeoutSeconds: 30
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var data = new System.Net.Http.StringContent($"{{\"user_name\":\"{init.user_apitv}\",\"user_passw\":\"{init.passwd_apitv}\",\"session\":true}}", Encoding.UTF8, "application/json");
|
var data = new System.Net.Http.StringContent($"{{\"user_name\":\"{init.user_apitv}\",\"user_passw\":\"{init.passwd_apitv}\",\"session\":true}}", Encoding.UTF8, "application/json");
|
||||||
root_auth = await Http.Post<JObject>($"{init.corsHost()}/api-fx/auth", data,
|
root_auth = await Http.Post<JObject>($"{init.corsHost()}/api-fx/auth", data,
|
||||||
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.httpversion, timeoutSeconds: 30
|
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.GetHttpVersion(), timeoutSeconds: 30
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ namespace Online.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var postdata = new System.Net.Http.StringContent($"{{\"email\":\"{login}\",\"password\":\"{pass}\",\"fingerprint\":\"{CrypTo.md5(DateTime.Now.ToString())}\",\"device\":{{}}}}", Encoding.UTF8, "application/json");
|
var postdata = new System.Net.Http.StringContent($"{{\"email\":\"{login}\",\"password\":\"{pass}\",\"fingerprint\":\"{CrypTo.md5(DateTime.Now.ToString())}\",\"device\":{{}}}}", Encoding.UTF8, "application/json");
|
||||||
var result = await Http.Post<JObject>($"{init.corsHost()}/api/login", postdata, httpversion: init.httpversion, proxy: proxy, headers: httpHeaders(init));
|
var result = await Http.Post<JObject>($"{init.corsHost()}/api/login", postdata, httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return ContentTo("Ошибка авторизации ;(");
|
return ContentTo("Ошибка авторизации ;(");
|
||||||
|
|||||||
@ -168,7 +168,7 @@ namespace Online.Controllers
|
|||||||
string uri = $"{init.host}/v1/api/media/{(serial == 1 ? "serials" : "movies")}";
|
string uri = $"{init.host}/v1/api/media/{(serial == 1 ? "serials" : "movies")}";
|
||||||
var data = new System.Net.Http.StringContent(JsonConvert.SerializeObject(new { search }), Encoding.UTF8, "application/json");
|
var data = new System.Net.Http.StringContent(JsonConvert.SerializeObject(new { search }), Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
var video = await Http.Get<JObject>(uri, body: data, timeoutSeconds: init.httptimeout, proxy: proxy, useDefaultHeaders: false, headers: httpHeaders(init, HeadersModel.Init(
|
var video = await Http.Get<JObject>(uri, body: data, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, useDefaultHeaders: false, headers: httpHeaders(init, HeadersModel.Init(
|
||||||
("X-API-AUTH", codeauth),
|
("X-API-AUTH", codeauth),
|
||||||
("X-API-ID", init.token.Split(":")[0])
|
("X-API-ID", init.token.Split(":")[0])
|
||||||
)));
|
)));
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace Online.Controllers
|
|||||||
if (string.IsNullOrWhiteSpace(code))
|
if (string.IsNullOrWhiteSpace(code))
|
||||||
{
|
{
|
||||||
string uri = $"{init.corsHost()}/oauth2/device?grant_type=device_code&client_id=xbmc&client_secret=cgg3gtifu46urtfp2zp1nqtba0k2ezxh";
|
string uri = $"{init.corsHost()}/oauth2/device?grant_type=device_code&client_id=xbmc&client_secret=cgg3gtifu46urtfp2zp1nqtba0k2ezxh";
|
||||||
var token_request = await Http.Post<JObject>(uri, "", httpversion: init.httpversion, proxy: proxy, headers: httpHeaders(init));
|
var token_request = await Http.Post<JObject>(uri, "", httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
|
|
||||||
if (token_request == null)
|
if (token_request == null)
|
||||||
return ContentTo($"нет доступа к {init.corsHost()}");
|
return ContentTo($"нет доступа к {init.corsHost()}");
|
||||||
@ -46,14 +46,14 @@ namespace Online.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
string uri = $"{init.corsHost()}/oauth2/device?grant_type=device_token&client_id=xbmc&client_secret=cgg3gtifu46urtfp2zp1nqtba0k2ezxh&code={code}";
|
string uri = $"{init.corsHost()}/oauth2/device?grant_type=device_token&client_id=xbmc&client_secret=cgg3gtifu46urtfp2zp1nqtba0k2ezxh&code={code}";
|
||||||
var device_token = await Http.Post<JObject>(uri, "", httpversion: init.httpversion, proxy: proxy, headers: httpHeaders(init));
|
var device_token = await Http.Post<JObject>(uri, "", httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
if (device_token == null || string.IsNullOrWhiteSpace(device_token.Value<string>("access_token")))
|
if (device_token == null || string.IsNullOrWhiteSpace(device_token.Value<string>("access_token")))
|
||||||
return LocalRedirect("/lite/kinopubpro");
|
return LocalRedirect("/lite/kinopubpro");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
{
|
{
|
||||||
uri = $"{init.corsHost()}/v1/device/notify?access_token={device_token.Value<string>("access_token")}";
|
uri = $"{init.corsHost()}/v1/device/notify?access_token={device_token.Value<string>("access_token")}";
|
||||||
await Http.Post(uri, $"&title={name}", httpversion: init.httpversion, proxy: proxy, headers: httpHeaders(init));
|
await Http.Post(uri, $"&title={name}", httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ContentTo("Добавьте в init.conf<br><br>\"KinoPub\": {<br> \"enable\": true,<br> \"token\": \"" + device_token.Value<string>("access_token") + "\"<br>}");
|
return ContentTo("Добавьте в init.conf<br><br>\"KinoPub\": {<br> \"enable\": true,<br> \"token\": \"" + device_token.Value<string>("access_token") + "\"<br>}");
|
||||||
|
|||||||
@ -139,7 +139,7 @@ namespace Online.Controllers
|
|||||||
});
|
});
|
||||||
|
|
||||||
PlaywrightBase.GotoAsync(page, uri);
|
PlaywrightBase.GotoAsync(page, uri);
|
||||||
await browser.WaitForAnySelectorAsync(page, "#playerjsfile", ".uppod-media", ".alert").ConfigureAwait(false);
|
await browser.WaitForAnySelectorAsync(page, "#playerjsfile", "[id^='playerjsfile']", ".uppod-media", ".alert").ConfigureAwait(false);
|
||||||
|
|
||||||
string content = await page.ContentAsync().ConfigureAwait(false);
|
string content = await page.ContentAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Shared.Models.Online.Vibix;
|
using Shared.Models.Online.Vibix;
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
using Shared.PlaywrightCore;
|
||||||
|
|
||||||
namespace Online.Controllers
|
namespace Online.Controllers
|
||||||
{
|
{
|
||||||
@ -8,9 +10,28 @@ namespace Online.Controllers
|
|||||||
{
|
{
|
||||||
public Vibix() : base(AppInit.conf.Vibix) { }
|
public Vibix() : base(AppInit.conf.Vibix) { }
|
||||||
|
|
||||||
|
static readonly string[] qualityCandidates = new[] { "1080", "720", "480" };
|
||||||
|
|
||||||
|
StreamQualityTpl buildStreamQuality(string file)
|
||||||
|
{
|
||||||
|
var streams = new StreamQualityTpl();
|
||||||
|
if (string.IsNullOrEmpty(file))
|
||||||
|
return streams;
|
||||||
|
|
||||||
|
foreach (string q in qualityCandidates)
|
||||||
|
{
|
||||||
|
var g = new Regex($"{q}p?\\](\\{{[^\\}}]+\\}})?(?<file>https?://[^,\t\\[\\;\\{{ ]+)").Match(file).Groups;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(g["file"].Value))
|
||||||
|
streams.Append(HostStreamProxy(g["file"].Value), $"{q}p");
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("lite/vibix")]
|
[Route("lite/vibix")]
|
||||||
async public Task<ActionResult> Index(string imdb_id, long kinopoisk_id, string title, string original_title, int s = -1, bool rjson = false)
|
async public Task<ActionResult> Index(string imdb_id, long kinopoisk_id, string title, string original_title, int s = -1, bool rjson = false, int voiceover = 0)
|
||||||
{
|
{
|
||||||
if (await IsRequestBlocked(rch: true))
|
if (await IsRequestBlocked(rch: true))
|
||||||
return badInitMsg;
|
return badInitMsg;
|
||||||
@ -18,20 +39,142 @@ namespace Online.Controllers
|
|||||||
if (string.IsNullOrEmpty(init.token))
|
if (string.IsNullOrEmpty(init.token))
|
||||||
return OnError();
|
return OnError();
|
||||||
|
|
||||||
|
string enc_title = HttpUtility.UrlEncode(title);
|
||||||
|
string enc_original_title = HttpUtility.UrlEncode(original_title);
|
||||||
|
|
||||||
|
if (!hybridCache.TryGetValue($"vibix:v2:view:{kinopoisk_id}:{imdb_id}", out (Dictionary<string, JObject> seasons, string iframe) v2Cache))
|
||||||
|
{
|
||||||
|
v2Cache = await searchV2(imdb_id, kinopoisk_id);
|
||||||
|
if (v2Cache.seasons != null || !string.IsNullOrEmpty(v2Cache.iframe))
|
||||||
|
hybridCache.Set($"vibix:v2:view:{kinopoisk_id}:{imdb_id}", v2Cache, cacheTime(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v2Cache.seasons != null)
|
||||||
|
{
|
||||||
|
if (PlaywrightBrowser.Status == PlaywrightStatus.disabled)
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
#region Сериал
|
||||||
|
if (s == -1)
|
||||||
|
{
|
||||||
|
var tpl = new SeasonTpl(v2Cache.seasons.Count);
|
||||||
|
|
||||||
|
foreach (var season in v2Cache.seasons)
|
||||||
|
{
|
||||||
|
string link = $"{host}/lite/vibix?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={season.Key}";
|
||||||
|
tpl.Append($"{season.Key} сезон", link, season.Key);
|
||||||
|
}
|
||||||
|
return await ContentTpl(tpl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string sArhc = s.ToString();
|
||||||
|
var videos = v2Cache.seasons.First(i => i.Key == sArhc).Value["videos"].ToObject<Dictionary<string, JObject>>();
|
||||||
|
var etpl = new EpisodeTpl(videos.Count);
|
||||||
|
|
||||||
|
foreach (var video in videos)
|
||||||
|
{
|
||||||
|
string iframe = addIframeArgs(video.Value.Value<string>("iframe"));
|
||||||
|
etpl.Append($"{video.Key} серия", title ?? original_title, sArhc, video.Key, accsArgs($"{host}/lite/vibix/video/{AesTo.Encrypt(buildVideoPayload(iframe, null))}"), "call", vast: init.vast);
|
||||||
|
}
|
||||||
|
return await ContentTpl(etpl);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
var data = await search(imdb_id, kinopoisk_id);
|
var data = await search(imdb_id, kinopoisk_id);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
return OnError();
|
return OnError();
|
||||||
|
if (data.type == "serial" && PlaywrightBrowser.Status != PlaywrightStatus.disabled && !string.IsNullOrEmpty(data.embed_code))
|
||||||
|
{
|
||||||
|
var serials = await getSerials(imdb_id, kinopoisk_id);
|
||||||
|
if (serials?.seasons == null)
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
int defaultVoiceover = data.voiceovers?.FirstOrDefault()?.id ?? 0;
|
||||||
|
int activeVoiceover = voiceover > 0 ? voiceover : defaultVoiceover;
|
||||||
|
|
||||||
|
VoiceTpl? vtpl = null;
|
||||||
|
if (data.voiceovers != null && data.voiceovers.Length > 0)
|
||||||
|
{
|
||||||
|
var voices = new VoiceTpl(data.voiceovers.Length);
|
||||||
|
foreach (var voice in data.voiceovers)
|
||||||
|
{
|
||||||
|
string link = $"{host}/lite/vibix?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={s}&voiceover={voice.id}";
|
||||||
|
voices.Append(voice.name, voice.id == activeVoiceover, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
vtpl = voices;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s == -1)
|
||||||
|
{
|
||||||
|
var tpl = new SeasonTpl(serials.seasons.Length);
|
||||||
|
for (int i = 0; i < serials.seasons.Length; i++)
|
||||||
|
{
|
||||||
|
int seasonNumber = i + 1;
|
||||||
|
string link = $"{host}/lite/vibix?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={seasonNumber}&voiceover={activeVoiceover}";
|
||||||
|
tpl.Append($"{seasonNumber} сезон", link, seasonNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await ContentTpl(tpl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int seasonIndex = s - 1;
|
||||||
|
if (seasonIndex < 0 || seasonIndex >= serials.seasons.Length)
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
var season = serials.seasons[seasonIndex];
|
||||||
|
var etpl = new EpisodeTpl(vtpl, season.series?.Length ?? 0);
|
||||||
|
|
||||||
|
if (season.series != null)
|
||||||
|
{
|
||||||
|
foreach (var episode in season.series)
|
||||||
|
{
|
||||||
|
string embed = applyEmbedArgs(data.embed_code, s.ToString(), episode.id.ToString(), activeVoiceover);
|
||||||
|
etpl.Append(episode.name ?? $"{episode.id} серия", title ?? original_title, s.ToString(), episode.id.ToString(), accsArgs($"{host}/lite/vibix/video/{AesTo.Encrypt(buildVideoPayload(null, embed))}"), "call", vast: init.vast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await ContentTpl(etpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type == "movie" && PlaywrightBrowser.Status != PlaywrightStatus.disabled && !string.IsNullOrEmpty(data.iframe_url))
|
||||||
|
{
|
||||||
|
var mtpl = new MovieTpl(title, original_title, 1);
|
||||||
|
string iframe = addIframeArgs(data.iframe_url);
|
||||||
|
if (data.voiceovers != null && data.voiceovers.Length > 0)
|
||||||
|
{
|
||||||
|
int defaultVoiceover = data.voiceovers.FirstOrDefault()?.id ?? 0;
|
||||||
|
int activeVoiceover = voiceover > 0 ? voiceover : defaultVoiceover;
|
||||||
|
|
||||||
|
foreach (var voice in data.voiceovers)
|
||||||
|
{
|
||||||
|
string embed = applyEmbedArgs(data.embed_code, null, null, voice.id);
|
||||||
|
mtpl.Append(voice.name, accsArgs($"{host}/lite/vibix/video/{AesTo.Encrypt(buildVideoPayload(iframe, embed))}") + "#.m3u8", "call", voice_name: voice.name, vast: init.vast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtpl.Append("По-умолчанию", accsArgs($"{host}/lite/vibix/video/{AesTo.Encrypt(buildVideoPayload(iframe, data.embed_code))}") + "#.m3u8", "call", vast: init.vast);
|
||||||
|
}
|
||||||
|
return await ContentTpl(mtpl);
|
||||||
|
}
|
||||||
|
|
||||||
rhubFallback:
|
rhubFallback:
|
||||||
var cache = await InvokeCacheResult<EmbedModel>(ipkey($"vibix:iframe:{data.iframe_url}"), 20, async e =>
|
var cache = await InvokeCacheResult<EmbedModel>(ipkey($"vibix:iframe:{data.iframe_url}"), 20, async e =>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
string domain = getFrontendDomain();
|
||||||
string api_url = data.iframe_url
|
string api_url = data.iframe_url
|
||||||
.Replace("/embed/", "/api/v1/embed/")
|
.Replace("/embed/", "/api/v1/embed/")
|
||||||
.Replace("/embed-serials/", "/api/v1/embed-serials/");
|
.Replace("/embed-serials/", "/api/v1/embed-serials/");
|
||||||
|
|
||||||
api_url += $"?iframe_url={HttpUtility.UrlEncode(data.iframe_url)}";
|
api_url += $"?iframe_url={HttpUtility.UrlEncode(data.iframe_url)}";
|
||||||
api_url += $"&kp={CrypTo.unic(6).ToLower()}";
|
api_url += $"&kp={CrypTo.unic(6).ToLower()}";
|
||||||
api_url += "&domain=cm.vibix.biz&parent_domain=cm.vibix.biz";
|
api_url += $"&domain={domain}&parent_domain={domain}";
|
||||||
|
|
||||||
var api_headers = HeadersModel.Init(
|
var api_headers = HeadersModel.Init(
|
||||||
("accept", "*/*"),
|
("accept", "*/*"),
|
||||||
@ -62,15 +205,7 @@ namespace Online.Controllers
|
|||||||
|
|
||||||
foreach (var movie in cache.Value.playlist)
|
foreach (var movie in cache.Value.playlist)
|
||||||
{
|
{
|
||||||
var streams = new StreamQualityTpl();
|
var streams = buildStreamQuality(movie.file);
|
||||||
|
|
||||||
foreach (string q in new string[] { "1080", "720", "480" })
|
|
||||||
{
|
|
||||||
var g = new Regex($"{q}p?\\](\\{{[^\\}}]+\\}})?(?<file>https?://[^,\t\\[\\;\\{{ ]+)").Match(movie.file).Groups;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(g["file"].Value))
|
|
||||||
streams.Append(HostStreamProxy(g["file"].Value), $"{q}p");
|
|
||||||
}
|
|
||||||
|
|
||||||
mtpl.Append(movie.title, streams.Firts().link, streamquality: streams, vast: init.vast);
|
mtpl.Append(movie.title, streams.Firts().link, streamquality: streams, vast: init.vast);
|
||||||
}
|
}
|
||||||
@ -84,9 +219,6 @@ namespace Online.Controllers
|
|||||||
#region Сериал
|
#region Сериал
|
||||||
return await ContentTpl(cache, () =>
|
return await ContentTpl(cache, () =>
|
||||||
{
|
{
|
||||||
string enc_title = HttpUtility.UrlEncode(title);
|
|
||||||
string enc_original_title = HttpUtility.UrlEncode(original_title);
|
|
||||||
|
|
||||||
if (s == -1)
|
if (s == -1)
|
||||||
{
|
{
|
||||||
var tpl = new SeasonTpl(cache.Value.playlist.Length);
|
var tpl = new SeasonTpl(cache.Value.playlist.Length);
|
||||||
@ -121,14 +253,7 @@ namespace Online.Controllers
|
|||||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(file))
|
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(file))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var streams = new StreamQualityTpl();
|
var streams = buildStreamQuality(file);
|
||||||
|
|
||||||
foreach (string q in new string[] { "1080", "720", "480" })
|
|
||||||
{
|
|
||||||
var g = new Regex($"{q}p?\\](\\{{[^\\}}]+\\}})?(?<file>https?://[^,\t\\[\\;\\{{ ]+)").Match(file).Groups;
|
|
||||||
if (!string.IsNullOrEmpty(g["file"].Value))
|
|
||||||
streams.Append(HostStreamProxy(g["file"].Value), $"{q}p");
|
|
||||||
}
|
|
||||||
|
|
||||||
etpl.Append(name, title ?? original_title, sArhc, Regex.Match(name, "([0-9]+)").Groups[1].Value, streams.Firts().link, streamquality: streams, vast: init.vast);
|
etpl.Append(name, title ?? original_title, sArhc, Regex.Match(name, "([0-9]+)").Groups[1].Value, streams.Firts().link, streamquality: streams, vast: init.vast);
|
||||||
}
|
}
|
||||||
@ -140,7 +265,440 @@ namespace Online.Controllers
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
string addIframeArgs(string iframe)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(iframe))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
if (iframe.Contains("domain=") || iframe.Contains("parent_domain="))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
string delimiter = iframe.Contains("?") ? "&" : "?";
|
||||||
|
string domain = getFrontendDomain();
|
||||||
|
return $"{iframe}{delimiter}domain={domain}&parent_domain={domain}";
|
||||||
|
}
|
||||||
|
|
||||||
|
string buildVideoPayload(string iframe, string embed)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(embed))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
return JObject.FromObject(new
|
||||||
|
{
|
||||||
|
iframe,
|
||||||
|
embed
|
||||||
|
}).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
string buildEmbedHtml(string embedCode)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(embedCode))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
embedCode = normalizeEmbedCode(embedCode);
|
||||||
|
|
||||||
|
return $@"<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name=""viewport"" content=""width=device-width, initial-scale=1"">
|
||||||
|
<script src=""https://graphicslab.io/sdk/v2/rendex-sdk.min.js""></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{embedCode}
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
string applyEmbedArgs(string embedCode, string season, string episodes, int voiceover)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(embedCode))
|
||||||
|
return embedCode;
|
||||||
|
|
||||||
|
embedCode = normalizeEmbedCode(embedCode);
|
||||||
|
var attrs = new List<string>();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(season) && !embedCode.Contains("data-season="))
|
||||||
|
attrs.Add($"data-season=\"{season}\"");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(episodes) && !embedCode.Contains("data-episodes="))
|
||||||
|
attrs.Add($"data-episodes=\"{episodes}\"");
|
||||||
|
|
||||||
|
if (voiceover > 0 && !embedCode.Contains("data-voiceover="))
|
||||||
|
attrs.Add($"data-voiceover=\"{voiceover}\"");
|
||||||
|
|
||||||
|
if (voiceover > 0 && !embedCode.Contains("data-voiceover-only="))
|
||||||
|
attrs.Add("data-voiceover-only=\"true\"");
|
||||||
|
|
||||||
|
if (attrs.Count == 0)
|
||||||
|
return embedCode;
|
||||||
|
|
||||||
|
string insert = $"<ins {string.Join(" ", attrs)} ";
|
||||||
|
return Regex.Replace(embedCode, "<ins\\b", insert, RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalizeEmbedCode(string embedCode)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(embedCode))
|
||||||
|
return embedCode;
|
||||||
|
|
||||||
|
if (Regex.IsMatch(embedCode, "<ins\\b", RegexOptions.IgnoreCase))
|
||||||
|
return embedCode;
|
||||||
|
|
||||||
|
return $"<ins {embedCode}></ins>";
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Video
|
||||||
|
[HttpGet]
|
||||||
|
[Route("lite/vibix/video/{*iframe}")]
|
||||||
|
async public ValueTask<ActionResult> Video(string iframe)
|
||||||
|
{
|
||||||
|
if (PlaywrightBrowser.Status == PlaywrightStatus.disabled)
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
if (await IsRequestBlocked(rch: true))
|
||||||
|
return badInitMsg;
|
||||||
|
|
||||||
|
string decrypted = AesTo.Decrypt(iframe);
|
||||||
|
if (string.IsNullOrEmpty(decrypted))
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
string embedCode = null;
|
||||||
|
string iframeUrl = decrypted;
|
||||||
|
|
||||||
|
if (decrypted.StartsWith("{"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var payload = JObject.Parse(decrypted);
|
||||||
|
iframeUrl = payload.Value<string>("iframe");
|
||||||
|
embedCode = payload.Value<string>("embed");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
iframeUrl = addIframeArgs(iframeUrl);
|
||||||
|
if (string.IsNullOrEmpty(iframeUrl) && string.IsNullOrEmpty(embedCode))
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
string cacheKey = iframeUrl ?? CrypTo.md5(embedCode ?? string.Empty);
|
||||||
|
return await InvkSemaphore($"vibix:video:{cacheKey}:{proxyManager?.CurrentProxyIp}", async key =>
|
||||||
|
{
|
||||||
|
if (!hybridCache.TryGetValue(key, out (string location, StreamQualityTpl streamquality) cache))
|
||||||
|
{
|
||||||
|
int bestQuality = 0;
|
||||||
|
var streamquality = new StreamQualityTpl();
|
||||||
|
string referer = Regex.Match(iframeUrl ?? string.Empty, "(^https?://[^/]+)").Groups[1].Value;
|
||||||
|
if (string.IsNullOrEmpty(referer))
|
||||||
|
referer = $"https://{getFrontendDomain()}";
|
||||||
|
var headers = httpHeaders(init, HeadersModel.Init
|
||||||
|
(
|
||||||
|
("referer", referer)
|
||||||
|
));
|
||||||
|
|
||||||
|
TimeSpan? cacheTtl = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var browser = new PlaywrightBrowser(init.priorityBrowser))
|
||||||
|
{
|
||||||
|
var page = await browser.NewPageAsync(init.plugin, proxy: proxy_data, headers: headers?.ToDictionary()).ConfigureAwait(false);
|
||||||
|
if (page == null)
|
||||||
|
return OnError();
|
||||||
|
|
||||||
|
await page.AddInitScriptAsync(@"() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('pljsquality', '1080p');
|
||||||
|
localStorage.setItem('ksquality', '1080p');
|
||||||
|
} catch (e) {}
|
||||||
|
}").ConfigureAwait(false);
|
||||||
|
|
||||||
|
await page.RouteAsync("**/api/v1/embed/**", async route =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (route.Request.Method != "GET")
|
||||||
|
{
|
||||||
|
await route.ContinueAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await route.ContinueAsync();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await route.ContinueAsync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.RouteAsync("**/*", async route =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(cache.location))
|
||||||
|
{
|
||||||
|
await route.AbortAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqUrl = route.Request.Url;
|
||||||
|
|
||||||
|
if (reqUrl.Contains("/hls/", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
reqUrl.Contains("/seg-", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
reqUrl.Contains(".ts", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var playlist = Regex.Replace(reqUrl, @"/seg-[^/?]+\.ts", "/index.m3u8", RegexOptions.IgnoreCase);
|
||||||
|
updateCacheTtl(ref cacheTtl, reqUrl);
|
||||||
|
updateLocation(ref cache.location, ref bestQuality, playlist, streamquality);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reqUrl.IndexOf(".m3u8", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
(reqUrl.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||||
|
reqUrl.IndexOf(".ts", StringComparison.OrdinalIgnoreCase) < 0))
|
||||||
|
{
|
||||||
|
updateCacheTtl(ref cacheTtl, reqUrl);
|
||||||
|
updateLocation(ref cache.location, ref bestQuality, reqUrl, streamquality);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await PlaywrightBase.AbortOrCache(page, route, abortMedia: true, fullCacheJS: true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await route.ContinueAsync();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
});
|
||||||
|
|
||||||
|
var options = new PageGotoOptions()
|
||||||
|
{
|
||||||
|
Timeout = 15_000,
|
||||||
|
WaitUntil = WaitUntilState.NetworkIdle
|
||||||
|
};
|
||||||
|
|
||||||
|
IResponse result = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await page.GotoAsync("https://cm.vibix.biz", new PageGotoOptions()
|
||||||
|
{
|
||||||
|
Timeout = 10_000,
|
||||||
|
WaitUntil = WaitUntilState.DOMContentLoaded
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(embedCode))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await page.SetContentAsync(buildEmbedHtml(embedCode), new PageSetContentOptions()
|
||||||
|
{
|
||||||
|
Timeout = 15_000,
|
||||||
|
WaitUntil = WaitUntilState.NetworkIdle
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(iframeUrl))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await page.SetContentAsync(PlaywrightBase.IframeHtml(iframeUrl), new PageSetContentOptions()
|
||||||
|
{
|
||||||
|
Timeout = 15_000,
|
||||||
|
WaitUntil = WaitUntilState.NetworkIdle
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(cache.location) && !string.IsNullOrEmpty(iframeUrl))
|
||||||
|
result = await page.GotoAsync(iframeUrl, options).ConfigureAwait(false);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await page.EvaluateAsync(@"() => {
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
if (video) {
|
||||||
|
video.muted = true;
|
||||||
|
video.play().catch(() => {});
|
||||||
|
}
|
||||||
|
const btn = document.querySelector('.vjs-big-play-button, .plyr__control--overlaid, .jw-icon-playback, .jw-icon-play');
|
||||||
|
if (btn) btn.click();
|
||||||
|
}").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(cache.location))
|
||||||
|
await page.WaitForTimeoutAsync(3000).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (result != null && string.IsNullOrEmpty(cache.location))
|
||||||
|
{
|
||||||
|
string html = await page.ContentAsync().ConfigureAwait(false);
|
||||||
|
cache.location = Regex.Match(html, "<video preload=\"none\" src=\"(https?://[^\"]+)\"").Groups[1].Value;
|
||||||
|
if (!cache.location.Contains(".m3u") && !cache.location.Contains(".mp4"))
|
||||||
|
cache.location = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaywrightBase.WebLog("SET", iframe, cache.location, proxy_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(cache.location))
|
||||||
|
{
|
||||||
|
proxyManager?.Refresh();
|
||||||
|
return OnError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return OnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyManager?.Success();
|
||||||
|
cache.streamquality = streamquality;
|
||||||
|
hybridCache.Set(key, cache, resolveCacheTtl(cacheTtl, cacheTime(20)));
|
||||||
|
}
|
||||||
|
|
||||||
|
string refererStream = Regex.Match(iframeUrl ?? string.Empty, "(^https?://[^/]+)").Groups[1].Value;
|
||||||
|
if (string.IsNullOrEmpty(refererStream))
|
||||||
|
refererStream = "https://cm.vibix.biz";
|
||||||
|
var headers_stream = httpHeaders(init.corsHost(), HeadersModel.Join(HeadersModel.Init("referer", refererStream), init.headers_stream));
|
||||||
|
|
||||||
|
string link = HostStreamProxy(cache.location, headers: headers_stream);
|
||||||
|
var streamQuality = cache.streamquality;
|
||||||
|
|
||||||
|
bool hasQualities = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (streamQuality.Any())
|
||||||
|
hasQualities = true;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (!hasQualities)
|
||||||
|
return ContentTo(VideoTpl.ToJson("play", link, "auto", vast: init.vast));
|
||||||
|
|
||||||
|
return ContentTo(VideoTpl.ToJson("play", streamQuality.Firts().link, "auto", streamquality: streamQuality, vast: init.vast));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
void updateLocation(ref string location, ref int bestQuality, string candidate, StreamQualityTpl streamquality)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(candidate))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int q = getQualityFromUrl(candidate);
|
||||||
|
if (q == 0)
|
||||||
|
{
|
||||||
|
if (tryExpandQualityVariants(candidate, streamquality, ref bestQuality, ref location))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(location))
|
||||||
|
location = candidate;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamquality.Append(HostStreamProxy(candidate), $"{q}p");
|
||||||
|
|
||||||
|
if (q > bestQuality)
|
||||||
|
{
|
||||||
|
bestQuality = q;
|
||||||
|
location = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tryExpandQualityVariants(string candidate, StreamQualityTpl streamquality, ref int bestQuality, ref string location)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(candidate, "/hls/\\d+/\\d+/(?<id>\\d+)\\.mp4/index\\.m3u8", RegexOptions.IgnoreCase);
|
||||||
|
if (!match.Success)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string id = match.Groups["id"].Value;
|
||||||
|
int endIdx = candidate.IndexOf($"{id}.mp4/index.m3u8", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (endIdx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string prefix = candidate.Substring(0, endIdx);
|
||||||
|
string suffix = candidate.Substring(endIdx + $"{id}.mp4/index.m3u8".Length);
|
||||||
|
|
||||||
|
foreach (int quality in new[] { 1080, 720, 480 })
|
||||||
|
{
|
||||||
|
string variant = $"{prefix}{id}_{quality}p.mp4/index.m3u8{suffix}";
|
||||||
|
streamquality.Append(HostStreamProxy(variant), $"{quality}p");
|
||||||
|
|
||||||
|
if (quality > bestQuality)
|
||||||
|
{
|
||||||
|
bestQuality = quality;
|
||||||
|
location = variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getQualityFromUrl(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(url))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var match = Regex.Match(url, "(2160|1440|1080|720|480|360|240)p", RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success && int.TryParse(match.Groups[1].Value, out int q))
|
||||||
|
return q;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCacheTtl(ref TimeSpan? cacheTtl, string url)
|
||||||
|
{
|
||||||
|
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var query = HttpUtility.ParseQueryString(uri.Query);
|
||||||
|
if (!long.TryParse(query["expires"], out long expires))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DateTimeOffset expiresAt;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
expiresAt = expires > 9999999999 ? DateTimeOffset.FromUnixTimeMilliseconds(expires) : DateTimeOffset.FromUnixTimeSeconds(expires);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ttl = expiresAt - DateTimeOffset.UtcNow - TimeSpan.FromSeconds(45);
|
||||||
|
if (ttl <= TimeSpan.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ttl < TimeSpan.FromSeconds(30))
|
||||||
|
ttl = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
if (!cacheTtl.HasValue || ttl < cacheTtl.Value)
|
||||||
|
cacheTtl = ttl;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan resolveCacheTtl(TimeSpan? cacheTtl, TimeSpan fallback)
|
||||||
|
{
|
||||||
|
if (!cacheTtl.HasValue)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (cacheTtl.Value <= TimeSpan.Zero)
|
||||||
|
return TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
return cacheTtl.Value < fallback ? cacheTtl.Value : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getFrontendDomain()
|
||||||
|
{
|
||||||
|
if (Uri.TryCreate(host, UriKind.Absolute, out var uri) && !string.IsNullOrEmpty(uri.Host))
|
||||||
|
return uri.Host;
|
||||||
|
|
||||||
|
if (Uri.TryCreate($"https://{host}", UriKind.Absolute, out var fallback) && !string.IsNullOrEmpty(fallback.Host))
|
||||||
|
return fallback.Host;
|
||||||
|
|
||||||
|
return "cm.vibix.biz";
|
||||||
|
}
|
||||||
|
|
||||||
#region search
|
#region search
|
||||||
async ValueTask<Video> search(string imdb_id, long kinopoisk_id)
|
async ValueTask<Video> search(string imdb_id, long kinopoisk_id)
|
||||||
@ -179,11 +737,111 @@ namespace Online.Controllers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(video.iframe_url) || string.IsNullOrEmpty(video.type))
|
if (string.IsNullOrEmpty(video.type))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(video.iframe_url) || !video.iframe_url.Contains("token="))
|
||||||
|
{
|
||||||
|
string iframe = getIframeFromEmbed(video.embed_code);
|
||||||
|
if (!string.IsNullOrEmpty(iframe))
|
||||||
|
video.iframe_url = iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(video.iframe_url))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return video;
|
return video;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region serials
|
||||||
|
async Task<SerialsRoot> getSerials(string imdb_id, long kinopoisk_id)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(imdb_id) && kinopoisk_id == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
string uri = kinopoisk_id > 0 ? $"kp/{kinopoisk_id}" : $"imdb/{imdb_id}";
|
||||||
|
|
||||||
|
var serials = await httpHydra.Get<SerialsRoot>($"{init.host}/api/v1/serials/{uri}", safety: true, addheaders: HeadersModel.Init(
|
||||||
|
("Accept", "application/json"),
|
||||||
|
("Authorization", $"Bearer {init.token}"),
|
||||||
|
("X-CSRF-TOKEN", "")
|
||||||
|
));
|
||||||
|
|
||||||
|
if (serials?.seasons == null)
|
||||||
|
{
|
||||||
|
proxyManager?.Refresh();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyManager?.Success();
|
||||||
|
return serials;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
string getIframeFromEmbed(string embed)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(embed))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
string iframe = Regex.Match(embed, "src=[\"'](?<url>https?://[^\"']+)", RegexOptions.IgnoreCase).Groups["url"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(iframe))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region searchV2
|
||||||
|
async Task<(Dictionary<string, JObject> seasons, string iframe)> searchV2(string imdb_id, long kinopoisk_id)
|
||||||
|
{
|
||||||
|
string imdbArg = !string.IsNullOrEmpty(imdb_id) ? $"&imdb={imdb_id}" : null;
|
||||||
|
string kpArg = kinopoisk_id > 0 ? $"&kp={kinopoisk_id}" : null;
|
||||||
|
|
||||||
|
JToken data = null;
|
||||||
|
foreach (var candidate in new (string item, string arg)[] { ("movie", kpArg), ("movie", imdbArg), ("serial", kpArg), ("serial", imdbArg) })
|
||||||
|
{
|
||||||
|
data = await goSearchV2(candidate.item, candidate.arg);
|
||||||
|
if (data != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
return (null, null);
|
||||||
|
|
||||||
|
string iframe = data.Value<string>("iframe");
|
||||||
|
var seasons = data["seasons"]?.ToObject<Dictionary<string, JObject>>();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(iframe) && seasons == null)
|
||||||
|
return (null, null);
|
||||||
|
|
||||||
|
proxyManager?.Success();
|
||||||
|
return (seasons, iframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<JToken> goSearchV2(string item, string arg)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(arg))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
long kp = 0;
|
||||||
|
string imdb = null;
|
||||||
|
if (arg.StartsWith("&kp=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
long.TryParse(arg[4..], out kp);
|
||||||
|
else if (arg.StartsWith("&imdb=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
imdb = arg[6..];
|
||||||
|
|
||||||
|
var video = await goSearch(imdb, kp);
|
||||||
|
if (video == null || !string.Equals(video.type, item, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(video.iframe_url))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
["iframe"] = video.iframe_url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,7 +119,7 @@ namespace Online.Controllers
|
|||||||
}
|
}
|
||||||
else if (init.priorityBrowser == "http")
|
else if (init.priorityBrowser == "http")
|
||||||
{
|
{
|
||||||
location = await Http.GetLocation(link, httpversion: init.httpversion, timeoutSeconds: init.httptimeout, proxy: proxy, headers: headers);
|
location = await Http.GetLocation(link, httpversion: init.GetHttpVersion(), timeoutSeconds: init.GetHttpTimeout(), proxy: proxy, headers: headers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
|
using System.Linq;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Shared.PlaywrightCore;
|
using Shared.PlaywrightCore;
|
||||||
|
|
||||||
@ -9,10 +10,23 @@ namespace Online.Controllers
|
|||||||
{
|
{
|
||||||
public Videoseed() : base(AppInit.conf.Videoseed) { }
|
public Videoseed() : base(AppInit.conf.Videoseed) { }
|
||||||
|
|
||||||
|
static int ExtractOrderNumber(string key)
|
||||||
|
{
|
||||||
|
string numericKey = Regex.Replace(key ?? string.Empty, "\\D", string.Empty);
|
||||||
|
return int.TryParse(numericKey, out int n) ? n : int.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetEscapedToken()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(init.token))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Uri.EscapeDataString(NormalizeToken(init.token));
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("lite/videoseed")]
|
[Route("lite/videoseed")]
|
||||||
async public Task<ActionResult> Index(string imdb_id, long kinopoisk_id, string title, string original_title, int year, int s = -1, bool rjson = false, int serial = -1)
|
async public Task<ActionResult> Index(string imdb_id, long kinopoisk_id, string title, string original_title, int year, int s = -1, bool rjson = false, int serial = -1, string t = null) {
|
||||||
{
|
|
||||||
if (PlaywrightBrowser.Status == PlaywrightStatus.disabled)
|
if (PlaywrightBrowser.Status == PlaywrightStatus.disabled)
|
||||||
return OnError();
|
return OnError();
|
||||||
|
|
||||||
@ -25,11 +39,24 @@ namespace Online.Controllers
|
|||||||
return await InvkSemaphore($"videoseed:view:{kinopoisk_id}:{imdb_id}:{original_title}", async key =>
|
return await InvkSemaphore($"videoseed:view:{kinopoisk_id}:{imdb_id}:{original_title}", async key =>
|
||||||
{
|
{
|
||||||
#region search
|
#region search
|
||||||
if (!hybridCache.TryGetValue(key, out (Dictionary<string, JObject> seasons, string iframe) cache))
|
if (!hybridCache.TryGetValue(key, out (Dictionary<string, JObject> seasons, string iframe, Dictionary<string, string> translations) cache))
|
||||||
{
|
{
|
||||||
var data = await goSearch(serial, kinopoisk_id > 0, $"&kp={kinopoisk_id}")
|
var candidates = new (bool ok, string arg)[]
|
||||||
?? await goSearch(serial, !string.IsNullOrEmpty(imdb_id), $"&tmdb={imdb_id}")
|
{
|
||||||
?? await goSearch(serial, !string.IsNullOrEmpty(original_title), $"&q={HttpUtility.UrlEncode(original_title)}&release_year_from={year - 1}&release_year_to={year + 1}");
|
(kinopoisk_id > 0, $"&kp={kinopoisk_id}"),
|
||||||
|
(!string.IsNullOrEmpty(imdb_id), $"&imdb={imdb_id}"),
|
||||||
|
(!string.IsNullOrEmpty(imdb_id), $"&imdb_id={imdb_id}"),
|
||||||
|
(!string.IsNullOrEmpty(imdb_id), $"&tmdb={imdb_id}"),
|
||||||
|
(!string.IsNullOrEmpty(original_title), $"&q={HttpUtility.UrlEncode(original_title)}&release_year_from={year - 1}&release_year_to={year + 1}")
|
||||||
|
};
|
||||||
|
|
||||||
|
JToken data = null;
|
||||||
|
foreach (var c in candidates)
|
||||||
|
{
|
||||||
|
data = await goSearch(serial, c.ok, c.arg);
|
||||||
|
if (data != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
@ -37,10 +64,15 @@ namespace Online.Controllers
|
|||||||
return OnError();
|
return OnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache.translations = data?["translation_iframe"]?.ToObject<Dictionary<string, JObject>>()
|
||||||
|
?.ToDictionary(item => item.Key, item => item.Value?.Value<string>("iframe"));
|
||||||
|
|
||||||
if (serial == 1)
|
if (serial == 1)
|
||||||
cache.seasons = data?["seasons"]?.ToObject<Dictionary<string, JObject>>();
|
cache.seasons = data?["seasons"]?.ToObject<Dictionary<string, JObject>>();
|
||||||
else
|
else
|
||||||
|
{
|
||||||
cache.iframe = data?.Value<string>("iframe");
|
cache.iframe = data?.Value<string>("iframe");
|
||||||
|
}
|
||||||
|
|
||||||
if (cache.seasons == null && string.IsNullOrEmpty(cache.iframe))
|
if (cache.seasons == null && string.IsNullOrEmpty(cache.iframe))
|
||||||
{
|
{
|
||||||
@ -59,6 +91,17 @@ namespace Online.Controllers
|
|||||||
var mtpl = new MovieTpl(title, original_title, 1);
|
var mtpl = new MovieTpl(title, original_title, 1);
|
||||||
mtpl.Append("По-умолчанию", accsArgs($"{host}/lite/videoseed/video/{AesTo.Encrypt(cache.iframe)}") + "#.m3u8", "call", vast: init.vast);
|
mtpl.Append("По-умолчанию", accsArgs($"{host}/lite/videoseed/video/{AesTo.Encrypt(cache.iframe)}") + "#.m3u8", "call", vast: init.vast);
|
||||||
|
|
||||||
|
if (cache.translations != null)
|
||||||
|
{
|
||||||
|
foreach (var translation in cache.translations)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(translation.Value) || translation.Value == cache.iframe)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mtpl.Append(translation.Key, accsArgs($"{host}/lite/videoseed/video/{AesTo.Encrypt(translation.Value)}") + "#.m3u8", "call", voice_name: translation.Key, vast: init.vast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return await ContentTpl(mtpl);
|
return await ContentTpl(mtpl);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -72,7 +115,9 @@ namespace Online.Controllers
|
|||||||
{
|
{
|
||||||
var tpl = new SeasonTpl(cache.seasons.Count);
|
var tpl = new SeasonTpl(cache.seasons.Count);
|
||||||
|
|
||||||
foreach (var season in cache.seasons)
|
foreach (var season in cache.seasons
|
||||||
|
.OrderBy(item => ExtractOrderNumber(item.Key))
|
||||||
|
.ThenBy(item => item.Key))
|
||||||
{
|
{
|
||||||
string link = $"{host}/lite/videoseed?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={season.Key}";
|
string link = $"{host}/lite/videoseed?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={season.Key}";
|
||||||
tpl.Append($"{season.Key} сезон", link, season.Key);
|
tpl.Append($"{season.Key} сезон", link, season.Key);
|
||||||
@ -83,13 +128,41 @@ namespace Online.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
string sArhc = s.ToString();
|
string sArhc = s.ToString();
|
||||||
|
string activeTranslation = t;
|
||||||
|
bool hasTranslations = false;
|
||||||
|
VoiceTpl vtpl = default;
|
||||||
|
|
||||||
|
if (cache.translations?.Count > 0)
|
||||||
|
{
|
||||||
|
vtpl = new VoiceTpl(cache.translations.Count);
|
||||||
|
hasTranslations = true;
|
||||||
|
foreach (var translation in cache.translations)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(activeTranslation))
|
||||||
|
activeTranslation = translation.Key;
|
||||||
|
|
||||||
|
string link = $"{host}/lite/videoseed?rjson={rjson}&kinopoisk_id={kinopoisk_id}&imdb_id={imdb_id}&title={enc_title}&original_title={enc_original_title}&s={s}&t={HttpUtility.UrlEncode(translation.Key)}";
|
||||||
|
vtpl.Append(translation.Key, activeTranslation == translation.Key, link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var videos = cache.seasons.First(i => i.Key == sArhc).Value["videos"].ToObject<Dictionary<string, JObject>>();
|
var videos = cache.seasons.First(i => i.Key == sArhc).Value["videos"].ToObject<Dictionary<string, JObject>>();
|
||||||
|
|
||||||
var etpl = new EpisodeTpl(videos.Count);
|
var etpl = hasTranslations ? new EpisodeTpl(vtpl, videos.Count) : new EpisodeTpl(videos.Count);
|
||||||
|
|
||||||
foreach (var video in videos)
|
string defaultAudio = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(activeTranslation) && cache.translations != null && cache.translations.TryGetValue(activeTranslation, out string translationIframe))
|
||||||
|
defaultAudio = ExtractDefaultAudio(translationIframe);
|
||||||
|
|
||||||
|
foreach (var video in videos
|
||||||
|
.OrderBy(item => ExtractOrderNumber(item.Key))
|
||||||
|
.ThenBy(item => item.Key))
|
||||||
{
|
{
|
||||||
string iframe = video.Value.Value<string>("iframe");
|
string iframe = video.Value.Value<string>("iframe");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(defaultAudio))
|
||||||
|
iframe = ApplyDefaultAudio(iframe, defaultAudio);
|
||||||
|
|
||||||
etpl.Append($"{video.Key} серия", title ?? original_title, sArhc, video.Key, accsArgs($"{host}/lite/videoseed/video/{AesTo.Encrypt(iframe)}"), "call", vast: init.vast);
|
etpl.Append($"{video.Key} серия", title ?? original_title, sArhc, video.Key, accsArgs($"{host}/lite/videoseed/video/{AesTo.Encrypt(iframe)}"), "call", vast: init.vast);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,61 +185,173 @@ namespace Online.Controllers
|
|||||||
if (string.IsNullOrEmpty(iframe))
|
if (string.IsNullOrEmpty(iframe))
|
||||||
return OnError();
|
return OnError();
|
||||||
|
|
||||||
iframe = Regex.Replace(iframe, "token=[a-z0-9]{32}", "token=00000000000000000000000000000000");
|
var tokenValue = GetEscapedToken();
|
||||||
iframe = Regex.Replace(iframe, "tv-[0-9]", "tv-2");
|
if (!string.IsNullOrEmpty(tokenValue))
|
||||||
|
{
|
||||||
|
if (iframe.Contains("token=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
iframe = Regex.Replace(iframe, "token=[^&]+", $"token={tokenValue}", RegexOptions.IgnoreCase);
|
||||||
|
else
|
||||||
|
iframe = $"{iframe}{(iframe.Contains('?') ? "&" : "?")}token={tokenValue}";
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe = NormalizeIframeParams(iframe);
|
||||||
|
|
||||||
return await InvkSemaphore($"videoseed:video:{iframe}:{proxyManager?.CurrentProxyIp}", async key =>
|
return await InvkSemaphore($"videoseed:video:{iframe}:{proxyManager?.CurrentProxyIp}", async key =>
|
||||||
{
|
{
|
||||||
|
const string hardUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36";
|
||||||
|
|
||||||
|
string iframeHost = Regex.Match(iframe, "(^https?://[^/]+)").Groups[1].Value;
|
||||||
|
if (string.IsNullOrEmpty(iframeHost))
|
||||||
|
iframeHost = init.host;
|
||||||
|
|
||||||
|
var proxyInfo = proxyManager?.BaseGet() ?? default;
|
||||||
|
|
||||||
if (!hybridCache.TryGetValue(key, out string location))
|
if (!hybridCache.TryGetValue(key, out string location))
|
||||||
{
|
{
|
||||||
var headers = httpHeaders(init, HeadersModel.Init
|
var headers = httpHeaders(init);
|
||||||
(
|
if (headers != null)
|
||||||
("referer", "encrypt:kwwsv=22ylghrvhhg1wy2")
|
headers.Add(new HeadersModel("User-Agent", hardUserAgent));
|
||||||
));
|
else
|
||||||
|
headers = HeadersModel.Init(("User-Agent", hardUserAgent));
|
||||||
|
|
||||||
|
string gotoReferer = string.IsNullOrEmpty(iframeHost) ? init.host : iframeHost;
|
||||||
|
if (!string.IsNullOrEmpty(gotoReferer) && !gotoReferer.EndsWith("/"))
|
||||||
|
gotoReferer += "/";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var browser = new PlaywrightBrowser(init.priorityBrowser))
|
using (var browser = new PlaywrightBrowser(init.priorityBrowser))
|
||||||
{
|
{
|
||||||
var page = await browser.NewPageAsync(init.plugin, proxy: proxy_data, headers: headers?.ToDictionary()).ConfigureAwait(false);
|
var page = await browser.NewPageAsync(init.plugin, proxy: proxyInfo.data, headers: headers?.ToDictionary()).ConfigureAwait(false);
|
||||||
if (page == null)
|
if (page == null)
|
||||||
return null;
|
return OnError();
|
||||||
|
|
||||||
await page.AddInitScriptAsync("localStorage.setItem('pljsquality', '1080p');").ConfigureAwait(false);
|
await page.AddInitScriptAsync("localStorage.setItem('pljsquality', '1080p');").ConfigureAwait(false);
|
||||||
|
|
||||||
|
var locationFinal = false;
|
||||||
|
|
||||||
|
page.Response += (_, response) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string url = response.Url;
|
||||||
|
if (!IsMediaUrl(url))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (response.Status == 200)
|
||||||
|
{
|
||||||
|
location = url;
|
||||||
|
locationFinal = true;
|
||||||
|
}
|
||||||
|
else if (response.Status == 301 || response.Status == 302)
|
||||||
|
{
|
||||||
|
if (response.Headers.TryGetValue("location", out string redirect))
|
||||||
|
{
|
||||||
|
location = ResolveRedirectUrl(url, redirect);
|
||||||
|
locationFinal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
};
|
||||||
|
|
||||||
await page.RouteAsync("**/*", async route =>
|
await page.RouteAsync("**/*", async route =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(location))
|
var rt = route.Request.ResourceType;
|
||||||
|
if (rt == "image" || rt == "font" || rt == "stylesheet")
|
||||||
{
|
{
|
||||||
await route.AbortAsync();
|
await route.AbortAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.Request.Url.Contains(".m3u8") || (route.Request.Url.Contains(".mp4") && !route.Request.Url.Contains(".ts")))
|
|
||||||
location = route.Request.Url;
|
|
||||||
|
|
||||||
if (await PlaywrightBase.AbortOrCache(page, route, abortMedia: true, fullCacheJS: true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
await route.ContinueAsync();
|
await route.ContinueAsync();
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var waitMedia = page.WaitForResponseAsync(r => IsMediaUrl(r.Url), new() { Timeout = 15_000 });
|
||||||
|
|
||||||
var options = new PageGotoOptions()
|
var options = new PageGotoOptions()
|
||||||
{
|
{
|
||||||
Timeout = 15_000,
|
Timeout = 15_000,
|
||||||
WaitUntil = WaitUntilState.NetworkIdle
|
WaitUntil = WaitUntilState.DOMContentLoaded,
|
||||||
|
Referer = gotoReferer
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await page.GotoAsync(iframe, options).ConfigureAwait(false);
|
var result = await page.GotoAsync(iframe, options).ConfigureAwait(false);
|
||||||
|
|
||||||
|
PlaywrightBase.WebLog("GOTO", iframe, result != null ? result.Status.ToString() : "null", proxy_data);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await page.EvaluateAsync(@"() => {
|
||||||
|
const v = document.querySelector('video');
|
||||||
|
if (v) {
|
||||||
|
try { v.muted = true; } catch(e) {}
|
||||||
|
try { v.play && v.play(); } catch(e) {}
|
||||||
|
}
|
||||||
|
const btn = document.querySelector('.vjs-big-play-button, .jw-icon-playback, .plyr__control--overlaid, .playerjs-play, .playerjs__play');
|
||||||
|
if (btn) { try { btn.click(); } catch(e) {} }
|
||||||
|
try { document.body && document.body.click(); } catch(e) {}
|
||||||
|
}");
|
||||||
|
|
||||||
|
await page.Mouse.ClickAsync(20, 20);
|
||||||
|
await page.Keyboard.PressAsync("Space");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mediaResp = await waitMedia.ConfigureAwait(false);
|
||||||
|
if (mediaResp != null && string.IsNullOrEmpty(location))
|
||||||
|
{
|
||||||
|
if (mediaResp.Status == 301 || mediaResp.Status == 302)
|
||||||
|
{
|
||||||
|
if (mediaResp.Headers.TryGetValue("location", out var redirect))
|
||||||
|
location = ResolveRedirectUrl(mediaResp.Url, redirect);
|
||||||
|
locationFinal = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
location = mediaResp.Url;
|
||||||
|
locationFinal = mediaResp.Status == 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
for (int i = 0; i < 40 && !locationFinal; i++)
|
||||||
|
await Task.Delay(250).ConfigureAwait(false);
|
||||||
if (result != null && string.IsNullOrEmpty(location))
|
if (result != null && string.IsNullOrEmpty(location))
|
||||||
{
|
{
|
||||||
string html = await page.ContentAsync().ConfigureAwait(false);
|
string html = await page.ContentAsync().ConfigureAwait(false);
|
||||||
location = Regex.Match(html, "<video preload=\"none\" src=\"(https?://[^\"]+)\"").Groups[1].Value;
|
location = Regex.Match(html, "<source[^>]+src=\"(https?://[^\"]+)\"", RegexOptions.IgnoreCase).Groups[1].Value;
|
||||||
if (!location.Contains(".m3u") && !location.Contains(".mp4"))
|
|
||||||
|
if (string.IsNullOrEmpty(location))
|
||||||
|
location = Regex.Match(html, "<video[^>]+src=\"(https?://[^\"]+)\"", RegexOptions.IgnoreCase).Groups[1].Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(location))
|
||||||
|
location = Regex.Match(html, "(https?:\\\\/\\\\/[^\"'\\s]+\\.m3u8[^\"'\\s]*)", RegexOptions.IgnoreCase).Groups[1].Value;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(location))
|
||||||
|
{
|
||||||
|
location = location.Replace("\\/", "/");
|
||||||
|
|
||||||
|
if (location.StartsWith("//"))
|
||||||
|
{
|
||||||
|
var iframeUri = new Uri(iframe);
|
||||||
|
location = $"{iframeUri.Scheme}:{location}";
|
||||||
|
}
|
||||||
|
else if (location.StartsWith("/"))
|
||||||
|
{
|
||||||
|
var iframeUri = new Uri(iframe);
|
||||||
|
location = new Uri(iframeUri, location).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(location) || (!location.Contains(".m3u") && !location.Contains(".mp4")))
|
||||||
location = null;
|
location = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,8 +364,9 @@ namespace Online.Controllers
|
|||||||
return OnError();
|
return OnError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
PlaywrightBase.WebLog("ERR", iframe, $"{ex.GetType().Name}: {ex.Message}", proxy_data);
|
||||||
return OnError();
|
return OnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,10 +374,22 @@ namespace Online.Controllers
|
|||||||
hybridCache.Set(key, location, cacheTime(20));
|
hybridCache.Set(key, location, cacheTime(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
string referer = Regex.Match(iframe, "(^https?://[^/]+)").Groups[1].Value;
|
string streamReferer = string.IsNullOrEmpty(iframeHost) ? iframe : (iframeHost.EndsWith("/") ? iframeHost : iframeHost + "/");
|
||||||
var headers_stream = httpHeaders(init.corsHost(), HeadersModel.Join(HeadersModel.Init("referer", referer), init.headers_stream));
|
var headers_stream = HeadersModel.Join(
|
||||||
|
HeadersModel.Init(
|
||||||
|
("User-Agent", hardUserAgent),
|
||||||
|
("Referer", streamReferer),
|
||||||
|
("Origin", iframeHost),
|
||||||
|
("Accept", "*/*")
|
||||||
|
),
|
||||||
|
init.headers_stream
|
||||||
|
);
|
||||||
|
|
||||||
|
string link = HostStreamProxy(init, location, headers_stream, proxyInfo.proxy, force_streamproxy: true, rch);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(location) && location.Contains(".m3u8", StringComparison.OrdinalIgnoreCase))
|
||||||
|
link += "#.m3u8";
|
||||||
|
|
||||||
string link = HostStreamProxy(location, headers: headers_stream);
|
|
||||||
return ContentTo(VideoTpl.ToJson("play", link, "auto", vast: init.vast));
|
return ContentTo(VideoTpl.ToJson("play", link, "auto", vast: init.vast));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -203,8 +401,15 @@ namespace Online.Controllers
|
|||||||
if (!isOk)
|
if (!isOk)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var root = await httpHydra.Get<JObject>($"{init.corsHost()}/apiv2.php?item={(serial == 1 ? "serial" : "movie")}&token={init.token}" + arg, safety: true);
|
string apiHost = init.apihost ?? init.host;
|
||||||
|
string tokenValue = GetEscapedToken() ?? "";
|
||||||
|
var root = await httpHydra.Get<JObject>(
|
||||||
|
$"{init.cors(apiHost)}/apiv2.php?item={(serial == 1 ? "serial" : "movie")}&token={tokenValue}" + arg,
|
||||||
|
safety: true,
|
||||||
|
addheaders: HeadersModel.Init(
|
||||||
|
("referer", apiHost),
|
||||||
|
("origin", apiHost)
|
||||||
|
));
|
||||||
if (root == null || !root.ContainsKey("data") || root.Value<string>("status") == "error")
|
if (root == null || !root.ContainsKey("data") || root.Value<string>("status") == "error")
|
||||||
{
|
{
|
||||||
proxyManager?.Refresh();
|
proxyManager?.Refresh();
|
||||||
@ -214,5 +419,86 @@ namespace Online.Controllers
|
|||||||
return root["data"]?.First;
|
return root["data"]?.First;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
static string NormalizeIframeParams(string iframe)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(iframe))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
return Regex.Replace(iframe, "default_audio=([^&]+)", match =>
|
||||||
|
{
|
||||||
|
string value = match.Groups[1].Value;
|
||||||
|
if (string.IsNullOrEmpty(value) || value.Contains("%"))
|
||||||
|
return match.Value;
|
||||||
|
|
||||||
|
return $"default_audio={Uri.EscapeDataString(value)}";
|
||||||
|
}, RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string NormalizeToken(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
return token;
|
||||||
|
|
||||||
|
int separatorIndex = token.LastIndexOf('|');
|
||||||
|
if (separatorIndex >= 0 && separatorIndex < token.Length - 1)
|
||||||
|
return token[(separatorIndex + 1)..];
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
static string ExtractDefaultAudio(string iframe)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(iframe))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (Uri.TryCreate(iframe, UriKind.Absolute, out var uri))
|
||||||
|
{
|
||||||
|
var query = HttpUtility.ParseQueryString(uri.Query);
|
||||||
|
return query.Get("default_audio");
|
||||||
|
}
|
||||||
|
|
||||||
|
var match = Regex.Match(iframe, "default_audio=([^&]+)", RegexOptions.IgnoreCase);
|
||||||
|
return match.Success ? Uri.UnescapeDataString(match.Groups[1].Value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ApplyDefaultAudio(string iframe, string defaultAudio)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(iframe) || string.IsNullOrEmpty(defaultAudio))
|
||||||
|
return iframe;
|
||||||
|
|
||||||
|
string encoded = Uri.EscapeDataString(defaultAudio);
|
||||||
|
if (iframe.Contains("default_audio=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return Regex.Replace(iframe, "default_audio=[^&]+", $"default_audio={encoded}", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
return $"{iframe}{(iframe.Contains('?') ? "&" : "?")}default_audio={encoded}";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsMediaUrl(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(url))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return url.Contains(".m3u8") || (url.Contains(".mp4") && !url.Contains(".ts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ResolveRedirectUrl(string requestUrl, string redirect)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(redirect))
|
||||||
|
return redirect;
|
||||||
|
|
||||||
|
if (redirect.StartsWith("//", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var requestUri = new Uri(requestUrl);
|
||||||
|
return $"{requestUri.Scheme}:{redirect}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirect.StartsWith("/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var requestUri = new Uri(requestUrl);
|
||||||
|
return new Uri(requestUri, redirect).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ namespace SISI.Controllers.Ebalovo
|
|||||||
return res.currentUrl;
|
return res.currentUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Http.GetLocation(init.cors(location), timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: httpHeaders(init));
|
return await Http.GetLocation(init.cors(location), timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -613,13 +613,13 @@ namespace SISI.Controllers.NextHUB
|
|||||||
|
|
||||||
return rch?.enable == true
|
return rch?.enable == true
|
||||||
? await rch.Post(url.Replace("{page}", pg.ToString()), data, httpHeaders(init))
|
? await rch.Post(url.Replace("{page}", pg.ToString()), data, httpHeaders(init))
|
||||||
: await Http.Post(url.Replace("{page}", pg.ToString()), data, encoding: encodingResponse, headers: httpHeaders(init), proxy: proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion);
|
: await Http.Post(url.Replace("{page}", pg.ToString()), data, encoding: encodingResponse, headers: httpHeaders(init), proxy: proxy, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return rch?.enable == true
|
return rch?.enable == true
|
||||||
? await rch.Get(url.Replace("{page}", pg.ToString()), httpHeaders(init))
|
? await rch.Get(url.Replace("{page}", pg.ToString()), httpHeaders(init))
|
||||||
: init.priorityBrowser == "http" ? await Http.Get(url.Replace("{page}", pg.ToString()), encoding: encodingResponse, headers: httpHeaders(init), proxy: proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion)
|
: init.priorityBrowser == "http" ? await Http.Get(url.Replace("{page}", pg.ToString()), encoding: encodingResponse, headers: httpHeaders(init), proxy: proxy, timeoutSeconds: init.timeout, httpversion: init.GetHttpVersion())
|
||||||
: init.list.viewsource ? await PlaywrightBrowser.Get(init, url.Replace("{page}", pg.ToString()), httpHeaders(init), proxy_data, cookies: init.cookies)
|
: init.list.viewsource ? await PlaywrightBrowser.Get(init, url.Replace("{page}", pg.ToString()), httpHeaders(init), proxy_data, cookies: init.cookies)
|
||||||
: await ContentAsync(init, url.Replace("{page}", pg.ToString()), httpHeaders(init), proxy_data, search, sort, cat, model, pg);
|
: await ContentAsync(init, url.Replace("{page}", pg.ToString()), httpHeaders(init), proxy_data, search, sort, cat, model, pg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,7 @@ namespace SISI.Controllers.Porntrex
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
location = await Http.GetLocation(init.cors(link), timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers);
|
location = await Http.GetLocation(init.cors(link), timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(location) || link == location)
|
if (string.IsNullOrEmpty(location) || link == location)
|
||||||
|
|||||||
@ -45,7 +45,7 @@ namespace SISI.Controllers.XvideosRED
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
string html = await Http.Get(init.cors(url), cookie: init.cookie, timeoutSeconds: init.httptimeout, proxy: proxy, headers: httpHeaders(init));
|
string html = await Http.Get(init.cors(url), cookie: init.cookie, timeoutSeconds: init.GetHttpVersion(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
if (html == null)
|
if (html == null)
|
||||||
return OnError("html", refresh_proxy: string.IsNullOrEmpty(search));
|
return OnError("html", refresh_proxy: string.IsNullOrEmpty(search));
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ namespace SISI.Controllers.XvideosRED
|
|||||||
if (url == null)
|
if (url == null)
|
||||||
return OnError("stream_links");
|
return OnError("stream_links");
|
||||||
|
|
||||||
string html = await Http.Get(url, cookie: init.cookie, timeoutSeconds: init.httptimeout, proxy: proxy, headers: httpHeaders(init));
|
string html = await Http.Get(url, cookie: init.cookie, timeoutSeconds: init.GetHttpVersion(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: httpHeaders(init));
|
||||||
if (html == null)
|
if (html == null)
|
||||||
return OnError("stream_links");
|
return OnError("stream_links");
|
||||||
|
|
||||||
|
|||||||
@ -464,7 +464,7 @@ namespace Shared
|
|||||||
|
|
||||||
public SyncUserConf sync_user = new SyncUserConf() { enable = true, version = 2 };
|
public SyncUserConf sync_user = new SyncUserConf() { enable = true, version = 2 };
|
||||||
|
|
||||||
public StorageConf storage = new StorageConf() { enable = true, max_size = 7_000000, brotli = false, md5name = true };
|
public StorageConf storage = new StorageConf() { enable = true, max_size = 7_000000, brotli = false, };
|
||||||
|
|
||||||
public GCConf GC { get; set; } = new GCConf()
|
public GCConf GC { get; set; } = new GCConf()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,10 +24,12 @@ namespace Shared
|
|||||||
{
|
{
|
||||||
public class BaseController : Controller
|
public class BaseController : Controller
|
||||||
{
|
{
|
||||||
public static string appversion => "154";
|
|
||||||
|
|
||||||
public static string minorversion => "3";
|
public static string versionTag => "ce";
|
||||||
|
|
||||||
|
public static string appversion => "155";
|
||||||
|
|
||||||
|
public static string minorversion => "1";
|
||||||
|
|
||||||
protected static readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new();
|
protected static readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new();
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,8 @@ namespace Shared.Engine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class FFmpeg
|
public static class FFmpeg
|
||||||
{
|
{
|
||||||
|
private const string baseDownloadUrl = "https://github.com/lampac-talks/lampac/releases/download/ffmpeg";
|
||||||
|
|
||||||
#region InitializationAsync
|
#region InitializationAsync
|
||||||
static bool disableInstall = false;
|
static bool disableInstall = false;
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ namespace Shared.Engine
|
|||||||
if (File.Exists($"data/{fileName}.exe"))
|
if (File.Exists($"data/{fileName}.exe"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!await Http.DownloadFile($"https://github.com/immisterio/ffmpeg/releases/download/ffmpeg2/{fileName}-win{arh}-gpl.exe", $"data/{fileName}.exe"))
|
if (!await Http.DownloadFile($"{baseDownloadUrl}/{fileName}-win{arh}-gpl.exe", $"data/{fileName}.exe"))
|
||||||
File.Delete($"data/{fileName}.exe");
|
File.Delete($"data/{fileName}.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ namespace Shared.Engine
|
|||||||
if (File.Exists($"data/{fileName}"))
|
if (File.Exists($"data/{fileName}"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!await Http.DownloadFile($"https://github.com/immisterio/ffmpeg/releases/download/ffmpeg2/{fileName}-linux{arh}-gpl", $"data/{fileName}"))
|
if (!await Http.DownloadFile($"{baseDownloadUrl}/{fileName}-linux{arh}-gpl", $"data/{fileName}"))
|
||||||
{
|
{
|
||||||
File.Delete($"data/{fileName}");
|
File.Delete($"data/{fileName}");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.Get<T>(init.cors(url), headers, IgnoreDeserializeObject, useDefaultHeaders)
|
? rch.Get<T>(init.cors(url), headers, IgnoreDeserializeObject, useDefaultHeaders)
|
||||||
: Http.Get<T>(init.cors(url), timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.Get<T>(init.cors(url), timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> Get(string url, List<HeadersModel> addheaders = null, List<HeadersModel> newheaders = null, bool useDefaultHeaders = true, bool statusCodeOK = true, Encoding encoding = default, bool safety = false)
|
public Task<string> Get(string url, List<HeadersModel> addheaders = null, List<HeadersModel> newheaders = null, bool useDefaultHeaders = true, bool statusCodeOK = true, Encoding encoding = default, bool safety = false)
|
||||||
@ -36,7 +36,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.Get(init.cors(url), headers, useDefaultHeaders)
|
? rch.Get(init.cors(url), headers, useDefaultHeaders)
|
||||||
: Http.Get(init.cors(url), encoding, timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.Get(init.cors(url), encoding, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.GetSpan(spanAction, init.cors(url), headers, useDefaultHeaders)
|
? rch.GetSpan(spanAction, init.cors(url), headers, useDefaultHeaders)
|
||||||
: Http.GetSpan(spanAction, init.cors(url), timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.GetSpan(spanAction, init.cors(url), timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.Post<T>(init.cors(url), data, headers, IgnoreDeserializeObject, useDefaultHeaders)
|
? rch.Post<T>(init.cors(url), data, headers, IgnoreDeserializeObject, useDefaultHeaders)
|
||||||
: Http.Post<T>(init.cors(url), data, encoding: encoding, timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.Post<T>(init.cors(url), data, encoding: encoding, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> Post(string url, string data, List<HeadersModel> addheaders = null, List<HeadersModel> newheaders = null, bool useDefaultHeaders = true, bool statusCodeOK = true, Encoding encoding = default, bool safety = false)
|
public Task<string> Post(string url, string data, List<HeadersModel> addheaders = null, List<HeadersModel> newheaders = null, bool useDefaultHeaders = true, bool statusCodeOK = true, Encoding encoding = default, bool safety = false)
|
||||||
@ -67,7 +67,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.Post(init.cors(url), data, headers, useDefaultHeaders)
|
? rch.Post(init.cors(url), data, headers, useDefaultHeaders)
|
||||||
: Http.Post(init.cors(url), data, encoding: encoding, timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.Post(init.cors(url), data, encoding: encoding, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ namespace Shared.Engine
|
|||||||
|
|
||||||
return IsRchEnable(safety)
|
return IsRchEnable(safety)
|
||||||
? rch.PostSpan(spanAction, init.cors(url), data, headers, useDefaultHeaders)
|
? rch.PostSpan(spanAction, init.cors(url), data, headers, useDefaultHeaders)
|
||||||
: Http.PostSpan(spanAction, init.cors(url), data, encoding: encoding, timeoutSeconds: init.httptimeout, httpversion: init.httpversion, proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
: Http.PostSpan(spanAction, init.cors(url), data, encoding: encoding, timeoutSeconds: init.GetHttpTimeout(), httpversion: init.GetHttpVersion(), proxy: proxy, headers: headers, useDefaultHeaders: useDefaultHeaders, statusCodeOK: statusCodeOK);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ namespace Shared.Engine.Online
|
|||||||
|
|
||||||
string link = null;
|
string link = null;
|
||||||
|
|
||||||
var rx = Rx.Split("<li class=\"item\">", content, 1);
|
var rx = Rx.Matches("<li class=\"[^\"]*\\bitem\\b[^\"]*\">.*?</li>", content, 0, RegexOptions.CultureInvariant | RegexOptions.Singleline);
|
||||||
|
|
||||||
var similar = new SimilarTpl(rx.Count);
|
var similar = new SimilarTpl(rx.Count);
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ namespace Shared.Engine.Online
|
|||||||
|
|
||||||
string name = row.Match("<div class=\"title\"><[^>]+>([^<]+)");
|
string name = row.Match("<div class=\"title\"><[^>]+>([^<]+)");
|
||||||
string _year = row.Match("<span class=\"year\">([0-9]+)");
|
string _year = row.Match("<span class=\"year\">([0-9]+)");
|
||||||
string img = row.Match("<img src=\"/([^\"]+)\"");
|
string img = row.Match("<img[^>]+(?:data-src|src)=\"/([^\"]+)\"");
|
||||||
if (!string.IsNullOrEmpty(img))
|
if (!string.IsNullOrEmpty(img))
|
||||||
img = $"{apihost}/{img}";
|
img = $"{apihost}/{img}";
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ namespace Shared.Engine.Online
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string video = Regex.Match(news, "id=\"playerjsfile\">([^<]+)<").Groups[1].Value;
|
string video = Regex.Match(news, "id=\"playerjsfile[^\"]*\">([^<]+)<").Groups[1].Value;
|
||||||
if (string.IsNullOrEmpty(video))
|
if (string.IsNullOrEmpty(video))
|
||||||
{
|
{
|
||||||
if (news.Contains("<div class=\"alert\""))
|
if (news.Contains("<div class=\"alert\""))
|
||||||
|
|||||||
161
Shared/Engine/StorageManager.cs
Normal file
161
Shared/Engine/StorageManager.cs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Shared.Models;
|
||||||
|
|
||||||
|
namespace Shared.Engine
|
||||||
|
{
|
||||||
|
public static class StorageManager
|
||||||
|
{
|
||||||
|
private const string TempDirectoryName = "temp";
|
||||||
|
|
||||||
|
private static readonly string BaseStoragePath =
|
||||||
|
Path.GetFullPath("database/storage");
|
||||||
|
|
||||||
|
private static readonly string BaseTempStoragePath = $"{BaseStoragePath}/{TempDirectoryName}";
|
||||||
|
|
||||||
|
static StorageManager()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(BaseStoragePath);
|
||||||
|
Directory.CreateDirectory(BaseTempStoragePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetFilePath(
|
||||||
|
string pathType,
|
||||||
|
bool createDirectory = false,
|
||||||
|
RequestModel? requestInfo = null,
|
||||||
|
string? profileUid = null)
|
||||||
|
{
|
||||||
|
return GetFilePath(pathType, createDirectory, requestInfo?.user_uid, profileUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetFilePath(
|
||||||
|
string pathType,
|
||||||
|
bool createDirectory = false,
|
||||||
|
string? userUid = null,
|
||||||
|
string? profileUid = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(userUid))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cleanedUserId = CleanIdentifier(userUid);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(cleanedUserId))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cleanedProfileUid = CleanIdentifier(profileUid ?? string.Empty);
|
||||||
|
string hashInput = cleanedUserId + cleanedProfileUid;
|
||||||
|
string md5Key = CrypTo.md5(hashInput);
|
||||||
|
|
||||||
|
string safePathType = CleanPathType(pathType);
|
||||||
|
if (string.IsNullOrEmpty(safePathType))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string fullPath;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (safePathType == TempDirectoryName)
|
||||||
|
{
|
||||||
|
fullPath = $"{BaseStoragePath}/{TempDirectoryName}/{md5Key}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string level1 = md5Key[..2];
|
||||||
|
string level2 = md5Key[2..];
|
||||||
|
|
||||||
|
string directoryPath = $"{BaseStoragePath}/{safePathType}/{level1}";
|
||||||
|
fullPath = $"{directoryPath}/{level2}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullPath.StartsWith(BaseStoragePath + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(fullPath, BaseStoragePath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createDirectory || safePathType == TempDirectoryName)
|
||||||
|
{
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string directoryPath = Path.GetDirectoryName(fullPath);
|
||||||
|
if (!string.IsNullOrEmpty(directoryPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore all exceptions
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CleanIdentifier(string? input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var sb = new StringBuilder(input.Length);
|
||||||
|
|
||||||
|
foreach (var c in input.Where(c => char.IsLetterOrDigit(c)
|
||||||
|
|| c is '-' or '_' or '@' or '.' or '+' or '='))
|
||||||
|
{
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
string cleaned = sb.ToString();
|
||||||
|
|
||||||
|
if (cleaned.Length is 0 or > 160
|
||||||
|
|| cleaned.StartsWith(".")
|
||||||
|
|| cleaned.EndsWith(".")
|
||||||
|
|| cleaned.Contains("..")
|
||||||
|
|| cleaned.Contains("//")
|
||||||
|
|| cleaned.Contains(@"\")
|
||||||
|
|| cleaned.Contains(":"))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
private static string CleanPathType(string? input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var c in input.ToLowerInvariant().Where(c => char.IsLetterOrDigit(c)
|
||||||
|
|| c is '-' or '_'))
|
||||||
|
{
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
string cleaned = sb.ToString();
|
||||||
|
|
||||||
|
if (cleaned.Length is 0 or > 40
|
||||||
|
|| cleaned.Contains("..")
|
||||||
|
|| cleaned.StartsWith("-")
|
||||||
|
|| cleaned.EndsWith("-"))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,5 @@
|
|||||||
public long max_size { get; set; }
|
public long max_size { get; set; }
|
||||||
|
|
||||||
public bool brotli { get; set; }
|
public bool brotli { get; set; }
|
||||||
|
|
||||||
public bool md5name { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,10 +138,13 @@ namespace Shared.Models.Base
|
|||||||
|
|
||||||
public string priorityBrowser { get; set; }
|
public string priorityBrowser { get; set; }
|
||||||
|
|
||||||
public int httptimeout { get; set; } = 8;
|
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public int? httptimeout { get; set; }
|
||||||
public int httpversion { get; set; } = 1;
|
public int GetHttpTimeout() => httpversion ?? 8;
|
||||||
|
|
||||||
|
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public int? httpversion { get; set; }
|
||||||
|
public int GetHttpVersion() => httpversion ?? 1;
|
||||||
|
|
||||||
#region proxy
|
#region proxy
|
||||||
public bool useproxy { get; set; }
|
public bool useproxy { get; set; }
|
||||||
|
|||||||
@ -28,15 +28,20 @@ namespace Shared.Models.Templates
|
|||||||
Append(item.link, item.quality);
|
Append(item.link, item.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void EnsureData()
|
||||||
|
{
|
||||||
|
data ??= new List<StreamQualityDto>(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Any() => data != null && data.Any();
|
||||||
public bool Any() => data.Any();
|
|
||||||
|
|
||||||
public void Append(string link, string quality)
|
public void Append(string link, string quality)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(link) || string.IsNullOrEmpty(quality))
|
if (string.IsNullOrEmpty(link) || string.IsNullOrEmpty(quality))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
EnsureData();
|
||||||
|
|
||||||
if (InvkEvent.IsStreamQuality())
|
if (InvkEvent.IsStreamQuality())
|
||||||
{
|
{
|
||||||
var eventResult = InvkEvent.StreamQuality(new EventStreamQuality(link, quality, prepend: false));
|
var eventResult = InvkEvent.StreamQuality(new EventStreamQuality(link, quality, prepend: false));
|
||||||
@ -55,6 +60,8 @@ namespace Shared.Models.Templates
|
|||||||
if (string.IsNullOrEmpty(link) || string.IsNullOrEmpty(quality))
|
if (string.IsNullOrEmpty(link) || string.IsNullOrEmpty(quality))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
EnsureData();
|
||||||
|
|
||||||
if (InvkEvent.IsStreamQuality())
|
if (InvkEvent.IsStreamQuality())
|
||||||
{
|
{
|
||||||
var eventResult = InvkEvent.StreamQuality(new EventStreamQuality(link, quality, prepend: true));
|
var eventResult = InvkEvent.StreamQuality(new EventStreamQuality(link, quality, prepend: true));
|
||||||
@ -72,6 +79,8 @@ namespace Shared.Models.Templates
|
|||||||
|
|
||||||
public Dictionary<string, string> ToObject(bool emptyToNull = false)
|
public Dictionary<string, string> ToObject(bool emptyToNull = false)
|
||||||
{
|
{
|
||||||
|
EnsureData();
|
||||||
|
|
||||||
if (emptyToNull && data.Count == 0)
|
if (emptyToNull && data.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -85,6 +94,8 @@ namespace Shared.Models.Templates
|
|||||||
|
|
||||||
public string MaxQuality()
|
public string MaxQuality()
|
||||||
{
|
{
|
||||||
|
EnsureData();
|
||||||
|
|
||||||
if (data.Count == 0)
|
if (data.Count == 0)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
@ -93,6 +104,8 @@ namespace Shared.Models.Templates
|
|||||||
|
|
||||||
public StreamQualityDto Firts()
|
public StreamQualityDto Firts()
|
||||||
{
|
{
|
||||||
|
EnsureData();
|
||||||
|
|
||||||
if (data.Count == 0)
|
if (data.Count == 0)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.X64:
|
case Architecture.X64:
|
||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/chrome-win-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
string uri = $"{baseDownloadUrl}/chrome-win-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.X64:
|
case Architecture.X64:
|
||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/chrome-mac-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
string uri = $"{PlaywrightBase.baseDownloadUrl}/chrome-mac-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -114,7 +114,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.X86:
|
case Architecture.X86:
|
||||||
case Architecture.X64:
|
case Architecture.X64:
|
||||||
{
|
{
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/chrome-linux-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
string uri = $"{PlaywrightBase.baseDownloadUrl}/chrome-linux-{RuntimeInformation.ProcessArchitecture.ToString().ToLower()}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
bool res = await DownloadFile(uri, ".playwright/chrome.zip");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -45,7 +45,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.X64:
|
case Architecture.X64:
|
||||||
{
|
{
|
||||||
string camoufox = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "x86_64" : "i686";
|
string camoufox = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "x86_64" : "i686";
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/camoufox-135.0.1-beta.23-win.{camoufox}.zip";
|
string uri = $"{baseDownloadUrl}/camoufox-135.0.1-beta.23-win.{camoufox}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/firefox/release.zip", "firefox/");
|
bool res = await DownloadFile(uri, ".playwright/firefox/release.zip", "firefox/");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -69,7 +69,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string camoufox = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "x86_64" : "arm64";
|
string camoufox = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "x86_64" : "arm64";
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/camoufox-135.0.1-beta.23-mac.{camoufox}.zip";
|
string uri = $"{baseDownloadUrl}/camoufox-135.0.1-beta.23-mac.{camoufox}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/camoufox.zip");
|
bool res = await DownloadFile(uri, ".playwright/camoufox.zip");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -109,7 +109,7 @@ namespace Shared.PlaywrightCore
|
|||||||
|
|
||||||
if (camoufox != null)
|
if (camoufox != null)
|
||||||
{
|
{
|
||||||
string uri = $"https://github.com/immisterio/playwright/releases/download/chrome/camoufox-135.0.1-beta.23-lin.{camoufox}.zip";
|
string uri = $"{baseDownloadUrl}/camoufox-135.0.1-beta.23-lin.{camoufox}.zip";
|
||||||
bool res = await DownloadFile(uri, ".playwright/camoufox.zip", "firefox/");
|
bool res = await DownloadFile(uri, ".playwright/camoufox.zip", "firefox/");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -21,6 +21,8 @@ namespace Shared.PlaywrightCore
|
|||||||
|
|
||||||
public class PlaywrightBase
|
public class PlaywrightBase
|
||||||
{
|
{
|
||||||
|
protected const string baseDownloadUrl = "https://github.com/lampac-talks/lampac/releases/download/browsers";
|
||||||
|
|
||||||
static DateTime _nextClearDb = default;
|
static DateTime _nextClearDb = default;
|
||||||
|
|
||||||
public TaskCompletionSource<string> completionSource { get; private set; } = new TaskCompletionSource<string>();
|
public TaskCompletionSource<string> completionSource { get; private set; } = new TaskCompletionSource<string>();
|
||||||
@ -55,7 +57,7 @@ namespace Shared.PlaywrightCore
|
|||||||
|
|
||||||
if (!File.Exists(".playwright/package/index.js"))
|
if (!File.Exists(".playwright/package/index.js"))
|
||||||
{
|
{
|
||||||
bool res = await DownloadFile("https://github.com/immisterio/playwright/releases/download/chrome/package.zip", ".playwright/package.zip");
|
bool res = await DownloadFile($"{baseDownloadUrl}/package.zip", ".playwright/package.zip");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Playwright: error download package.zip");
|
Console.WriteLine("Playwright: error download package.zip");
|
||||||
@ -72,7 +74,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
||||||
bool res = await DownloadFile($"https://github.com/immisterio/playwright/releases/download/chrome/node-win-{arc}.exe", $".playwright\\node\\win32_{arc}\\node.exe");
|
bool res = await DownloadFile($"{baseDownloadUrl}/node-win-{arc}.exe", $".playwright\\node\\win32_{arc}\\node.exe");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Playwright: error download node-win-{arc}.exe");
|
Console.WriteLine($"Playwright: error download node-win-{arc}.exe");
|
||||||
@ -93,7 +95,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
||||||
bool res = await DownloadFile($"https://github.com/immisterio/playwright/releases/download/chrome/node-mac-{arc}", $".playwright/node/mac-{arc}/node");
|
bool res = await DownloadFile($"{baseDownloadUrl}/node-mac-{arc}", $".playwright/node/mac-{arc}/node");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Playwright: error download node-mac-{arc}");
|
Console.WriteLine($"Playwright: error download node-mac-{arc}");
|
||||||
@ -118,7 +120,7 @@ namespace Shared.PlaywrightCore
|
|||||||
case Architecture.Arm64:
|
case Architecture.Arm64:
|
||||||
{
|
{
|
||||||
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
string arc = RuntimeInformation.ProcessArchitecture.ToString().ToLower();
|
||||||
bool res = await DownloadFile($"https://github.com/immisterio/playwright/releases/download/chrome/node-linux-{arc}", $".playwright/node/linux-{arc}/node");
|
bool res = await DownloadFile($"{baseDownloadUrl}/node-linux-{arc}", $".playwright/node/linux-{arc}/node");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Playwright: error download node-linux-{arc}");
|
Console.WriteLine($"Playwright: error download node-linux-{arc}");
|
||||||
@ -131,7 +133,7 @@ namespace Shared.PlaywrightCore
|
|||||||
}
|
}
|
||||||
case Architecture.Arm:
|
case Architecture.Arm:
|
||||||
{
|
{
|
||||||
bool res = await DownloadFile("https://github.com/immisterio/playwright/releases/download/chrome/node-linux-armv7l", ".playwright/node/linux-arm/node");
|
bool res = await DownloadFile($"{baseDownloadUrl}/node-linux-armv7l", ".playwright/node/linux-arm/node");
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Playwright: error download node-linux-armv7l");
|
Console.WriteLine("Playwright: error download node-linux-armv7l");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user