lampac/Online/Controllers/FilmixTV.cs
2026-02-07 00:18:50 +03:00

193 lines
7.3 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using Shared.Models.Online.FilmixTV;
using Shared.Models.Online.Settings;
using System.Text;
using System.Threading;
using F = System.IO.File;
namespace Online.Controllers
{
/// <summary>
/// Автор https://github.com/fellicienne
/// https://github.com/lampac-talks/lampac/pull/41
/// </summary>
public class FilmixTV : BaseOnlineController<FilmixSettings>
{
public FilmixTV() : base(AppInit.conf.FilmixTV)
{
loadKitInitialization = (j, i, c) =>
{
if (j.ContainsKey("pro"))
i.pro = c.pro;
i.user_apitv = c.user_apitv;
i.passwd_apitv = c.passwd_apitv;
return i;
};
}
static readonly SemaphoreSlim _accessTokenLock = new SemaphoreSlim(1, 1);
[HttpGet]
[Route("lite/filmixtv")]
async public Task<ActionResult> Index(string title, string original_title, int clarification, int year, int postid, int t = -1, int? s = null, bool rjson = false, bool similar = false, string source = null, string id = null)
{
if (postid == 0 && !string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(id))
{
if (source.ToLower() is "filmix" or "filmixapp")
{
if (!int.TryParse(id, out postid))
int.TryParse(Regex.Match(id, "/([0-9]+)-").Groups[1].Value, out postid);
}
}
if (await IsRequestBlocked(rch: true))
return badInitMsg;
if (string.IsNullOrEmpty(init.user_apitv))
return OnError("user_apitv", statusCode: 401, gbcache: false);
#region accessToken
var tokenResult = await EnsureAccessToken();
if (!tokenResult.IsSuccess)
return ShowError(HttpUtility.HtmlEncode(tokenResult.ErrorMsg));
init.token_apitv = tokenResult.Token;
#endregion
var bearer = HeadersModel.Init
(
("Authorization", $"Bearer {init.token_apitv}"),
("hash", init.hash_apitv)
);
var oninvk = new FilmixTVInvoke
(
host,
init.corsHost(),
bearer,
httpHydra,
streamfile => HostStreamProxy(streamfile),
requesterror: () => proxyManager?.Refresh(),
rjson: rjson
);
if (postid == 0)
{
var search = await InvokeCacheResult($"filmixtv:search:{title}:{original_title}:{clarification}:{similar}", 40,
() => oninvk.Search(title, original_title, clarification, year, similar)
);
if (!search.IsSuccess)
return OnError(search.ErrorMsg);
if (search.Value.id == 0)
return await ContentTpl(search.Value.similars);
postid = search.Value.id;
}
rhubFallback:
var cache = await InvokeCacheResult<RootObject>($"filmixtv:post:{postid}:{init.token_apitv}", 20, async e =>
{
string json = await httpHydra.Get($"{init.corsHost()}/api-fx/post/{postid}/video-links", addheaders: bearer, safety: true);
if (json == null)
return e.Fail("json", refresh_proxy: true);
return e.Success(oninvk.Post(json));
});
if (IsRhubFallback(cache, safety: true))
goto rhubFallback;
return await ContentTpl(cache, () => oninvk.Tpl(cache.Value, init.pro, postid, title, original_title, t, s, vast: init.vast));
}
#region [Copilot AI] EnsureAccessToken
async ValueTask<(bool IsSuccess, string Token, string ErrorMsg)> EnsureAccessToken()
{
try
{
await _accessTokenLock.WaitAsync(TimeSpan.FromMinutes(1));
string hashFile = $"cache/filmixtv-{CrypTo.md5(init.user_apitv)}.hash";
if (F.Exists(hashFile))
{
init.hash_apitv = F.ReadAllText(hashFile);
}
else
{
var rtk = await Http.Get<JObject>($"{init.corsHost()}/api-fx/request-token",
proxy: proxy, httpversion: init.GetHttpVersion(), timeoutSeconds: 30
);
if (rtk == null || !rtk.ContainsKey("token"))
return (false, null, "rtk");
string hash = rtk.Value<string>("token");
if (string.IsNullOrEmpty(hash))
return (false, null, "hash");
init.hash_apitv = hash;
}
var auth = await InvokeCacheResult<string>($"filmixtv:accessToken:{init.user_apitv}:{init.passwd_apitv}:{init.hash_apitv}", 60*5, async e =>
{
JObject root_auth = null;
string authFile = $"cache/filmixtv-{CrypTo.md5(init.hash_apitv)}.auth";
if (F.Exists(authFile))
{
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)}",
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.GetHttpVersion(), timeoutSeconds: 30
);
}
else
{
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,
proxy: proxy, headers: HeadersModel.Init("hash", init.hash_apitv), httpversion: init.GetHttpVersion(), timeoutSeconds: 30
);
}
string accessToken = root_auth?.GetValue("accessToken")?.ToString();
if (string.IsNullOrEmpty(accessToken))
{
if (root_auth != null)
{
if (root_auth.ContainsKey("msg"))
return e.Fail(root_auth.Value<string>("msg"));
return e.Fail(root_auth.ToString());
}
return e.Fail("accessToken");
}
F.WriteAllText(hashFile, init.hash_apitv);
F.WriteAllText(authFile, root_auth.ToString());
return e.Success(accessToken);
});
if (!auth.IsSuccess)
return (false, null, auth.ErrorMsg);
return (true, auth.Value, null);
}
catch (Exception ex) { return (false, null, ex.Message); }
finally
{
_accessTokenLock.Release();
}
}
#endregion
}
}