mirror of
https://github.com/lampame/lampac-ukraine.git
synced 2026-04-16 17:32:20 +00:00
Compare commits
4 Commits
4d4ac22601
...
1c49df634a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c49df634a | ||
|
|
cdc83ac49a | ||
|
|
c55ad64ee3 | ||
|
|
bc1fbac530 |
@ -62,6 +62,9 @@ modules - optional, if not specified, all modules from the repository will be in
|
||||
"enable": true,
|
||||
"domain": "https://uaflix.net",
|
||||
"displayname": "Uaflix",
|
||||
"login": null,
|
||||
"passwd": null,
|
||||
"cookie": null,
|
||||
"webcorshost": null,
|
||||
"streamproxy": false,
|
||||
"useproxy": false,
|
||||
|
||||
@ -18,7 +18,7 @@ using Uaflix.Models;
|
||||
namespace Uaflix.Controllers
|
||||
{
|
||||
|
||||
public class Controller : BaseOnlineController
|
||||
public class Controller : BaseOnlineController<UaflixSettings>
|
||||
{
|
||||
ProxyManager proxyManager;
|
||||
|
||||
@ -42,7 +42,8 @@ namespace Uaflix.Controllers
|
||||
OnLog($"Uaflix Index: kinopoisk_id={kinopoisk_id}, imdb_id={imdb_id}, id={id}");
|
||||
OnLog($"Uaflix Index: year={year}, source={source}, t={t}, e={e}, rjson={rjson}");
|
||||
|
||||
var invoke = new UaflixInvoke(init, hybridCache, OnLog, proxyManager);
|
||||
var auth = new UaflixAuth(init, memoryCache, OnLog, proxyManager);
|
||||
var invoke = new UaflixInvoke(init, hybridCache, OnLog, proxyManager, auth);
|
||||
|
||||
// Обробка параметра checksearch - повертаємо спеціальну відповідь для валідації
|
||||
if (checksearch)
|
||||
@ -52,32 +53,21 @@ namespace Uaflix.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
string filmTitle = !string.IsNullOrEmpty(title) ? title : original_title;
|
||||
string searchUrl = $"{init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(filmTitle)}";
|
||||
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) };
|
||||
|
||||
var searchHtml = await Http.Get(init.cors(searchUrl), headers: headers, proxy: proxyManager.Get(), timeoutSeconds: 10);
|
||||
|
||||
// Швидка перевірка наявності результатів без повного парсингу
|
||||
if (!string.IsNullOrEmpty(searchHtml) &&
|
||||
(searchHtml.Contains("sres-wrap") || searchHtml.Contains("sres-item") || searchHtml.Contains("search-results")))
|
||||
bool hasContent = await invoke.CheckSearchAvailability(title, original_title);
|
||||
if (hasContent)
|
||||
{
|
||||
// Якщо знайдено контент, повертаємо "data-json=" для валідації
|
||||
OnLog("checksearch: Content found, returning validation response");
|
||||
OnLog("checksearch: Контент знайдено, повертаю валідаційний маркер");
|
||||
OnLog("=== RETURN: checksearch validation (data-json=) ===");
|
||||
return Content("data-json=", "text/plain; charset=utf-8");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Якщо нічого не знайдено, повертаємо OnError
|
||||
OnLog("checksearch: No content found");
|
||||
OnLog("=== RETURN: checksearch OnError ===");
|
||||
return OnError("uaflix", proxyManager);
|
||||
}
|
||||
|
||||
OnLog("checksearch: Контент не знайдено");
|
||||
OnLog("=== RETURN: checksearch OnError ===");
|
||||
return OnError("uaflix", proxyManager);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnLog($"checksearch error: {ex.Message}");
|
||||
OnLog($"checksearch: помилка - {ex.Message}");
|
||||
OnLog("=== RETURN: checksearch exception OnError ===");
|
||||
return OnError("uaflix", proxyManager);
|
||||
}
|
||||
|
||||
@ -1,72 +1,67 @@
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Shared;
|
||||
using Shared.Engine;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Shared.Models.Online.Settings;
|
||||
using Shared.Models.Module;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Newtonsoft.Json;
|
||||
using Shared.Models;
|
||||
using Shared.Models.Events;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Uaflix.Models;
|
||||
|
||||
namespace Uaflix
|
||||
{
|
||||
public class ModInit
|
||||
{
|
||||
public static double Version => 3.8;
|
||||
public static double Version => 4.0;
|
||||
|
||||
public static UaflixSettings UaFlix;
|
||||
|
||||
public static OnlinesSettings UaFlix;
|
||||
public static bool ApnHostProvided;
|
||||
|
||||
public static OnlinesSettings Settings
|
||||
public static UaflixSettings Settings
|
||||
{
|
||||
get => UaFlix;
|
||||
set => UaFlix = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// модуль загружен
|
||||
/// Модуль завантажено.
|
||||
/// </summary>
|
||||
public static void loaded(InitspaceModel initspace)
|
||||
{
|
||||
|
||||
|
||||
UaFlix = new OnlinesSettings("Uaflix", "https://uafix.net", streamproxy: false, useproxy: false)
|
||||
UaFlix = new UaflixSettings("Uaflix", "https://uafix.net", streamproxy: false, useproxy: false)
|
||||
{
|
||||
displayname = "UaFlix",
|
||||
group = 0,
|
||||
group_hide = false,
|
||||
globalnameproxy = null,
|
||||
displayindex = 0,
|
||||
login = null,
|
||||
passwd = null,
|
||||
proxy = new Shared.Models.Base.ProxySettings()
|
||||
{
|
||||
useAuth = true,
|
||||
username = "a",
|
||||
password = "a",
|
||||
list = new string[] { "socks5://IP:PORT" }
|
||||
},
|
||||
// Note: OnlinesSettings не має властивості additional, використовуємо інший підхід
|
||||
}
|
||||
};
|
||||
|
||||
var conf = ModuleInvoke.Conf("Uaflix", UaFlix);
|
||||
|
||||
var conf = ModuleInvoke.Conf("Uaflix", UaFlix) ?? JObject.FromObject(UaFlix);
|
||||
bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost);
|
||||
conf.Remove("apn");
|
||||
conf.Remove("apn_host");
|
||||
UaFlix = conf.ToObject<OnlinesSettings>();
|
||||
UaFlix = conf.ToObject<UaflixSettings>();
|
||||
|
||||
if (hasApn)
|
||||
ApnHelper.ApplyInitConf(apnEnabled, apnHost, UaFlix);
|
||||
|
||||
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
|
||||
if (hasApn && apnEnabled)
|
||||
{
|
||||
@ -77,8 +72,8 @@ namespace Uaflix
|
||||
UaFlix.apnstream = false;
|
||||
UaFlix.apn = null;
|
||||
}
|
||||
|
||||
// Виводити "уточнити пошук"
|
||||
|
||||
// Показувати «уточнити пошук».
|
||||
AppInit.conf.online.with_search.Add("uaflix");
|
||||
}
|
||||
}
|
||||
@ -186,6 +181,7 @@ namespace Uaflix
|
||||
_resetTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDisconnected()
|
||||
{
|
||||
return _disconnectTime is not null
|
||||
@ -201,4 +197,4 @@ namespace Uaflix
|
||||
}
|
||||
|
||||
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
27
Uaflix/Models/UaflixSettings.cs
Normal file
27
Uaflix/Models/UaflixSettings.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Shared.Models.Online.Settings;
|
||||
|
||||
namespace Uaflix.Models
|
||||
{
|
||||
public class UaflixSettings : OnlinesSettings, ICloneable
|
||||
{
|
||||
public UaflixSettings(string plugin, string host, string apihost = null, bool useproxy = false, string token = null, bool enable = true, bool streamproxy = false, bool rip = false, bool forceEncryptToken = false, string rch_access = null, string stream_access = null)
|
||||
: base(plugin, host, apihost, useproxy, token, enable, streamproxy, rip, forceEncryptToken, rch_access, stream_access)
|
||||
{
|
||||
}
|
||||
|
||||
public string login { get; set; }
|
||||
|
||||
public string passwd { get; set; }
|
||||
|
||||
public new UaflixSettings Clone()
|
||||
{
|
||||
return (UaflixSettings)MemberwiseClone();
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
334
Uaflix/UaflixAuth.cs
Normal file
334
Uaflix/UaflixAuth.cs
Normal file
@ -0,0 +1,334 @@
|
||||
using Shared.Engine;
|
||||
using Shared.Models;
|
||||
using Uaflix.Models;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Uaflix
|
||||
{
|
||||
public sealed class UaflixAuth
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, CookieContainer> CookieContainers = new();
|
||||
private static readonly ConcurrentDictionary<string, string> CookieHeaders = new();
|
||||
|
||||
private readonly UaflixSettings _init;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly Action<string> _onLog;
|
||||
private readonly ProxyManager _proxyManager;
|
||||
|
||||
public UaflixAuth(UaflixSettings init, IMemoryCache memoryCache, Action<string> onLog, ProxyManager proxyManager)
|
||||
{
|
||||
_init = init;
|
||||
_memoryCache = memoryCache;
|
||||
_onLog = onLog;
|
||||
_proxyManager = proxyManager;
|
||||
}
|
||||
|
||||
public bool CanUseCredentials => !string.IsNullOrWhiteSpace(_init?.login) && !string.IsNullOrWhiteSpace(_init?.passwd);
|
||||
|
||||
public async ValueTask<string> GetCookieHeaderAsync(bool forceRefresh = false)
|
||||
{
|
||||
if (_init == null || string.IsNullOrWhiteSpace(_init.host))
|
||||
return null;
|
||||
|
||||
Uri hostUri;
|
||||
try
|
||||
{
|
||||
hostUri = new Uri(EnsureTrailingSlash(_init.host));
|
||||
}
|
||||
catch
|
||||
{
|
||||
_onLog("UaflixAuth: некоректний host у конфігурації");
|
||||
return null;
|
||||
}
|
||||
|
||||
string key = BuildAuthKey();
|
||||
|
||||
if (forceRefresh)
|
||||
{
|
||||
CookieHeaders.TryRemove(key, out _);
|
||||
CookieContainers.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
if (CookieHeaders.TryGetValue(key, out string cachedCookie) && !string.IsNullOrWhiteSpace(cachedCookie))
|
||||
return cachedCookie;
|
||||
|
||||
if (CookieContainers.TryGetValue(key, out CookieContainer cachedContainer))
|
||||
{
|
||||
string cookieFromContainer = BuildCookieHeader(cachedContainer, hostUri);
|
||||
if (!string.IsNullOrWhiteSpace(cookieFromContainer))
|
||||
{
|
||||
CookieHeaders[key] = cookieFromContainer;
|
||||
return cookieFromContainer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_init.cookie))
|
||||
{
|
||||
string normalized = NormalizeCookie(_init.cookie);
|
||||
if (string.IsNullOrWhiteSpace(normalized))
|
||||
return null;
|
||||
|
||||
var manualContainer = CreateContainerFromCookie(normalized);
|
||||
CacheAuthState(key, normalized, manualContainer);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
if (!CanUseCredentials)
|
||||
return null;
|
||||
|
||||
string loginThrottleKey = $"uaflix:login:{_init.host}:{_init.login}";
|
||||
if (!forceRefresh && _memoryCache.TryGetValue(loginThrottleKey, out _))
|
||||
return null;
|
||||
|
||||
_memoryCache.Set(loginThrottleKey, 0, TimeSpan.FromSeconds(20));
|
||||
|
||||
var authResult = await LoginByCredentials();
|
||||
if (!authResult.success || string.IsNullOrWhiteSpace(authResult.cookie))
|
||||
return null;
|
||||
|
||||
CacheAuthState(key, authResult.cookie, authResult.container);
|
||||
return authResult.cookie;
|
||||
}
|
||||
|
||||
public void ApplyCookieHeader(List<HeadersModel> headers, string cookie)
|
||||
{
|
||||
if (headers == null || string.IsNullOrWhiteSpace(cookie))
|
||||
return;
|
||||
|
||||
headers.RemoveAll(h => h.name.Equals("Cookie", StringComparison.OrdinalIgnoreCase));
|
||||
headers.Add(new HeadersModel("Cookie", cookie));
|
||||
}
|
||||
|
||||
private async Task<(bool success, string cookie, CookieContainer container)> LoginByCredentials()
|
||||
{
|
||||
try
|
||||
{
|
||||
string host = EnsureTrailingSlash(_init.host);
|
||||
var hostUri = new Uri(host);
|
||||
var container = new CookieContainer();
|
||||
|
||||
var headers = new List<HeadersModel>
|
||||
{
|
||||
new HeadersModel("User-Agent", "Mozilla/5.0"),
|
||||
new HeadersModel("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
|
||||
new HeadersModel("Referer", host),
|
||||
new HeadersModel("Origin", _init.host),
|
||||
new HeadersModel("Accept-Language", "uk-UA,uk;q=0.9")
|
||||
};
|
||||
|
||||
var postParams = new Dictionary<string, string>
|
||||
{
|
||||
["login_name"] = _init.login,
|
||||
["login_password"] = _init.passwd,
|
||||
["login"] = "submit",
|
||||
["login_not_save"] = "1"
|
||||
};
|
||||
|
||||
using var postData = new FormUrlEncodedContent(postParams);
|
||||
var response = await Http.BasePost(host, postData,
|
||||
timeoutSeconds: 20,
|
||||
headers: headers,
|
||||
proxy: _proxyManager?.Get(),
|
||||
cookieContainer: container,
|
||||
statusCodeOK: false);
|
||||
|
||||
if (response.response == null)
|
||||
{
|
||||
_onLog("UaflixAuth: логін не вдався, немає HTTP-відповіді");
|
||||
return (false, null, null);
|
||||
}
|
||||
|
||||
string body = response.content ?? string.Empty;
|
||||
bool hasAuthError = body.Contains("Помилка авторизації", StringComparison.OrdinalIgnoreCase)
|
||||
|| body.Contains("Вхід на сайт не був проведений", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
string cookie = BuildCookieHeader(container, hostUri) ?? string.Empty;
|
||||
bool hasSession = cookie.Contains("PHPSESSID=", StringComparison.OrdinalIgnoreCase);
|
||||
bool hasDleAuthCookie = cookie.Contains("dle_newpm=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_user_id=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_password=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_hash=", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (response.response.Headers.TryGetValues("Set-Cookie", out IEnumerable<string> setCookies))
|
||||
{
|
||||
foreach (string line in setCookies)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line) || IsDeletedCookie(line))
|
||||
continue;
|
||||
|
||||
TrySetCookie(container, hostUri, line);
|
||||
}
|
||||
|
||||
cookie = BuildCookieHeader(container, hostUri) ?? string.Empty;
|
||||
hasSession = cookie.Contains("PHPSESSID=", StringComparison.OrdinalIgnoreCase);
|
||||
hasDleAuthCookie = cookie.Contains("dle_newpm=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_user_id=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_password=", StringComparison.OrdinalIgnoreCase)
|
||||
|| cookie.Contains("dle_hash=", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (hasAuthError || !hasSession || !hasDleAuthCookie)
|
||||
{
|
||||
_onLog($"UaflixAuth: авторизація неуспішна, status={(int)response.response.StatusCode}");
|
||||
return (false, null, null);
|
||||
}
|
||||
|
||||
_onLog("UaflixAuth: авторизація успішна");
|
||||
return (true, cookie, container);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_onLog($"UaflixAuth: помилка авторизації - {ex.Message}");
|
||||
return (false, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildAuthKey()
|
||||
{
|
||||
string login = _init.login ?? string.Empty;
|
||||
string manualCookie = _init.cookie ?? string.Empty;
|
||||
return $"{_init.host}|{login}|{manualCookie}";
|
||||
}
|
||||
|
||||
private void CacheAuthState(string key, string cookie, CookieContainer container)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cookie))
|
||||
CookieHeaders[key] = cookie;
|
||||
|
||||
if (container != null)
|
||||
CookieContainers[key] = container;
|
||||
}
|
||||
|
||||
private CookieContainer CreateContainerFromCookie(string cookie)
|
||||
{
|
||||
var container = new CookieContainer();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cookie))
|
||||
return container;
|
||||
|
||||
Uri hostUri = new Uri(EnsureTrailingSlash(_init.host));
|
||||
foreach (string part in cookie.Split(';'))
|
||||
{
|
||||
string row = part.Trim();
|
||||
if (string.IsNullOrWhiteSpace(row) || !row.Contains('='))
|
||||
continue;
|
||||
|
||||
string name = row[..row.IndexOf('=')].Trim();
|
||||
string value = row[(row.IndexOf('=') + 1)..].Trim();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
continue;
|
||||
|
||||
TryAddCookie(container, hostUri.Host, name, value);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private static void TrySetCookie(CookieContainer container, Uri uri, string setCookieLine)
|
||||
{
|
||||
try
|
||||
{
|
||||
container.SetCookies(uri, setCookieLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
string raw = setCookieLine.Split(';')[0].Trim();
|
||||
int eq = raw.IndexOf('=');
|
||||
if (eq <= 0)
|
||||
return;
|
||||
|
||||
string name = raw[..eq].Trim();
|
||||
string value = raw[(eq + 1)..].Trim();
|
||||
TryAddCookie(container, uri.Host, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryAddCookie(CookieContainer container, string host, string name, string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cookie = new Cookie(name, value, "/", host)
|
||||
{
|
||||
HttpOnly = true,
|
||||
Expires = name.Equals("PHPSESSID", StringComparison.OrdinalIgnoreCase)
|
||||
? default(DateTime)
|
||||
: DateTime.UtcNow.AddMonths(6)
|
||||
};
|
||||
|
||||
container.Add(cookie);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildCookieHeader(CookieContainer container, Uri hostUri)
|
||||
{
|
||||
if (container == null)
|
||||
return null;
|
||||
|
||||
var cookies = container.GetCookies(hostUri)
|
||||
.Cast<Cookie>()
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.Name) && !string.IsNullOrWhiteSpace(c.Value))
|
||||
.Select(c => $"{c.Name}={c.Value}")
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
return cookies.Count == 0 ? null : string.Join("; ", cookies);
|
||||
}
|
||||
|
||||
private static bool IsDeletedCookie(string line)
|
||||
{
|
||||
return line.Contains("=deleted;", StringComparison.OrdinalIgnoreCase)
|
||||
|| line.Contains("Max-Age=0", StringComparison.OrdinalIgnoreCase)
|
||||
|| line.Contains("expires=Thu, 01-Jan-1970", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string NormalizeCookie(string cookie)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cookie))
|
||||
return null;
|
||||
|
||||
var pairs = new List<string>();
|
||||
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (string part in cookie.Split(';'))
|
||||
{
|
||||
string row = part.Trim();
|
||||
if (string.IsNullOrWhiteSpace(row) || !row.Contains('='))
|
||||
continue;
|
||||
|
||||
int eq = row.IndexOf('=');
|
||||
if (eq <= 0)
|
||||
continue;
|
||||
|
||||
string name = row[..eq].Trim();
|
||||
string value = row[(eq + 1)..].Trim();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
continue;
|
||||
|
||||
map[name] = value;
|
||||
}
|
||||
|
||||
foreach (var kv in map)
|
||||
pairs.Add($"{kv.Key}={kv.Value}");
|
||||
|
||||
return pairs.Count == 0 ? null : string.Join("; ", pairs);
|
||||
}
|
||||
|
||||
private static string EnsureTrailingSlash(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
return url;
|
||||
|
||||
return url.EndsWith('/') ? url : $"{url}/";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,17 +23,19 @@ namespace Uaflix
|
||||
private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase);
|
||||
private static readonly Regex QualityFhdRegex = new Regex(@"(^|[^0-9])(1080p?)([^0-9]|$)|\bfhd\b", RegexOptions.IgnoreCase);
|
||||
|
||||
private OnlinesSettings _init;
|
||||
private IHybridCache _hybridCache;
|
||||
private Action<string> _onLog;
|
||||
private ProxyManager _proxyManager;
|
||||
private readonly UaflixSettings _init;
|
||||
private readonly IHybridCache _hybridCache;
|
||||
private readonly Action<string> _onLog;
|
||||
private readonly ProxyManager _proxyManager;
|
||||
private readonly UaflixAuth _auth;
|
||||
|
||||
public UaflixInvoke(OnlinesSettings init, IHybridCache hybridCache, Action<string> onLog, ProxyManager proxyManager)
|
||||
public UaflixInvoke(UaflixSettings init, IHybridCache hybridCache, Action<string> onLog, ProxyManager proxyManager, UaflixAuth auth)
|
||||
{
|
||||
_init = init;
|
||||
_hybridCache = hybridCache;
|
||||
_onLog = onLog;
|
||||
_proxyManager = proxyManager;
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
string AshdiRequestUrl(string url)
|
||||
@ -46,6 +48,97 @@ namespace Uaflix
|
||||
|
||||
return ApnHelper.WrapUrl(_init, url);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckSearchAvailability(string title, string originalTitle)
|
||||
{
|
||||
string filmTitle = !string.IsNullOrWhiteSpace(title) ? title : originalTitle;
|
||||
if (string.IsNullOrWhiteSpace(filmTitle))
|
||||
return false;
|
||||
|
||||
string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(filmTitle)}";
|
||||
var headers = new List<HeadersModel>()
|
||||
{
|
||||
new HeadersModel("User-Agent", "Mozilla/5.0"),
|
||||
new HeadersModel("Referer", _init.host)
|
||||
};
|
||||
|
||||
string searchHtml = await GetHtml(searchUrl, headers, timeoutSeconds: 10);
|
||||
if (string.IsNullOrWhiteSpace(searchHtml))
|
||||
return false;
|
||||
|
||||
return searchHtml.Contains("sres-wrap")
|
||||
|| searchHtml.Contains("sres-item")
|
||||
|| searchHtml.Contains("search-results");
|
||||
}
|
||||
|
||||
async Task<string> GetHtml(string url, List<HeadersModel> headers, int timeoutSeconds = 15, bool retryOnUnauthorized = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
return null;
|
||||
|
||||
string requestUrl = _init.cors(url);
|
||||
bool withAuth = ShouldUseAuth(url);
|
||||
var requestHeaders = headers != null ? new List<HeadersModel>(headers) : new List<HeadersModel>();
|
||||
|
||||
if (withAuth && _auth != null)
|
||||
{
|
||||
string cookie = await _auth.GetCookieHeaderAsync();
|
||||
_auth.ApplyCookieHeader(requestHeaders, cookie);
|
||||
}
|
||||
|
||||
var response = await Http.BaseGet(requestUrl,
|
||||
headers: requestHeaders,
|
||||
timeoutSeconds: timeoutSeconds,
|
||||
proxy: _proxyManager.Get(),
|
||||
statusCodeOK: false);
|
||||
|
||||
if (response.response?.StatusCode == HttpStatusCode.Forbidden
|
||||
&& retryOnUnauthorized
|
||||
&& withAuth
|
||||
&& _auth != null
|
||||
&& _auth.CanUseCredentials)
|
||||
{
|
||||
_onLog($"UaflixAuth: отримано 403 для {url}, виконую повторну авторизацію");
|
||||
string refreshedCookie = await _auth.GetCookieHeaderAsync(forceRefresh: true);
|
||||
_auth.ApplyCookieHeader(requestHeaders, refreshedCookie);
|
||||
|
||||
response = await Http.BaseGet(requestUrl,
|
||||
headers: requestHeaders,
|
||||
timeoutSeconds: timeoutSeconds,
|
||||
proxy: _proxyManager.Get(),
|
||||
statusCodeOK: false);
|
||||
}
|
||||
|
||||
if (response.response?.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
if (response.response != null)
|
||||
_onLog($"Uaflix HTTP {(int)response.response.StatusCode} для {url}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.content;
|
||||
}
|
||||
|
||||
bool ShouldUseAuth(string url)
|
||||
{
|
||||
if (_auth == null || string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(_init?.host))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Uri siteUri = new Uri(_init.host);
|
||||
Uri requestUri;
|
||||
if (!Uri.TryCreate(url, UriKind.Absolute, out requestUri))
|
||||
requestUri = new Uri(siteUri, url.TrimStart('/'));
|
||||
|
||||
return string.Equals(requestUri.Host, siteUri.Host, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Методи для визначення та парсингу різних типів плеєрів
|
||||
|
||||
@ -144,7 +237,7 @@ namespace Uaflix
|
||||
new HeadersModel("Referer", _init.host)
|
||||
};
|
||||
|
||||
string html = await Http.Get(_init.cors(pageUrl), headers: headers, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(pageUrl, headers);
|
||||
if (string.IsNullOrWhiteSpace(html))
|
||||
return (null, null);
|
||||
|
||||
@ -294,7 +387,7 @@ namespace Uaflix
|
||||
}
|
||||
}
|
||||
|
||||
string html = await Http.Get(_init.cors(AshdiRequestUrl(requestUrl)), headers: headers, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(AshdiRequestUrl(requestUrl), headers);
|
||||
|
||||
// Знайти JSON у new Playerjs({file:'...'})
|
||||
var match = Regex.Match(html, @"file:'(\[.+?\])'", RegexOptions.Singleline);
|
||||
@ -405,7 +498,7 @@ namespace Uaflix
|
||||
|
||||
try
|
||||
{
|
||||
string html = await Http.Get(_init.cors(iframeUrl), headers: headers, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(iframeUrl, headers);
|
||||
|
||||
// Знайти file:"url"
|
||||
var match = Regex.Match(html, @"file:\s*""([^""]+\.m3u8)""");
|
||||
@ -441,7 +534,7 @@ namespace Uaflix
|
||||
try
|
||||
{
|
||||
string requestUrl = WithAshdiMultivoice(iframeUrl);
|
||||
string html = await Http.Get(_init.cors(AshdiRequestUrl(requestUrl)), headers: headers, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(AshdiRequestUrl(requestUrl), headers);
|
||||
if (string.IsNullOrEmpty(html))
|
||||
return result;
|
||||
|
||||
@ -740,7 +833,7 @@ namespace Uaflix
|
||||
foreach (string query in queries)
|
||||
{
|
||||
string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(query)}";
|
||||
string searchHtml = await Http.Get(_init.cors(searchUrl), headers: headers, proxy: _proxyManager.Get());
|
||||
string searchHtml = await GetHtml(searchUrl, headers);
|
||||
if (string.IsNullOrWhiteSpace(searchHtml))
|
||||
continue;
|
||||
|
||||
@ -926,7 +1019,7 @@ namespace Uaflix
|
||||
new HeadersModel("Referer", _init.host)
|
||||
};
|
||||
|
||||
string html = await Http.Get(_init.cors(url), headers: headers, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(url, headers);
|
||||
if (!string.IsNullOrWhiteSpace(html))
|
||||
{
|
||||
var doc = new HtmlDocument();
|
||||
@ -1152,7 +1245,7 @@ namespace Uaflix
|
||||
try
|
||||
{
|
||||
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
||||
var filmHtml = await Http.Get(_init.cors(filmUrl), headers: headers, proxy: _proxyManager.Get());
|
||||
var filmHtml = await GetHtml(filmUrl, headers);
|
||||
var doc = new HtmlDocument();
|
||||
doc.LoadHtml(filmHtml);
|
||||
|
||||
@ -1227,7 +1320,7 @@ namespace Uaflix
|
||||
try
|
||||
{
|
||||
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
||||
var filmHtml = await Http.Get(_init.cors(filmUrl), headers: headers, proxy: _proxyManager.Get());
|
||||
var filmHtml = await GetHtml(filmUrl, headers);
|
||||
var filmDoc = new HtmlDocument();
|
||||
filmDoc.LoadHtml(filmHtml);
|
||||
|
||||
@ -1265,7 +1358,7 @@ namespace Uaflix
|
||||
if (safeSeasonUrls.Count == 0)
|
||||
return null;
|
||||
|
||||
var seasonTasks = safeSeasonUrls.Select(url => Http.Get(_init.cors(url), headers: headers, proxy: _proxyManager.Get()));
|
||||
var seasonTasks = safeSeasonUrls.Select(url => GetHtml(url, headers));
|
||||
var seasonPagesHtml = await Task.WhenAll(seasonTasks);
|
||||
|
||||
foreach (var html in seasonPagesHtml)
|
||||
@ -1326,7 +1419,7 @@ namespace Uaflix
|
||||
var result = new Uaflix.Models.PlayResult() { streams = new List<PlayStream>() };
|
||||
try
|
||||
{
|
||||
string html = await Http.Get(_init.cors(url), headers: new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get());
|
||||
string html = await GetHtml(url, new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) });
|
||||
var doc = new HtmlDocument();
|
||||
doc.LoadHtml(html);
|
||||
|
||||
@ -1429,7 +1522,7 @@ namespace Uaflix
|
||||
async Task<List<PlayStream>> ParseAllZetvideoSources(string iframeUrl)
|
||||
{
|
||||
var result = new List<PlayStream>();
|
||||
var html = await Http.Get(_init.cors(iframeUrl), headers: new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://zetvideo.net/") }, proxy: _proxyManager.Get());
|
||||
var html = await GetHtml(iframeUrl, new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://zetvideo.net/") });
|
||||
if (string.IsNullOrEmpty(html)) return result;
|
||||
|
||||
var doc = new HtmlDocument();
|
||||
@ -1473,7 +1566,7 @@ namespace Uaflix
|
||||
async Task<List<PlayStream>> ParseAllAshdiSources(string iframeUrl)
|
||||
{
|
||||
var result = new List<PlayStream>();
|
||||
var html = await Http.Get(_init.cors(AshdiRequestUrl(iframeUrl)), headers: new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }, proxy: _proxyManager.Get());
|
||||
var html = await GetHtml(AshdiRequestUrl(iframeUrl), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") });
|
||||
if (string.IsNullOrEmpty(html)) return result;
|
||||
|
||||
var doc = new HtmlDocument();
|
||||
@ -1500,7 +1593,7 @@ namespace Uaflix
|
||||
async Task<SubtitleTpl?> GetAshdiSubtitles(string id)
|
||||
{
|
||||
string url = $"https://ashdi.vip/vod/{id}";
|
||||
var html = await Http.Get(_init.cors(AshdiRequestUrl(url)), headers: new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }, proxy: _proxyManager.Get());
|
||||
var html = await GetHtml(AshdiRequestUrl(url), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") });
|
||||
string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value;
|
||||
if (!string.IsNullOrEmpty(subtitle))
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user