1181 lines
46 KiB
C#
1181 lines
46 KiB
C#
using DLNA.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using MongoDB.Driver;
|
|
using MonoTorrent;
|
|
using MonoTorrent.Client;
|
|
using NetVips;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using Shared;
|
|
using Shared.Engine;
|
|
using Shared.Engine.JacRed;
|
|
using Shared.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Web;
|
|
using IO = System.IO;
|
|
|
|
namespace DLNA.Controllers
|
|
{
|
|
public class DLNAController : BaseController
|
|
{
|
|
#region DLNAController
|
|
static string dlna_path => AppInit.conf.dlna.path;
|
|
|
|
static string defTrackers = "tr=http://retracker.local/announce&tr=http%3A%2F%2Fbt4.t-ru.org%2Fann%3Fmagnet&tr=http://retracker.mgts.by:80/announce&tr=http://tracker.city9x.com:2710/announce&tr=http://tracker.electro-torrent.pl:80/announce&tr=http://tracker.internetwarriors.net:1337/announce&tr=http://tracker2.itzmx.com:6961/announce&tr=udp://opentor.org:2710&tr=udp://public.popcorn-tracker.org:6969/announce&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://bt.svao-ix.ru/announce&tr=udp://explodie.org:6969/announce&tr=wss://tracker.btorrent.xyz&tr=wss://tracker.openwebtorrent.com";
|
|
|
|
static ClientEngine torrentEngine;
|
|
static DateTime lastBullderClientEngineCall = DateTime.MinValue;
|
|
|
|
public static void Initialization()
|
|
{
|
|
Directory.CreateDirectory("cache/torrent");
|
|
Directory.CreateDirectory($"{dlna_path}/");
|
|
Directory.CreateDirectory($"{dlna_path}/thumbs/");
|
|
Directory.CreateDirectory($"{dlna_path}/tmdb/");
|
|
|
|
ThreadPool.QueueUserWorkItem(async _ =>
|
|
{
|
|
string trackers_best_ip = await Http.Get("https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best_ip.txt", timeoutSeconds: 20);
|
|
if (trackers_best_ip != null)
|
|
{
|
|
foreach (string line in trackers_best_ip.Split("\n"))
|
|
{
|
|
string tr = line.Replace("\n", "").Replace("\r", "").Trim();
|
|
if (!string.IsNullOrWhiteSpace(tr))
|
|
defTrackers += $"&tr={tr}";
|
|
}
|
|
}
|
|
});
|
|
|
|
ThreadPool.QueueUserWorkItem(async _ =>
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(TimeSpan.FromMinutes(5));
|
|
await removeClientEngine();
|
|
}
|
|
catch { }
|
|
}
|
|
});
|
|
|
|
ThreadPool.QueueUserWorkItem(async _ =>
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(TimeSpan.FromMinutes(1));
|
|
|
|
if (torrentEngine == null)
|
|
continue;
|
|
|
|
if (lastBullderClientEngineCall == DateTime.MinValue || DateTime.UtcNow - lastBullderClientEngineCall < TimeSpan.FromMinutes(10))
|
|
continue;
|
|
|
|
if (!HasActiveTorrentTasks())
|
|
await removeClientEngine();
|
|
}
|
|
catch { }
|
|
}
|
|
});
|
|
|
|
if (!Directory.Exists("cache/metadata"))
|
|
return;
|
|
|
|
#region Resume download
|
|
var _files = Directory.GetFiles("cache/metadata", "*.torrent");
|
|
if (_files.Length == 0)
|
|
return;
|
|
|
|
bullderClientEngine();
|
|
|
|
foreach (string path in _files)
|
|
{
|
|
var t = Torrent.Load(path);
|
|
var manager = AppInit.conf.dlna.mode == "stream" ? torrentEngine.AddStreamingAsync(t, $"{dlna_path}/").Result : torrentEngine.AddAsync(t, $"{dlna_path}/").Result;
|
|
|
|
//if (FastResume.TryLoad($"cache/fastresume/{t.InfoHash.ToHex()}.fresume", out FastResume resume))
|
|
// manager.LoadFastResume(resume);
|
|
|
|
int[] indexs = null;
|
|
|
|
try
|
|
{
|
|
if (IO.File.Exists($"cache/metadata/{t.InfoHashes.V1.ToHex()}.json"))
|
|
indexs = JsonConvert.DeserializeObject<int[]>(IO.File.ReadAllText($"cache/metadata/{t.InfoHashes.V1.ToHex()}.json"));
|
|
}
|
|
catch { }
|
|
|
|
bool setPriority = false;
|
|
|
|
manager.TorrentStateChanged += async (s, e) =>
|
|
{
|
|
try
|
|
{
|
|
if (e != null && e.NewState == TorrentState.Seeding)
|
|
await e.TorrentManager.StopAsync();
|
|
|
|
if (e != null && (e.NewState == TorrentState.Metadata || e.NewState == TorrentState.Hashing || e.NewState == TorrentState.Downloading))
|
|
{
|
|
if (!setPriority)
|
|
{
|
|
setPriority = true;
|
|
|
|
if (indexs == null || indexs.Length == 0)
|
|
{
|
|
await manager.SetFilePriorityAsync(manager.Files[0], Priority.High);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < manager.Files.Count; i++)
|
|
{
|
|
if (indexs.Contains(i))
|
|
{
|
|
await manager.SetFilePriorityAsync(manager.Files[i], i == indexs[0] ? Priority.High : Priority.Normal);
|
|
}
|
|
else
|
|
{
|
|
await manager.SetFilePriorityAsync(manager.Files[i], Priority.DoNotDownload);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (e != null && (e.NewState == TorrentState.Stopped || e.NewState == TorrentState.Stopping))
|
|
{
|
|
try
|
|
{
|
|
IO.File.Delete(path);
|
|
IO.File.Delete(path.Replace(".torrent", ".json"));
|
|
}
|
|
catch { }
|
|
|
|
foreach (var f in e.TorrentManager.Files)
|
|
{
|
|
try
|
|
{
|
|
if (f.Priority == Priority.DoNotDownload && IO.File.Exists(f.FullPath))
|
|
IO.File.Delete(f.FullPath);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
await removeClientEngine(e.TorrentManager.InfoHashes.V1.ToHex().ToLower());
|
|
}
|
|
}
|
|
catch { }
|
|
};
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
#region dlna.js
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
[Route("dlna.js")]
|
|
[Route("dlna/js/{token}")]
|
|
public ActionResult Plugin(string token)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Content(string.Empty);
|
|
|
|
var sb = new StringBuilder(FileCache.ReadAllText("plugins/dlna.js"));
|
|
|
|
sb.Replace("{localhost}", host)
|
|
.Replace("{token}", HttpUtility.UrlEncode(token));
|
|
|
|
return Content(sb.ToString(), "application/javascript; charset=utf-8");
|
|
}
|
|
#endregion
|
|
|
|
#region bullderClientEngine
|
|
static Task bullderClientEngine(int connectionTimeout = 10)
|
|
{
|
|
lastBullderClientEngineCall = DateTime.UtcNow;
|
|
|
|
if (torrentEngine != null)
|
|
return Task.CompletedTask;
|
|
|
|
EngineSettingsBuilder engineSettingsBuilder = new EngineSettingsBuilder()
|
|
{
|
|
MaximumHalfOpenConnections = 20,
|
|
ConnectionTimeout = TimeSpan.FromSeconds(connectionTimeout),
|
|
MaximumDownloadRate = AppInit.conf.dlna.downloadSpeed,
|
|
MaximumUploadRate = AppInit.conf.dlna.uploadSpeed,
|
|
MaximumDiskReadRate = AppInit.conf.dlna.maximumDiskReadRate,
|
|
MaximumDiskWriteRate = AppInit.conf.dlna.maximumDiskWriteRate
|
|
};
|
|
|
|
torrentEngine = new ClientEngine(engineSettingsBuilder.ToSettings());
|
|
return torrentEngine.StartAllAsync();
|
|
}
|
|
#endregion
|
|
|
|
#region HasActiveTorrentTasks
|
|
static bool HasActiveTorrentTasks()
|
|
{
|
|
try
|
|
{
|
|
if (torrentEngine?.Torrents == null)
|
|
return false;
|
|
|
|
foreach (var torrent in torrentEngine.Torrents)
|
|
{
|
|
if (torrent.State == TorrentState.Metadata || torrent.State == TorrentState.Downloading || torrent.State == TorrentState.Starting || torrent.State == TorrentState.Hashing)
|
|
return true;
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
#region removeClientEngine
|
|
async static Task removeClientEngine(string hash = null)
|
|
{
|
|
try
|
|
{
|
|
if (torrentEngine?.Torrents != null)
|
|
{
|
|
var tdl = new List<TorrentManager>();
|
|
|
|
foreach (var i in torrentEngine.Torrents)
|
|
{
|
|
if (hash != null)
|
|
{
|
|
if (i.InfoHashes.V1.ToHex().ToLower() == hash)
|
|
{
|
|
try
|
|
{
|
|
await i.StopAsync(TimeSpan.FromSeconds(20));
|
|
}
|
|
catch { }
|
|
|
|
tdl.Add(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (i.State == TorrentState.Seeding || i.State == TorrentState.Stopped || i.State == TorrentState.Stopping)
|
|
{
|
|
try
|
|
{
|
|
await i.StopAsync(TimeSpan.FromSeconds(120));
|
|
}
|
|
catch { }
|
|
|
|
tdl.Add(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tdl.Count > 0)
|
|
{
|
|
foreach (var item in tdl)
|
|
{
|
|
try
|
|
{
|
|
torrentEngine.Torrents.Remove(item);
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
await torrentEngine.RemoveAsync(item);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
}
|
|
|
|
if (torrentEngine.Torrents.Count == 0)
|
|
{
|
|
try
|
|
{
|
|
await torrentEngine.StopAllAsync();
|
|
}
|
|
catch { }
|
|
|
|
torrentEngine.Dispose();
|
|
torrentEngine = null;
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
#endregion
|
|
|
|
#region getTorrent
|
|
async ValueTask<(string magnet, byte[] torrent)> getTorrent(string path)
|
|
{
|
|
if (!path.StartsWith("http"))
|
|
return (path, null);
|
|
|
|
string memkey = $"DLNAController:getTorrent:{path}";
|
|
if (!memoryCache.TryGetValue(memkey, out (string magnet, byte[] torrent) cache))
|
|
{
|
|
var handler = new System.Net.Http.HttpClientHandler()
|
|
{
|
|
AllowAutoRedirect = false
|
|
};
|
|
|
|
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
|
|
|
|
using (var client = new System.Net.Http.HttpClient(handler))
|
|
{
|
|
client.Timeout = TimeSpan.FromSeconds(10);
|
|
|
|
using (var response = await client.GetAsync(path))
|
|
{
|
|
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
|
{
|
|
using (var content = response.Content)
|
|
{
|
|
var t = await content.ReadAsByteArrayAsync();
|
|
cache.magnet = BencodeTo.Magnet(t);
|
|
if (cache.magnet != null)
|
|
cache.torrent = t;
|
|
}
|
|
}
|
|
else if ((int)response.StatusCode is 301 or 302 or 307)
|
|
{
|
|
string location = response.Headers.Location?.ToString() ?? response.RequestMessage.RequestUri?.ToString();
|
|
if (location != null && location.StartsWith("magnet:"))
|
|
cache.magnet = location;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cache.magnet == null && cache.torrent == null)
|
|
return (null, null);
|
|
|
|
memoryCache.Set(memkey, cache, DateTime.Now.AddMinutes(10));
|
|
}
|
|
|
|
return (cache.magnet, cache.torrent);
|
|
}
|
|
#endregion
|
|
|
|
#region getTmdb
|
|
JObject getTmdb(string name)
|
|
{
|
|
try
|
|
{
|
|
string file = $"{dlna_path}/tmdb/{CrypTo.md5(name)}.json";
|
|
if (IO.File.Exists(file))
|
|
{
|
|
var tmdb = JsonConvert.DeserializeObject<JObject>(IO.File.ReadAllText(file));
|
|
tmdb.Remove("created_by");
|
|
tmdb.Remove("networks");
|
|
tmdb.Remove("production_companies");
|
|
|
|
return tmdb;
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region getEpisodes
|
|
JArray getEpisodes(JObject tmdb, string fileName)
|
|
{
|
|
try
|
|
{
|
|
if (tmdb == null || !tmdb.ContainsKey("number_of_seasons"))
|
|
return null;
|
|
|
|
int season = getSeason(fileName);
|
|
|
|
string file = $"{dlna_path}/tmdb/{tmdb.Value<long>("id")}_season-{season}.json";
|
|
if (IO.File.Exists(file))
|
|
{
|
|
if (memoryCache.TryGetValue(file, out JArray episodes))
|
|
return episodes;
|
|
|
|
episodes = JsonConvert.DeserializeObject<JObject>(IO.File.ReadAllText(file)).Value<JArray>("episodes");
|
|
|
|
memoryCache.Set(file, episodes, DateTime.Now.AddSeconds(10));
|
|
return episodes;
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region getEpisode
|
|
int getEpisode(string fileName)
|
|
{
|
|
if (int.TryParse(Regex.Match(fileName, "EP?([0-9]+)", RegexOptions.IgnoreCase).Groups[1].Value, out int _e) && _e > 0)
|
|
return _e;
|
|
|
|
return 0;
|
|
}
|
|
#endregion
|
|
|
|
#region getSeason
|
|
int getSeason(string fileName)
|
|
{
|
|
if (int.TryParse(Regex.Match(fileName, "S([0-9]+)", RegexOptions.IgnoreCase).Groups[1].Value, out int _s) && _s > 0)
|
|
return _s;
|
|
|
|
return 0;
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Navigation
|
|
[HttpGet]
|
|
[Route("dlna")]
|
|
public ActionResult Index(string path)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Json(new { });
|
|
|
|
#region getImage
|
|
string getImage(string name)
|
|
{
|
|
string pathimage = $"thumbs/{name}.jpg";
|
|
if (IO.File.Exists($"{dlna_path}/" + pathimage))
|
|
return $"{host}/dlna/stream?path={HttpUtility.UrlEncode(pathimage)}";
|
|
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region getPreview
|
|
string getPreview(string name)
|
|
{
|
|
string pathimage = $"temp/{name}.mp4";
|
|
if (IO.File.Exists($"{dlna_path}/" + pathimage))
|
|
return $"{host}/dlna/stream?path={HttpUtility.UrlEncode(pathimage)}";
|
|
|
|
pathimage = $"temp/{name}.webm";
|
|
if (IO.File.Exists($"{dlna_path}/" + pathimage))
|
|
return $"{host}/dlna/stream?path={HttpUtility.UrlEncode(pathimage)}";
|
|
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region countFiles
|
|
int countFiles(string _path)
|
|
{
|
|
int count = 0;
|
|
|
|
foreach (string file in Directory.GetFiles(_path))
|
|
{
|
|
if (!Regex.IsMatch(Path.GetExtension(file), AppInit.conf.dlna.mediaPattern))
|
|
continue;
|
|
|
|
if (new FileInfo(file).Length > 0)
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endregion
|
|
|
|
var playlist = new List<DlnaModel>();
|
|
|
|
#region folders
|
|
foreach (string folder in Directory.GetDirectories($"{dlna_path}/" + path))
|
|
{
|
|
if (folder.Contains("thumbs") || folder.Contains("tmdb") || folder.Contains("temp"))
|
|
continue;
|
|
|
|
int length = countFiles(folder);
|
|
if (length > 0 || Directory.GetDirectories(folder).Length > 0)
|
|
{
|
|
playlist.Add(new DlnaModel()
|
|
{
|
|
type = "folder",
|
|
name = Path.GetFileName(folder),
|
|
uri = $"{host}/dlna?path={HttpUtility.UrlEncode(folder.Replace($"{dlna_path}/", ""))}",
|
|
img = getImage(CrypTo.md5(Path.GetFileName(folder))),
|
|
preview = getPreview(CrypTo.md5(Path.GetFileName(folder))),
|
|
path = folder.Replace($"{dlna_path}/", ""),
|
|
length = countFiles(folder),
|
|
creationTime = Directory.GetCreationTime(folder),
|
|
tmdb = getTmdb(Path.GetFileName(folder))
|
|
});
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region files
|
|
var filesTmdb = getTmdb(path);
|
|
var subtitles = Directory.GetFiles($"{dlna_path}/" + path, "*.srt");
|
|
|
|
foreach (string file in Directory.GetFiles($"{dlna_path}/" + path))
|
|
{
|
|
if (!Regex.IsMatch(Path.GetExtension(file), AppInit.conf.dlna.mediaPattern))
|
|
continue;
|
|
|
|
string name = Path.GetFileName(file);
|
|
var fileinfo = new FileInfo(file);
|
|
if (fileinfo.Length == 0)
|
|
continue;
|
|
|
|
JObject episodeTmdb = null;
|
|
|
|
string img = getImage(CrypTo.md5(name));
|
|
var episodes = getEpisodes(filesTmdb, name);
|
|
if (episodes != null)
|
|
{
|
|
int episode = getEpisode(name);
|
|
if (episode > 0)
|
|
{
|
|
episodeTmdb = episodes.FirstOrDefault(i => i.Value<int>("episode_number") == episode)?.ToObject<JObject>();
|
|
episodeTmdb.Remove("crew");
|
|
episodeTmdb.Remove("guest_stars");
|
|
|
|
if (episodeTmdb != null && episodeTmdb.ContainsKey("still_path"))
|
|
img = $"tmdb:/t/p/w400" + episodeTmdb.Value<string>("still_path");
|
|
}
|
|
}
|
|
|
|
if (img == null)
|
|
img = getImage(CrypTo.md5($"{path}/{name}"));
|
|
|
|
var dlnaModel = new DlnaModel()
|
|
{
|
|
type = "file",
|
|
name = name,
|
|
uri = $"{host}/dlna/stream?path={HttpUtility.UrlEncode(file.Replace($"{dlna_path}/", ""))}",
|
|
img = img,
|
|
preview = getPreview(CrypTo.md5(name)),
|
|
subtitles = new List<Subtitle>(),
|
|
path = file.Replace($"{dlna_path}/", ""),
|
|
length = fileinfo.Length,
|
|
creationTime = fileinfo.CreationTime,
|
|
s = getSeason(name),
|
|
e = getEpisode(name),
|
|
tmdb = string.IsNullOrEmpty(path) ? getTmdb(name) : filesTmdb,
|
|
episode = episodeTmdb
|
|
};
|
|
|
|
#region subtitles
|
|
foreach (string subfile in subtitles)
|
|
{
|
|
if (subfile.Contains(Path.GetFileNameWithoutExtension(file)))
|
|
{
|
|
dlnaModel.subtitles.Add(new Subtitle()
|
|
{
|
|
label = "Sub #1",
|
|
url = $"{host}/dlna/stream?path={HttpUtility.UrlEncode($"{path}/{Path.GetFileName(subfile)}")}"
|
|
});
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
playlist.Add(dlnaModel);
|
|
}
|
|
#endregion
|
|
|
|
var jSettings = new JsonSerializerSettings()
|
|
{
|
|
DefaultValueHandling = DefaultValueHandling.Ignore,
|
|
NullValueHandling = NullValueHandling.Ignore
|
|
};
|
|
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
#region torrentEngine
|
|
if (torrentEngine?.Torrents != null)
|
|
{
|
|
foreach (var t in torrentEngine.Torrents)
|
|
{
|
|
if (t.State == TorrentState.Metadata || t.State == TorrentState.Downloading || t.State == TorrentState.Starting)
|
|
{
|
|
if (t.Torrent?.Name == null || (!IO.File.Exists($"{dlna_path}/{t.Torrent.Name}") && !Directory.Exists($"{dlna_path}/{t.Torrent.Name}")))
|
|
{
|
|
playlist.Add(new DlnaModel()
|
|
{
|
|
type = "file",
|
|
name = t.Torrent?.Name ?? t.InfoHashes.V1.ToHex(),
|
|
img = getImage(t.InfoHashes.V1.ToHex())
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
return ContentTo(JsonConvert.SerializeObject(playlist.OrderByDescending(i => i.creationTime), jSettings));
|
|
}
|
|
|
|
return ContentTo(JsonConvert.SerializeObject(playlist.OrderBy(i =>
|
|
{
|
|
ulong.TryParse(Regex.Replace(i.name, "[^0-9]+", ""), out ulong ident);
|
|
return ident;
|
|
|
|
}), jSettings));
|
|
}
|
|
#endregion
|
|
|
|
#region Stream
|
|
[HttpGet]
|
|
[Route("dlna/stream")]
|
|
public ActionResult Stream(string path)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Json(new { });
|
|
|
|
string contentType = "application/octet-stream";
|
|
|
|
if (path.EndsWith(".jpg"))
|
|
contentType = "image/jpeg";
|
|
|
|
return File(IO.File.OpenRead($"{dlna_path}/{path}"), contentType, true);
|
|
}
|
|
#endregion
|
|
|
|
#region Delete
|
|
[HttpGet]
|
|
[Route("dlna/delete")]
|
|
public ActionResult Delete(string path)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Content(string.Empty);
|
|
|
|
try
|
|
{
|
|
IO.File.Delete($"{dlna_path}/{path}");
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
Directory.Delete($"{dlna_path}/{path}", true);
|
|
}
|
|
catch { }
|
|
|
|
return Content(string.Empty);
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Managers
|
|
[HttpGet]
|
|
[Route("dlna/tracker/managers")]
|
|
public ActionResult Managers()
|
|
{
|
|
if (!AppInit.conf.dlna.enable || torrentEngine?.Torrents == null)
|
|
return Content("[]");
|
|
|
|
return Json(torrentEngine.Torrents.Select(i => new
|
|
{
|
|
InfoHash = i.InfoHashes.V1.ToHex(),
|
|
Name = i.Torrent?.Name ?? i.InfoHashes.V1.ToHex(),
|
|
//Engine = new
|
|
//{
|
|
// i.Engine.ConnectionManager.HalfOpenConnections,
|
|
// i.Engine.ConnectionManager.OpenConnections,
|
|
// i.Engine.TotalDownloadSpeed,
|
|
// i.Engine.TotalUploadSpeed,
|
|
//},
|
|
Files = i.Files?.Select(f => new
|
|
{
|
|
f.Path,
|
|
Priority = f.Priority.ToString(),
|
|
f.Length,
|
|
BytesDownloaded = f.BytesDownloaded()
|
|
}),
|
|
i.Monitor,
|
|
i.OpenConnections,
|
|
i.PartialProgress,
|
|
i.Progress,
|
|
i.Peers,
|
|
State = i.State.ToString(),
|
|
i.UploadingTo
|
|
}));
|
|
}
|
|
#endregion
|
|
|
|
#region Show
|
|
[HttpGet]
|
|
[Route("dlna/tracker/show")]
|
|
async public Task<JsonResult> Show(string path)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Json(new { error = "enable" });
|
|
|
|
try
|
|
{
|
|
var tparse = await getTorrent(path);
|
|
if (tparse.torrent != null)
|
|
return Json(Torrent.Load(tparse.torrent).Files.Select(i => new { i.Path }));
|
|
|
|
if (tparse.magnet == null)
|
|
return Json(new { error = "magnet" });
|
|
|
|
string hash = Regex.Match(tparse.magnet, "btih:([a-z0-9]+)", RegexOptions.IgnoreCase).Groups[1].Value.ToLower();
|
|
if (IO.File.Exists($"cache/torrent/{hash}"))
|
|
return Json(Torrent.Load(IO.File.ReadAllBytes($"cache/torrent/{hash}")).Files.Select(i => new { i.Path }));
|
|
|
|
var s_cts = new CancellationTokenSource();
|
|
s_cts.CancelAfter(1000 * 60 * 3);
|
|
|
|
string magnet = tparse.magnet;
|
|
magnet += (magnet.Contains("?") ? "&" : "?") + defTrackers;
|
|
|
|
#region trackers
|
|
//if (IO.File.Exists("cache/trackers.txt") && AppInit.conf.dlna.addTrackersToMagnet)
|
|
//{
|
|
// foreach (string line in IO.File.ReadLines("cache/trackers.txt"))
|
|
// {
|
|
// if (string.IsNullOrWhiteSpace(line))
|
|
// continue;
|
|
|
|
// if (line.StartsWith("http") || line.StartsWith("udp:"))
|
|
// {
|
|
// string host = line.Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim();
|
|
// string tr = HttpUtility.UrlEncode(host);
|
|
|
|
// if (!magnet.Contains(tr))
|
|
// magnet += $"&tr={tr}";
|
|
// }
|
|
// }
|
|
//}
|
|
#endregion
|
|
|
|
await bullderClientEngine();
|
|
|
|
if (torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex().ToLower() == hash) is TorrentManager manager)
|
|
{
|
|
if (manager.Files != null)
|
|
return Json(manager.Files.Select(i => (ITorrentFile)i).Select(i => new { i.Path }));
|
|
|
|
await manager.WaitForMetadataAsync(s_cts.Token);
|
|
var files = manager.Files.Select(i => (ITorrentFile)i);
|
|
return Json(files.Select(i => new { i.Path }));
|
|
}
|
|
|
|
var data = await torrentEngine.DownloadMetadataAsync(MagnetLink.Parse(magnet), s_cts.Token);
|
|
if (data.IsEmpty)
|
|
return Json(new { error = "DownloadMetadata" });
|
|
|
|
IO.File.WriteAllBytes($"cache/torrent/{hash}", data.Span);
|
|
|
|
return Json(Torrent.Load(data.Span).Files.Select(i => new { i.Path }));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Json(new { error = ex.ToString() });
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Download
|
|
[HttpGet]
|
|
[Route("dlna/tracker/download")]
|
|
async public Task<JsonResult> Download(string path, int[] indexs, string thumb, long id, bool serial, int lastCount = -1)
|
|
{
|
|
if (!AppInit.conf.dlna.enable)
|
|
return Json(new { error = "enable" });
|
|
|
|
try
|
|
{
|
|
var tparse = await getTorrent(path);
|
|
if (tparse.magnet == null)
|
|
return Json(new { error = "magnet" });
|
|
|
|
// cache metadata
|
|
string hash = Regex.Match(tparse.magnet, "btih:([a-z0-9]+)", RegexOptions.IgnoreCase).Groups[1].Value.ToLower();
|
|
if (IO.File.Exists($"cache/torrent/{hash}") && !IO.File.Exists($"cache/metadata/{hash.ToUpper()}.torrent"))
|
|
IO.File.Copy($"cache/torrent/{hash}", $"cache/metadata/{hash.ToUpper()}.torrent");
|
|
|
|
var magnetLink = MagnetLink.Parse(tparse.magnet + (tparse.magnet.Contains("?") ? "&" : "?") + defTrackers);
|
|
|
|
await bullderClientEngine();
|
|
TorrentManager manager = torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex() == magnetLink.InfoHashes.V1.ToHex());
|
|
|
|
ThreadPool.QueueUserWorkItem(async _ =>
|
|
{
|
|
try
|
|
{
|
|
#region Download thumb
|
|
if (thumb != null)
|
|
{
|
|
try
|
|
{
|
|
#region IsValidImg
|
|
bool IsValidImg(byte[] _img)
|
|
{
|
|
if (AppInit.conf.imagelibrary == "NetVips")
|
|
return IsValidImgage(_img, path);
|
|
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
string uri = Regex.Replace(thumb, "^https?://[^/]+/", "");
|
|
|
|
var array = await Http.Download($"https://image.tmdb.org/{uri}", timeoutSeconds: 10);
|
|
if (array == null || !IsValidImg(array))
|
|
array = await Http.Download($"https://imagetmdb.{AppInit.conf.cub.mirror}/{uri}", timeoutSeconds: 10);
|
|
|
|
if (array != null && IsValidImg(array))
|
|
{
|
|
Directory.CreateDirectory($"{dlna_path}/thumbs");
|
|
IO.File.WriteAllBytes($"{dlna_path}/thumbs/{magnetLink.InfoHashes.V1.ToHex()}.jpg", array);
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
#endregion
|
|
|
|
if (manager == null)
|
|
{
|
|
dynamic tlink = tparse.torrent != null ? Torrent.Load(tparse.torrent) : magnetLink;
|
|
manager = AppInit.conf.dlna.mode == "stream" ? await torrentEngine.AddStreamingAsync(tlink, $"{dlna_path}/") : await torrentEngine.AddAsync(tlink, $"{dlna_path}/");
|
|
|
|
#region AddTrackerAsync
|
|
if (IO.File.Exists("cache/trackers.txt") && AppInit.conf.dlna.addTrackersToMagnet)
|
|
{
|
|
foreach (string line in IO.File.ReadLines("cache/trackers.txt").OrderBy(x => Random.Shared.Next()))
|
|
{
|
|
if (string.IsNullOrWhiteSpace(line))
|
|
continue;
|
|
|
|
string host = line.Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim();
|
|
|
|
if (host.StartsWith("http") || host.StartsWith("udp:"))
|
|
{
|
|
try
|
|
{
|
|
await manager.TrackerManager.AddTrackerAsync(new Uri(host));
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
await manager.StartAsync();
|
|
await manager.WaitForMetadataAsync();
|
|
|
|
#region thumb
|
|
if (IO.File.Exists($"{dlna_path}/thumbs/{magnetLink.InfoHashes.V1.ToHex()}.jpg"))
|
|
{
|
|
try
|
|
{
|
|
IO.File.Copy($"{dlna_path}/thumbs/{magnetLink.InfoHashes.V1.ToHex()}.jpg", $"{dlna_path}/thumbs/{CrypTo.md5(manager.Torrent.Name)}.jpg", true);
|
|
}
|
|
catch { }
|
|
}
|
|
#endregion
|
|
|
|
#region TorrentStateChanged
|
|
bool dispose = false;
|
|
|
|
manager.TorrentStateChanged += async (s, e) =>
|
|
{
|
|
try
|
|
{
|
|
if (!dispose && e != null && (e.NewState == TorrentState.Seeding || e.NewState == TorrentState.Stopped || e.NewState == TorrentState.Stopping))
|
|
{
|
|
dispose = true;
|
|
|
|
try
|
|
{
|
|
IO.File.Delete($"cache/metadata/{e.TorrentManager.InfoHashes.V1.ToHex()}.torrent");
|
|
IO.File.Delete($"cache/metadata/{e.TorrentManager.InfoHashes.V1.ToHex()}.json");
|
|
}
|
|
catch { }
|
|
|
|
foreach (var f in e.TorrentManager.Files)
|
|
{
|
|
try
|
|
{
|
|
if (f.Priority == Priority.DoNotDownload && IO.File.Exists(f.FullPath))
|
|
{
|
|
if (f.Length == 0 || f.BytesDownloaded() == 0)
|
|
{
|
|
IO.File.Delete(f.FullPath);
|
|
}
|
|
else
|
|
{
|
|
double percentageDownloaded = (double)f.BytesDownloaded() / f.Length;
|
|
|
|
if (percentageDownloaded < 0.9)
|
|
IO.File.Delete(f.FullPath);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
await removeClientEngine(e.TorrentManager.InfoHashes.V1.ToHex().ToLower());
|
|
}
|
|
}
|
|
catch { }
|
|
};
|
|
#endregion
|
|
|
|
#region lastCount
|
|
if (lastCount > 0 && manager.Files.Count >= lastCount)
|
|
{
|
|
var _indexs = new List<int>();
|
|
for (int i = manager.Files.Count-1; i >= 0; i--)
|
|
{
|
|
if (_indexs.Count == lastCount)
|
|
break;
|
|
|
|
if (!Regex.IsMatch(Path.GetExtension(manager.Files[i].Path), AppInit.conf.dlna.mediaPattern))
|
|
continue;
|
|
|
|
_indexs.Add(i);
|
|
}
|
|
|
|
indexs = _indexs.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region indexs
|
|
if (indexs == null || indexs.Length == 0)
|
|
{
|
|
foreach (var file in manager.Files)
|
|
{
|
|
if (file.Priority != Priority.Normal)
|
|
await manager.SetFilePriorityAsync(file, Priority.Normal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory("cache/metadata/");
|
|
IO.File.WriteAllText($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.json", JsonConvert.SerializeObject(indexs));
|
|
|
|
for (int i = 0; i < manager.Files.Count; i++)
|
|
{
|
|
if (indexs.Contains(i))
|
|
{
|
|
if (manager.Files[i].Priority != Priority.Normal)
|
|
await manager.SetFilePriorityAsync(manager.Files[i], Priority.Normal);
|
|
}
|
|
else
|
|
{
|
|
if (manager.Files[i].Priority != Priority.DoNotDownload)
|
|
await manager.SetFilePriorityAsync(manager.Files[i], Priority.DoNotDownload);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region tmdb
|
|
string cat = serial ? "tv" : "movie";
|
|
var header = HeadersModel.Init(("localrequest", AppInit.rootPasswd));
|
|
string json = await Http.Get($"http://{AppInit.conf.listen.localhost}:{AppInit.conf.listen.port}/tmdb/api/3/{cat}/{id}?api_key={AppInit.conf.tmdb.api_key}&language=ru", timeoutSeconds: 20, headers: header);
|
|
|
|
if (string.IsNullOrEmpty(json))
|
|
json = await Http.Get($"https://apitmdb.{AppInit.conf.cub.mirror}/3/{cat}/{id}?api_key={AppInit.conf.tmdb.api_key}&language=ru", timeoutSeconds: 20);
|
|
|
|
if (!string.IsNullOrEmpty(json))
|
|
{
|
|
IO.File.WriteAllText($"{dlna_path}/tmdb/{CrypTo.md5(manager.Torrent.Name)}.json", json);
|
|
|
|
if (serial)
|
|
{
|
|
if (int.TryParse(Regex.Match(json, "\"number_of_seasons\":([0-9 ]+)").Groups[1].Value.Trim(), out int number_of_seasons) && number_of_seasons > 0)
|
|
{
|
|
async ValueTask write(int s)
|
|
{
|
|
string seasons = await Http.Get($"http://{AppInit.conf.listen.localhost}:{AppInit.conf.listen.port}/tmdb/api/3/{cat}/{id}/season/{s}?api_key={AppInit.conf.tmdb.api_key}&language=ru", timeoutSeconds: 20, headers: header);
|
|
|
|
if (string.IsNullOrEmpty(seasons))
|
|
seasons = await Http.Get($"https://apitmdb.{AppInit.conf.cub.mirror}/3/{cat}/{id}/season/{s}?api_key={AppInit.conf.tmdb.api_key}&language=ru", timeoutSeconds: 20);
|
|
|
|
if (!string.IsNullOrEmpty(seasons))
|
|
IO.File.WriteAllText($"{dlna_path}/tmdb/{id}_season-{s}.json", json);
|
|
}
|
|
|
|
if (number_of_seasons == 1)
|
|
await write(number_of_seasons);
|
|
else
|
|
{
|
|
foreach (var f in manager.Files)
|
|
{
|
|
int s = getSeason(Path.GetFileName(f.Path));
|
|
if (s > 0)
|
|
await write(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
catch { }
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Json(new { error = ex.ToString() });
|
|
}
|
|
|
|
return Json(new { status = true });
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Delete
|
|
[HttpGet]
|
|
[Route("dlna/tracker/stop")]
|
|
[Route("dlna/tracker/delete")]
|
|
async public Task<JsonResult> TorrentDelete(string infohash)
|
|
{
|
|
if (!AppInit.conf.dlna.enable || torrentEngine == null)
|
|
return Json(new { });
|
|
|
|
var manager = torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex() == infohash);
|
|
if (manager != null)
|
|
{
|
|
try
|
|
{
|
|
IO.File.Delete($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.torrent");
|
|
IO.File.Delete($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.json");
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
await manager.StopAsync();
|
|
}
|
|
catch { }
|
|
|
|
await removeClientEngine(manager.InfoHashes.V1.ToHex().ToLower());
|
|
}
|
|
|
|
return Json(new { status = true });
|
|
}
|
|
#endregion
|
|
|
|
#region Pause
|
|
[HttpGet]
|
|
[Route("dlna/tracker/pause")]
|
|
async public Task<JsonResult> TorrentPause(string infohash)
|
|
{
|
|
if (!AppInit.conf.dlna.enable || torrentEngine == null)
|
|
return Json(new { });
|
|
|
|
var manager = torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex() == infohash);
|
|
if (manager != null)
|
|
await manager.PauseAsync();
|
|
|
|
return Json(new { status = true });
|
|
}
|
|
#endregion
|
|
|
|
#region Start
|
|
[HttpGet]
|
|
[Route("dlna/tracker/start")]
|
|
async public Task<JsonResult> TorrentStart(string infohash)
|
|
{
|
|
if (!AppInit.conf.dlna.enable || torrentEngine == null)
|
|
return Json(new { });
|
|
|
|
var manager = torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex() == infohash);
|
|
if (manager != null)
|
|
await manager.StartAsync();
|
|
|
|
return Json(new { status = true });
|
|
}
|
|
#endregion
|
|
|
|
#region ChangeFilePriority
|
|
[HttpGet]
|
|
[Route("dlna/tracker/changefilepriority")]
|
|
async public Task<JsonResult> ChangeFilePriority(string infohash, int[] indexs)
|
|
{
|
|
if (!AppInit.conf.dlna.enable || torrentEngine == null)
|
|
return Json(new { });
|
|
|
|
var manager = torrentEngine.Torrents.FirstOrDefault(i => i.InfoHashes.V1.ToHex() == infohash);
|
|
if (manager != null)
|
|
{
|
|
if (indexs == null || indexs.Length == 0)
|
|
{
|
|
foreach (var file in manager.Files)
|
|
{
|
|
if (file.Priority != Priority.Normal)
|
|
await manager.SetFilePriorityAsync(file, Priority.Normal);
|
|
}
|
|
|
|
if (IO.File.Exists($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.json"))
|
|
IO.File.Delete($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.json");
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory("cache/metadata/");
|
|
IO.File.WriteAllText($"cache/metadata/{manager.InfoHashes.V1.ToHex()}.json", JsonConvert.SerializeObject(indexs));
|
|
|
|
for (int i = 0; i < manager.Files.Count; i++)
|
|
{
|
|
if (indexs.Contains(i))
|
|
{
|
|
if (manager.Files[i].Priority != Priority.Normal)
|
|
await manager.SetFilePriorityAsync(manager.Files[i], Priority.Normal);
|
|
}
|
|
else
|
|
{
|
|
if (manager.Files[i].Priority != Priority.DoNotDownload)
|
|
await manager.SetFilePriorityAsync(manager.Files[i], Priority.DoNotDownload);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Json(new { status = true });
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
#region IsValidImgage
|
|
static bool IsValidImgage(byte[] _img, string path)
|
|
{
|
|
if (_img == null)
|
|
return false;
|
|
|
|
using (var image = Image.NewFromBuffer(_img))
|
|
{
|
|
try
|
|
{
|
|
if (!path.Contains(".svg"))
|
|
{
|
|
// тестируем jpg/png на целостность
|
|
byte[] temp = image.JpegsaveBuffer();
|
|
if (temp == null || temp.Length == 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
} |