Skip to content

Commit

Permalink
Add preview image functionality to Civitai Resources
Browse files Browse the repository at this point in the history
  • Loading branch information
hbl917070 committed Apr 25, 2024
1 parent d354e3c commit 78c8a72
Show file tree
Hide file tree
Showing 18 changed files with 645 additions and 205 deletions.
8 changes: 4 additions & 4 deletions Tiefsee/Lib/FileLib.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -195,12 +194,13 @@ public static string FileToHash(string path) {
using var sha256 = System.Security.Cryptography.SHA256.Create();
string s;
if (File.Exists(path)) {
long fileSize = new FileInfo(path).Length; // File size
long ticks = new FileInfo(path).LastWriteTime.Ticks; // File last modified time
var fileinfo = new FileInfo(path);
long fileSize = fileinfo.Length; // File size
long ticks = fileinfo.LastWriteTime.Ticks; // File last modified time
s = Convert.ToBase64String(sha256.ComputeHash(Encoding.Default.GetBytes(fileSize + path + ticks)));
}
else {
s = path;
s = Convert.ToBase64String(sha256.ComputeHash(Encoding.Default.GetBytes(path)));
}
return s.ToLower().Replace("\\", "").Replace("/", "").Replace("+", "").Replace("=", "");
}
Expand Down
16 changes: 10 additions & 6 deletions Tiefsee/Lib/ImgLib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@ public class ImgLib {
/// 取得任何檔案的圖示
/// </summary>
/// <param name="path"></param>
/// <param name="size">16 32 64 128 256</param>
/// <param name="size"> 16 32 64 128 256 </param>
/// <param name="waitSec"> 等待秒數 </param>
/// <returns></returns>
public static Bitmap GetFileIcon(string path, int size) {
public static Bitmap GetFileIcon(string path, int size, double waitSec = 1.5) {
if (File.Exists(path) == false) { return null; }

Bitmap icon = null;

Adapter.RunWithTimeout(1, () => {
// 取得圖片在Windows系統的縮圖
icon = WindowsThumbnailProvider.GetThumbnail(path, size, size, ThumbnailOptions.ScaleUp);
});
try {
Adapter.RunWithTimeout(waitSec, () => {
// 取得圖片在Windows系統的縮圖
icon = WindowsThumbnailProvider.GetThumbnail(path, size, size, ThumbnailOptions.ScaleUp);
});
}
catch { }

return icon;
}
Expand Down
117 changes: 78 additions & 39 deletions Tiefsee/Server/WebServerController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Text;
using System.Text.Json;

Expand Down Expand Up @@ -27,6 +29,7 @@ public WebServerController(WebServer ws) {
webServer.RouteAdd("/api/getPdf", GetPdf);
webServer.RouteAdd("/api/getText", GetText);
webServer.RouteAdd("/api/getFileIcon", GetFileIcon);
webServer.RouteAdd("/api/getWebIcon", GetWebIcon);
webServer.RouteAdd("/api/getFileInfo2", GetFileInfo2);
webServer.RouteAdd("/api/getFileInfo2List", GetFileInfo2List);
webServer.RouteAdd("/api/getUwpList", GetUwpList);
Expand Down Expand Up @@ -337,14 +340,14 @@ private void GetPdf(RequestData d) {
}

/// <summary>
/// 取得檔案的Exif資訊
/// 取得檔案的 Exif 資訊
/// </summary>
void GetText(RequestData d) {

string path = d.args["path"];
path = Uri.UnescapeDataString(path);

//如果檔案不存在就返回404錯誤
//如果檔案不存在就返回 404 錯誤
if (File.Exists(path) == false) {
d.context.Response.StatusCode = 404;
WriteString(d, JsonSerializer.Serialize(new ImgExif()));
Expand All @@ -360,7 +363,7 @@ void GetText(RequestData d) {
}

/// <summary>
///
/// 取得檔案的 Icon
/// </summary>
private void GetFileIcon(RequestData d) {

Expand All @@ -370,59 +373,98 @@ private void GetFileIcon(RequestData d) {

// 如果檔案不存在就返回 404 錯誤
if (File.Exists(path) == false) {
d.context.Response.StatusCode = 404;
WriteString(d, "404");
WriteError(d, 404, "找不到檔案");
return;
}

d.context.Response.ContentType = "image/png";
bool is304 = HeadersAdd304(d, path); // 回傳檔案時加入快取的 Headers
if (is304) { return; }

Bitmap icon = null;
using Bitmap icon = ImgLib.GetFileIcon(path, size, 3);

// 如果取得失敗,就返回 500 錯誤
if (icon == null) {
WriteError(d, 500, "圖示取得失敗");
return;
}

try {
icon = ImgLib.GetFileIcon(path, size);
using Stream input = new MemoryStream();
icon.Save(input, System.Drawing.Imaging.ImageFormat.Png);
input.Position = 0;

WriteStream(d, input); // 回傳檔案

icon.Dispose();
}
catch {
WriteError(d, 500, "圖示解析失敗");
}
catch { }
}

// 如果取得失敗,就等待 1 秒後再試一次
if (icon == null) {
Thread.Sleep(1000);
/// <summary>
/// 從網路下載圖片後,返回圖片的 icon
/// </summary>
/// <param name="d"></param>
private async void GetWebIcon(RequestData d) {

var args = d.args;
int size = Int32.Parse(args["size"]);
string path = Uri.UnescapeDataString(args["path"]); // 檔案儲存的相對路徑
string url = Uri.UnescapeDataString(args["url"]);

string tempPath = Path.Combine(AppPath.tempDirWebFile, path);

// 如果資料夾不存在就建立
if (Directory.Exists(Path.GetDirectoryName(tempPath)) == false) {
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
}

if (File.Exists(tempPath) == false) {
try {
icon = ImgLib.GetFileIcon(path, size);
// 下載圖片
using HttpClient webClient = new();
webClient.Timeout = TimeSpan.FromSeconds(10); // 設定超時時間為 10 秒
byte[] data = webClient.GetByteArrayAsync(url).Result;
File.WriteAllBytes(tempPath, data);
}
catch (Exception ex) {
Debug.WriteLine("GetWebIcon fail " + ex.Message);
WriteError(d, 500, "圖片下載失敗: " + ex);
return;
}
catch { }
}

// 如果 2 次都取得失敗,就返回 500 錯誤
// 如果檔案不存在就返回 404 錯誤
if (File.Exists(tempPath) == false) {
WriteError(d, 404, "找不到檔案");
return;
}

d.context.Response.ContentType = "image/png";
bool is304 = HeadersAdd304(d, tempPath); // 回傳檔案時加入快取的 Headers
if (is304) { return; }

using Bitmap icon = ImgLib.GetFileIcon(tempPath, size, 3);

// 如果取得失敗,就返回 500 錯誤
if (icon == null) {
d.context.Response.StatusCode = 500;
WriteString(d, "500");
WriteError(d, 500, "圖示取得失敗");
return;
}

try {
using Stream input = new MemoryStream();

icon.Save(input, System.Drawing.Imaging.ImageFormat.Png);
input.Position = 0;

d.context.Response.ContentLength64 = input.Length;
WriteStream(d, input); // 回傳檔案

if (d.context.Request.HttpMethod != "HEAD") {
byte[] buffer = new byte[1024 * 16];
int nbytes;
while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0) {
// context.Response.SendChunked = input.Length > 1024 * 16;
d.context.Response.OutputStream.Write(buffer, 0, nbytes);
}
}
icon.Dispose();
}
catch {
d.context.Response.StatusCode = 500;
WriteString(d, "500");
WriteError(d, 500, "圖示解析失敗");
}
}

Expand Down Expand Up @@ -700,11 +742,17 @@ private bool HeadersAdd304(RequestData d, string path) {
return false;
}

/// <summary>
/// 回傳錯誤
/// </summary>
private void WriteError(RequestData d, int code, string msg) {
d.context.Response.StatusCode = code;
WriteString(d, msg);
}

/// <summary>
/// 回傳字串
/// </summary>
/// <param name="context"></param>
/// <param name="str"></param>
private void WriteString(RequestData d, string str) {
d.context.Response.AddHeader("Content-Encoding", "br"); // 告訴瀏覽器使用了Brotli壓縮
d.context.Response.AddHeader("Content-Type", "text/text; charset=utf-8"); //設定編碼
Expand Down Expand Up @@ -738,15 +786,7 @@ private void WriteFile(RequestData d, string path) {
d.context.Response.OutputStream.Write(buffer, 0, nbytes);
}
}
// context.Response.StatusCode = (int)HttpStatusCode.OK;
// context.Response.OutputStream.Flush();
}
/*using (FileStream fs = new FileStream(_path, FileMode.Open, FileAccess.Read, FileAccess.Read, FileShare.ReadWrite)) {
byte[] _responseArray = new byte[fs.Length];
fs.Read(_responseArray, 0, _responseArray.Length);
fs.Close();
context.Response.OutputStream.Write(_responseArray, 0, _responseArray.Length); // write bytes to the output stream
}*/
}

/// <summary>
Expand All @@ -764,7 +804,6 @@ void WriteStream(RequestData d, Stream ms) {
}
}


private IDictionary<string, string> _mimeTypeMappings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) {
#region extension to MIME type list
{".asf", "video/x-ms-asf"},
Expand Down
3 changes: 3 additions & 0 deletions Tiefsee/WebWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public static string GetAppInfo(string[] args, int quickLookRunType) {
startPort = Program.startPort,
appDirPath = System.AppDomain.CurrentDomain.BaseDirectory,
appDataPath = AppPath.appData,
tempDirWebFile = AppPath.tempDirWebFile,
mainPort = Program.webServer.port,
settingPath = AppPath.appDataSetting,
quickLookRunType = quickLookRunType,
Expand Down Expand Up @@ -266,6 +267,8 @@ public class AppInfo {
public string appDirPath { get; set; }
/// <summary> 程式的暫存資料夾 </summary>
public string appDataPath { get; set; }
/// <summary> 暫存資料夾 - 從網路下載的檔案 </summary>
public string tempDirWebFile { get; set; }
/// <summary> 目前使用的 port </summary>
public int mainPort { get; set; }
/// <summary> setting.js 的路徑 </summary>
Expand Down
44 changes: 40 additions & 4 deletions Www/ejs/SettingWindow/SettingWindow.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@
</label>
</div>

<div class="text-title" i18n="sw.other.displayDeleteConfirmationDialog">
檔案刪除前顯示確認視窗
<div class="text-title" i18n="sw.other.displayDeleteConfirmationDialog">
檔案刪除前顯示確認視窗
</div>
<div class="text-content">
<label class="switch">
Expand All @@ -304,8 +304,8 @@
</label>
</div>

<div class="text-title" i18n="sw.other.whenInsertingFile">
偵測到檔案新增時,插入於
<div class="text-title" i18n="sw.other.whenInsertingFile">
偵測到檔案新增時,插入於
</div>
<div class="text-content">
<select class="text-input" id="select-whenInsertingFile">
Expand Down Expand Up @@ -576,6 +576,42 @@
<input class="text-input" type="number" id="text-mainExifMaxLine">
</div>

<!-- Civitai Resources -->
<div class="text-title" i18n="sw.informationPanel.civitaiResourcesEnabled"> 顯示 Civitai Resources </div>
<div class="text-content">
<label class="switch">
<input type="checkbox" id="switch-civitaiResourcesEnabled">
<span class="slider round"></span>
</label>
</div>
<div id="civitaiBox" class="collapseBox">
<div class="text-title" i18n="sw.informationPanel.civitaiResourcesDefault"> 圖片預設狀態 </div>
<div class="text-content">
<select class="text-input" id="select-civitaiResourcesDefault">
<option value="true" i18n="menu.expand"> 展開 </option>
<option value="false" i18n="menu.collapse"> 折疊 </option>
</select>
</div>

<div class="text-title" i18n="sw.informationPanel.civitaiResourcesImgNumber"> 圖片數量 </div>
<div class="text-content">
<select class="text-input" id="select-civitaiResourcesImgNumber">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>

<div class="text-title" i18n="sw.informationPanel.civitaiResourcesNsfwLevel"> 允許 NSFW 圖片 </div>
<div class="text-content">
<label class="switch">
<input type="checkbox" id="switch-civitaiResourcesNsfwLevel">
<span class="slider round"></span>
</label>
</div>

</div>

</div>


Expand Down
22 changes: 21 additions & 1 deletion Www/lang/langData.js
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,28 @@ var langData = {
"zh-TW": `自動找出相同檔名的檔案。<br> 例如 "dog.jpg", "dog.txt", "dog.preview.png"`,
"en": `Automatically find files with the same file name. <br> For example, "dog.jpg", "dog.txt", "dog.preview.png"`,
"ja": `同じファイル名のファイルを自動的に見つけます。<br> 例えば、"dog.jpg", "dog.txt", "dog.preview.png"`
}
},

civitaiResourcesEnabled: {
"zh-TW": `顯示 Civitai Resources`,
"en": `Display Civitai Resources`,
"ja": `Civitai Resources を表示する`
},
civitaiResourcesDefault : {
"zh-TW": `圖片預設狀態`,
"en": `Default image state`,
"ja": `画像のデフォルト状態`,
},
civitaiResourcesImgNumber : {
"zh-TW": `圖片數量`,
"en": `Number of images`,
"ja": `画像の数`,
},
civitaiResourcesNsfwLevel : {
"zh-TW": `允許 NSFW 圖片`,
"en": `Allow NSFW images`,
"ja": `NSFW 画像を許可する`,
},
},

// 工具列
Expand Down
Loading

0 comments on commit 78c8a72

Please sign in to comment.