mirror of
https://github.com/lampame/lampac-ukraine.git
synced 2026-04-16 17:32:20 +00:00
Compare commits
No commits in common. "1c49df634a1be307007888689951116806d561ce" and "4d4ac22601331188fd88c0f1020d914de1ba2a6a" have entirely different histories.
1c49df634a
...
4d4ac22601
@ -62,9 +62,6 @@ modules - optional, if not specified, all modules from the repository will be in
|
|||||||
"enable": true,
|
"enable": true,
|
||||||
"domain": "https://uaflix.net",
|
"domain": "https://uaflix.net",
|
||||||
"displayname": "Uaflix",
|
"displayname": "Uaflix",
|
||||||
"login": null,
|
|
||||||
"passwd": null,
|
|
||||||
"cookie": null,
|
|
||||||
"webcorshost": null,
|
"webcorshost": null,
|
||||||
"streamproxy": false,
|
"streamproxy": false,
|
||||||
"useproxy": false,
|
"useproxy": false,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ using Uaflix.Models;
|
|||||||
namespace Uaflix.Controllers
|
namespace Uaflix.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
public class Controller : BaseOnlineController<UaflixSettings>
|
public class Controller : BaseOnlineController
|
||||||
{
|
{
|
||||||
ProxyManager proxyManager;
|
ProxyManager proxyManager;
|
||||||
|
|
||||||
@ -42,8 +42,7 @@ namespace Uaflix.Controllers
|
|||||||
OnLog($"Uaflix Index: kinopoisk_id={kinopoisk_id}, imdb_id={imdb_id}, id={id}");
|
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}");
|
OnLog($"Uaflix Index: year={year}, source={source}, t={t}, e={e}, rjson={rjson}");
|
||||||
|
|
||||||
var auth = new UaflixAuth(init, memoryCache, OnLog, proxyManager);
|
var invoke = new UaflixInvoke(init, hybridCache, OnLog, proxyManager);
|
||||||
var invoke = new UaflixInvoke(init, hybridCache, OnLog, proxyManager, auth);
|
|
||||||
|
|
||||||
// Обробка параметра checksearch - повертаємо спеціальну відповідь для валідації
|
// Обробка параметра checksearch - повертаємо спеціальну відповідь для валідації
|
||||||
if (checksearch)
|
if (checksearch)
|
||||||
@ -53,21 +52,32 @@ namespace Uaflix.Controllers
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool hasContent = await invoke.CheckSearchAvailability(title, original_title);
|
string filmTitle = !string.IsNullOrEmpty(title) ? title : original_title;
|
||||||
if (hasContent)
|
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")))
|
||||||
{
|
{
|
||||||
OnLog("checksearch: Контент знайдено, повертаю валідаційний маркер");
|
// Якщо знайдено контент, повертаємо "data-json=" для валідації
|
||||||
|
OnLog("checksearch: Content found, returning validation response");
|
||||||
OnLog("=== RETURN: checksearch validation (data-json=) ===");
|
OnLog("=== RETURN: checksearch validation (data-json=) ===");
|
||||||
return Content("data-json=", "text/plain; charset=utf-8");
|
return Content("data-json=", "text/plain; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
OnLog("checksearch: Контент не знайдено");
|
{
|
||||||
|
// Якщо нічого не знайдено, повертаємо OnError
|
||||||
|
OnLog("checksearch: No content found");
|
||||||
OnLog("=== RETURN: checksearch OnError ===");
|
OnLog("=== RETURN: checksearch OnError ===");
|
||||||
return OnError("uaflix", proxyManager);
|
return OnError("uaflix", proxyManager);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
OnLog($"checksearch: помилка - {ex.Message}");
|
OnLog($"checksearch error: {ex.Message}");
|
||||||
OnLog("=== RETURN: checksearch exception OnError ===");
|
OnLog("=== RETURN: checksearch exception OnError ===");
|
||||||
return OnError("uaflix", proxyManager);
|
return OnError("uaflix", proxyManager);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,67 +1,72 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Shared;
|
using Shared;
|
||||||
using Shared.Engine;
|
using Shared.Engine;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Shared.Models.Online.Settings;
|
||||||
using Shared.Models.Module;
|
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;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Uaflix.Models;
|
|
||||||
|
|
||||||
namespace Uaflix
|
namespace Uaflix
|
||||||
{
|
{
|
||||||
public class ModInit
|
public class ModInit
|
||||||
{
|
{
|
||||||
public static double Version => 4.0;
|
public static double Version => 3.8;
|
||||||
|
|
||||||
public static UaflixSettings UaFlix;
|
|
||||||
|
|
||||||
|
public static OnlinesSettings UaFlix;
|
||||||
public static bool ApnHostProvided;
|
public static bool ApnHostProvided;
|
||||||
|
|
||||||
public static UaflixSettings Settings
|
public static OnlinesSettings Settings
|
||||||
{
|
{
|
||||||
get => UaFlix;
|
get => UaFlix;
|
||||||
set => UaFlix = value;
|
set => UaFlix = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Модуль завантажено.
|
/// модуль загружен
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void loaded(InitspaceModel initspace)
|
public static void loaded(InitspaceModel initspace)
|
||||||
{
|
{
|
||||||
UaFlix = new UaflixSettings("Uaflix", "https://uafix.net", streamproxy: false, useproxy: false)
|
|
||||||
|
|
||||||
|
UaFlix = new OnlinesSettings("Uaflix", "https://uafix.net", streamproxy: false, useproxy: false)
|
||||||
{
|
{
|
||||||
displayname = "UaFlix",
|
displayname = "UaFlix",
|
||||||
group = 0,
|
group = 0,
|
||||||
group_hide = false,
|
group_hide = false,
|
||||||
globalnameproxy = null,
|
globalnameproxy = null,
|
||||||
displayindex = 0,
|
displayindex = 0,
|
||||||
login = null,
|
|
||||||
passwd = null,
|
|
||||||
proxy = new Shared.Models.Base.ProxySettings()
|
proxy = new Shared.Models.Base.ProxySettings()
|
||||||
{
|
{
|
||||||
useAuth = true,
|
useAuth = true,
|
||||||
username = "a",
|
username = "a",
|
||||||
password = "a",
|
password = "a",
|
||||||
list = new string[] { "socks5://IP:PORT" }
|
list = new string[] { "socks5://IP:PORT" }
|
||||||
}
|
},
|
||||||
|
// Note: OnlinesSettings не має властивості additional, використовуємо інший підхід
|
||||||
};
|
};
|
||||||
|
|
||||||
var conf = ModuleInvoke.Conf("Uaflix", UaFlix) ?? JObject.FromObject(UaFlix);
|
var conf = ModuleInvoke.Conf("Uaflix", UaFlix);
|
||||||
bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost);
|
bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost);
|
||||||
conf.Remove("apn");
|
conf.Remove("apn");
|
||||||
conf.Remove("apn_host");
|
conf.Remove("apn_host");
|
||||||
UaFlix = conf.ToObject<UaflixSettings>();
|
UaFlix = conf.ToObject<OnlinesSettings>();
|
||||||
|
|
||||||
if (hasApn)
|
if (hasApn)
|
||||||
ApnHelper.ApplyInitConf(apnEnabled, apnHost, UaFlix);
|
ApnHelper.ApplyInitConf(apnEnabled, apnHost, UaFlix);
|
||||||
|
|
||||||
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
|
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
|
||||||
if (hasApn && apnEnabled)
|
if (hasApn && apnEnabled)
|
||||||
{
|
{
|
||||||
@ -73,7 +78,7 @@ namespace Uaflix
|
|||||||
UaFlix.apn = null;
|
UaFlix.apn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Показувати «уточнити пошук».
|
// Виводити "уточнити пошук"
|
||||||
AppInit.conf.online.with_search.Add("uaflix");
|
AppInit.conf.online.with_search.Add("uaflix");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +186,6 @@ namespace Uaflix
|
|||||||
_resetTimer = null;
|
_resetTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsDisconnected()
|
public static bool IsDisconnected()
|
||||||
{
|
{
|
||||||
return _disconnectTime is not null
|
return _disconnectTime is not null
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,334 +0,0 @@
|
|||||||
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,19 +23,17 @@ namespace Uaflix
|
|||||||
private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase);
|
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 static readonly Regex QualityFhdRegex = new Regex(@"(^|[^0-9])(1080p?)([^0-9]|$)|\bfhd\b", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private readonly UaflixSettings _init;
|
private OnlinesSettings _init;
|
||||||
private readonly IHybridCache _hybridCache;
|
private IHybridCache _hybridCache;
|
||||||
private readonly Action<string> _onLog;
|
private Action<string> _onLog;
|
||||||
private readonly ProxyManager _proxyManager;
|
private ProxyManager _proxyManager;
|
||||||
private readonly UaflixAuth _auth;
|
|
||||||
|
|
||||||
public UaflixInvoke(UaflixSettings init, IHybridCache hybridCache, Action<string> onLog, ProxyManager proxyManager, UaflixAuth auth)
|
public UaflixInvoke(OnlinesSettings init, IHybridCache hybridCache, Action<string> onLog, ProxyManager proxyManager)
|
||||||
{
|
{
|
||||||
_init = init;
|
_init = init;
|
||||||
_hybridCache = hybridCache;
|
_hybridCache = hybridCache;
|
||||||
_onLog = onLog;
|
_onLog = onLog;
|
||||||
_proxyManager = proxyManager;
|
_proxyManager = proxyManager;
|
||||||
_auth = auth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string AshdiRequestUrl(string url)
|
string AshdiRequestUrl(string url)
|
||||||
@ -49,97 +47,6 @@ namespace Uaflix
|
|||||||
return ApnHelper.WrapUrl(_init, url);
|
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 Методи для визначення та парсингу різних типів плеєрів
|
#region Методи для визначення та парсингу різних типів плеєрів
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -237,7 +144,7 @@ namespace Uaflix
|
|||||||
new HeadersModel("Referer", _init.host)
|
new HeadersModel("Referer", _init.host)
|
||||||
};
|
};
|
||||||
|
|
||||||
string html = await GetHtml(pageUrl, headers);
|
string html = await Http.Get(_init.cors(pageUrl), headers: headers, proxy: _proxyManager.Get());
|
||||||
if (string.IsNullOrWhiteSpace(html))
|
if (string.IsNullOrWhiteSpace(html))
|
||||||
return (null, null);
|
return (null, null);
|
||||||
|
|
||||||
@ -387,7 +294,7 @@ namespace Uaflix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string html = await GetHtml(AshdiRequestUrl(requestUrl), headers);
|
string html = await Http.Get(_init.cors(AshdiRequestUrl(requestUrl)), headers: headers, proxy: _proxyManager.Get());
|
||||||
|
|
||||||
// Знайти JSON у new Playerjs({file:'...'})
|
// Знайти JSON у new Playerjs({file:'...'})
|
||||||
var match = Regex.Match(html, @"file:'(\[.+?\])'", RegexOptions.Singleline);
|
var match = Regex.Match(html, @"file:'(\[.+?\])'", RegexOptions.Singleline);
|
||||||
@ -498,7 +405,7 @@ namespace Uaflix
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string html = await GetHtml(iframeUrl, headers);
|
string html = await Http.Get(_init.cors(iframeUrl), headers: headers, proxy: _proxyManager.Get());
|
||||||
|
|
||||||
// Знайти file:"url"
|
// Знайти file:"url"
|
||||||
var match = Regex.Match(html, @"file:\s*""([^""]+\.m3u8)""");
|
var match = Regex.Match(html, @"file:\s*""([^""]+\.m3u8)""");
|
||||||
@ -534,7 +441,7 @@ namespace Uaflix
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
string requestUrl = WithAshdiMultivoice(iframeUrl);
|
string requestUrl = WithAshdiMultivoice(iframeUrl);
|
||||||
string html = await GetHtml(AshdiRequestUrl(requestUrl), headers);
|
string html = await Http.Get(_init.cors(AshdiRequestUrl(requestUrl)), headers: headers, proxy: _proxyManager.Get());
|
||||||
if (string.IsNullOrEmpty(html))
|
if (string.IsNullOrEmpty(html))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -833,7 +740,7 @@ namespace Uaflix
|
|||||||
foreach (string query in queries)
|
foreach (string query in queries)
|
||||||
{
|
{
|
||||||
string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(query)}";
|
string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(query)}";
|
||||||
string searchHtml = await GetHtml(searchUrl, headers);
|
string searchHtml = await Http.Get(_init.cors(searchUrl), headers: headers, proxy: _proxyManager.Get());
|
||||||
if (string.IsNullOrWhiteSpace(searchHtml))
|
if (string.IsNullOrWhiteSpace(searchHtml))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1019,7 +926,7 @@ namespace Uaflix
|
|||||||
new HeadersModel("Referer", _init.host)
|
new HeadersModel("Referer", _init.host)
|
||||||
};
|
};
|
||||||
|
|
||||||
string html = await GetHtml(url, headers);
|
string html = await Http.Get(_init.cors(url), headers: headers, proxy: _proxyManager.Get());
|
||||||
if (!string.IsNullOrWhiteSpace(html))
|
if (!string.IsNullOrWhiteSpace(html))
|
||||||
{
|
{
|
||||||
var doc = new HtmlDocument();
|
var doc = new HtmlDocument();
|
||||||
@ -1245,7 +1152,7 @@ namespace Uaflix
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
||||||
var filmHtml = await GetHtml(filmUrl, headers);
|
var filmHtml = await Http.Get(_init.cors(filmUrl), headers: headers, proxy: _proxyManager.Get());
|
||||||
var doc = new HtmlDocument();
|
var doc = new HtmlDocument();
|
||||||
doc.LoadHtml(filmHtml);
|
doc.LoadHtml(filmHtml);
|
||||||
|
|
||||||
@ -1320,7 +1227,7 @@ namespace Uaflix
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
||||||
var filmHtml = await GetHtml(filmUrl, headers);
|
var filmHtml = await Http.Get(_init.cors(filmUrl), headers: headers, proxy: _proxyManager.Get());
|
||||||
var filmDoc = new HtmlDocument();
|
var filmDoc = new HtmlDocument();
|
||||||
filmDoc.LoadHtml(filmHtml);
|
filmDoc.LoadHtml(filmHtml);
|
||||||
|
|
||||||
@ -1358,7 +1265,7 @@ namespace Uaflix
|
|||||||
if (safeSeasonUrls.Count == 0)
|
if (safeSeasonUrls.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var seasonTasks = safeSeasonUrls.Select(url => GetHtml(url, headers));
|
var seasonTasks = safeSeasonUrls.Select(url => Http.Get(_init.cors(url), headers: headers, proxy: _proxyManager.Get()));
|
||||||
var seasonPagesHtml = await Task.WhenAll(seasonTasks);
|
var seasonPagesHtml = await Task.WhenAll(seasonTasks);
|
||||||
|
|
||||||
foreach (var html in seasonPagesHtml)
|
foreach (var html in seasonPagesHtml)
|
||||||
@ -1419,7 +1326,7 @@ namespace Uaflix
|
|||||||
var result = new Uaflix.Models.PlayResult() { streams = new List<PlayStream>() };
|
var result = new Uaflix.Models.PlayResult() { streams = new List<PlayStream>() };
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string html = await GetHtml(url, new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) });
|
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());
|
||||||
var doc = new HtmlDocument();
|
var doc = new HtmlDocument();
|
||||||
doc.LoadHtml(html);
|
doc.LoadHtml(html);
|
||||||
|
|
||||||
@ -1522,7 +1429,7 @@ namespace Uaflix
|
|||||||
async Task<List<PlayStream>> ParseAllZetvideoSources(string iframeUrl)
|
async Task<List<PlayStream>> ParseAllZetvideoSources(string iframeUrl)
|
||||||
{
|
{
|
||||||
var result = new List<PlayStream>();
|
var result = new List<PlayStream>();
|
||||||
var html = await GetHtml(iframeUrl, new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://zetvideo.net/") });
|
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());
|
||||||
if (string.IsNullOrEmpty(html)) return result;
|
if (string.IsNullOrEmpty(html)) return result;
|
||||||
|
|
||||||
var doc = new HtmlDocument();
|
var doc = new HtmlDocument();
|
||||||
@ -1566,7 +1473,7 @@ namespace Uaflix
|
|||||||
async Task<List<PlayStream>> ParseAllAshdiSources(string iframeUrl)
|
async Task<List<PlayStream>> ParseAllAshdiSources(string iframeUrl)
|
||||||
{
|
{
|
||||||
var result = new List<PlayStream>();
|
var result = new List<PlayStream>();
|
||||||
var html = await GetHtml(AshdiRequestUrl(iframeUrl), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") });
|
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());
|
||||||
if (string.IsNullOrEmpty(html)) return result;
|
if (string.IsNullOrEmpty(html)) return result;
|
||||||
|
|
||||||
var doc = new HtmlDocument();
|
var doc = new HtmlDocument();
|
||||||
@ -1593,7 +1500,7 @@ namespace Uaflix
|
|||||||
async Task<SubtitleTpl?> GetAshdiSubtitles(string id)
|
async Task<SubtitleTpl?> GetAshdiSubtitles(string id)
|
||||||
{
|
{
|
||||||
string url = $"https://ashdi.vip/vod/{id}";
|
string url = $"https://ashdi.vip/vod/{id}";
|
||||||
var html = await GetHtml(AshdiRequestUrl(url), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") });
|
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());
|
||||||
string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value;
|
string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value;
|
||||||
if (!string.IsNullOrEmpty(subtitle))
|
if (!string.IsNullOrEmpty(subtitle))
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user