Compare commits

...

2 Commits

Author SHA1 Message Date
limil
c5fc9ac223 添加整理 2026-01-11 23:25:16 +08:00
limil
3d1ebec4db 不显示空白文件夹 2026-01-11 21:46:37 +08:00
8 changed files with 221 additions and 35 deletions

View File

@ -103,6 +103,7 @@ public partial class MainTreePanel : Tree
index += 1; index += 1;
} }
} }
} }
private void OnMultiSelected(TreeItem item, long column, bool selected) private void OnMultiSelected(TreeItem item, long column, bool selected)
@ -189,6 +190,7 @@ public partial class MainTreePanel : Tree
_selectingNodes.Clear(); _selectingNodes.Clear();
TreeItem root = CreateItem(); TreeItem root = CreateItem();
_mapper[root] = new TreeNode(null);
BuildTree(root, path); BuildTree(root, path);
} }
@ -197,35 +199,39 @@ public partial class MainTreePanel : Tree
return _mapper.TryGetValue(item, out node); return _mapper.TryGetValue(item, out node);
} }
private void BuildTree(TreeItem root, string path) private bool BuildTree(TreeItem current, string path)
{ {
var nodes = new Stack<(TreeItem node, string path)>(); var isEmpty = true;
_mapper[root] = new TreeNode(null); var node = _mapper[current];
nodes.Push((root, path));
while (nodes.Count > 0)
{
var node = nodes.Pop();
var father = node.node;
var fatherNode = _mapper[father];
foreach (var filePath in Directory.GetFiles(node.path)) foreach (var filePath in Directory.GetFiles(path))
{ {
isEmpty = false;
var item = InitItem(filePath, false); var item = InitItem(filePath, false);
var childNode = new TreeNode(item); var childNode = new TreeNode(item);
fatherNode.AddNode(childNode); node.AddNode(childNode);
var child = CreateNode(father, item); var child = CreateNode(current, item);
_mapper[child] = childNode; _mapper[child] = childNode;
} }
foreach (var subDirPath in Directory.GetDirectories(node.path)) foreach (var subDirPath in Directory.GetDirectories(path))
{ {
var item = InitItem(subDirPath, true); var item = InitItem(subDirPath, true);
var childNode = new TreeNode(item); var childNode = new TreeNode(item);
fatherNode.AddNode(childNode); node.AddNode(childNode);
var child = CreateNode(father, item); var child = CreateNode(current, item);
_mapper[child] = childNode; _mapper[child] = childNode;
nodes.Push((child, subDirPath)); if (BuildTree(child, subDirPath))
{
_mapper.Remove(child);
child.Free();
}
else
{
isEmpty = false;
} }
} }
return isEmpty;
} }
} }

125
FileMover.cs Normal file
View File

@ -0,0 +1,125 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Learn.Models;
using Learn.Parsers;
namespace Learn;
public class FileMover(string targetPath)
{
private readonly HashSet<TreeNode> _visited = new ();
private string NumToString(int n)
{
if (n < 10) return "0" + n;
return n.ToString();
}
private bool TryGetPath(TreeNode node, out string path)
{
path = null;
if (!node.TryGetValue(ItemFields.Key_Title, out var title, out _)) return false;
if (!node.TryGetValue(ItemFields.Key_Season, out var season, out _)) return false;
if (!node.TryGetValue(ItemFields.Key_Year, out var year, out _)) return false;
path = $"{title} ({year})/";
if (int.TryParse(season, out var seasonNum))
{
path += $"Season {seasonNum}";
}
else
{
path += season;
}
return true;
}
private void MoveFile(TreeNode node)
{
var path = node.Info.MainInfo[ItemFields.MainKey_Path];
if (!node.TryGetValue(ItemFields.Key_Type, out var type, out _)) return;
if (!node.TryGetValue(ItemFields.Key_Title, out var title, out _)) return;
if (!node.TryGetValue(ItemFields.Key_Episode, out var episode, out _)) return;
if (!node.TryGetValue(ItemFields.Key_Season, out var season, out _)) return;
if (!node.TryGetValue(ItemFields.Key_Year, out var year, out _)) return;
if (!TryGetPath(node, out var relativePath)) return;
var filenameParts = new List<string>();
var metaParts = new List<string>();
filenameParts.Add(title);
filenameParts.Add($"({year})");
var seasonAndEpisodePart = "";
if (int.TryParse(season, out var seasonNum))
{
seasonAndEpisodePart += $"S{NumToString(seasonNum)}";
}
var episodeNum = int.Parse(episode);
seasonAndEpisodePart += $"E{NumToString(episodeNum)}";
filenameParts.Add(seasonAndEpisodePart);
if (node.TryGetValue(ItemFields.Key_Group, out var group, out _))
{
metaParts.Add(group);
}
if (type == ItemFields.ItemType.Subtitle.ToString())
{
if (node.TryGetValue(ItemFields.Key_SubtitleLanguage, out var subLang, out _))
{
metaParts.Add(subLang);
}
}
var filename = string.Join(" ", string.Join(" ", filenameParts), string.Join("", metaParts.Select(part => $"[{part}]")));
filename += Path.GetExtension(path);
var filePath = string.Join("/", targetPath, relativePath, filename);
node.Info.Info["TargetPath"] = filePath;
}
private void MoveExtra(TreeNode node)
{
if (!TryGetPath(node, out var relativePath)) return;
var path = node.Info.MainInfo[ItemFields.MainKey_Path];
var name = Path.GetFileName(path);
var target = string.Join("/", targetPath, relativePath, name);
node.Info.Info["TargetPath"] = target;
}
public void DoMove(TreeNode node)
{
if (_visited.Contains(node)) return;
string type;
if (!node.TryGetValue(ItemFields.Key_Type, out type, out _))
{
type = ItemFields.ItemType.Unknown.ToString();
}
if (type == ItemFields.ItemType.Extra.ToString())
{
MoveExtra(node);
_visited.Add(node);
return;
}
if (node.Info.IsFolder())
{
foreach (var child in node.Children)
{
DoMove(child);
}
}
else
{
MoveFile(node);
}
_visited.Add(node);
}
}

1
FileMover.cs.uid Normal file
View File

@ -0,0 +1 @@
uid://cbo6d0l8ax518

43
Main.cs
View File

@ -5,8 +5,10 @@ using System.IO;
using Godot; using Godot;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Learn;
using Learn.Component; using Learn.Component;
using Learn.Config; using Learn.Config;
using Learn.Models;
using Learn.Parsers; using Learn.Parsers;
using Learn.Utils; using Learn.Utils;
@ -19,6 +21,7 @@ public partial class Main : Node
[Export] private Button _openDirButton; [Export] private Button _openDirButton;
[Export] private Button _doParseButton; [Export] private Button _doParseButton;
[Export] private Button _doTMDBParseButton; [Export] private Button _doTMDBParseButton;
[Export] private Button _doMoveButton;
[Export] private Button _saveButton; [Export] private Button _saveButton;
[Export] private Button _resetButton; [Export] private Button _resetButton;
[Export] private Button _loadButton; [Export] private Button _loadButton;
@ -59,9 +62,18 @@ public partial class Main : Node
_mainTreePanel.AddColumn(_columnText.Text, _mainTreePanel.AddColumn(_columnText.Text,
int.TryParse(_columnIndexText.Text, out var index) ? index : -1, int.TryParse(_columnIndexText.Text, out var index) ? index : -1,
int.TryParse(_columnWidthText.Text, out var width) ? width : -1); int.TryParse(_columnWidthText.Text, out var width) ? width : -1);
DoSave();
};
_removeColumnButton.Pressed += () =>
{
_mainTreePanel.RemoveColumn(_columnText.Text);
DoSave();
};
_clearColumnButton.Pressed += () =>
{
_mainTreePanel.ClearColumns();
DoSave();
}; };
_removeColumnButton.Pressed += () => _mainTreePanel.RemoveColumn(_columnText.Text);
_clearColumnButton.Pressed += () => _mainTreePanel.ClearColumns();
_expandAllButton.Pressed += ExpandAll; _expandAllButton.Pressed += ExpandAll;
_foldAllButton.Pressed += FoldAll; _foldAllButton.Pressed += FoldAll;
@ -71,6 +83,7 @@ public partial class Main : Node
_doParseButton.Pressed += DoParse; _doParseButton.Pressed += DoParse;
_doTMDBParseButton.Pressed += DoTMDBParse; _doTMDBParseButton.Pressed += DoTMDBParse;
_saveButton.Pressed += DoSave; _saveButton.Pressed += DoSave;
_doMoveButton.Pressed += DoMove;
_resetButton.Pressed += DoReset; _resetButton.Pressed += DoReset;
_loadButton.Pressed += LoadData; _loadButton.Pressed += LoadData;
_reloadConfigButton.Pressed += ReloadConfig; _reloadConfigButton.Pressed += ReloadConfig;
@ -158,9 +171,16 @@ public partial class Main : Node
foreach (var node in _mainTreePanel.SelectingNodes) foreach (var node in _mainTreePanel.SelectingNodes)
{
try
{ {
await parser.Parse(node); await parser.Parse(node);
} }
catch (Exception ex)
{
GD.Print(ex);
}
}
_doTMDBParseButton.Disabled = false; _doTMDBParseButton.Disabled = false;
_mainTreePanel.UpdateColumns(); _mainTreePanel.UpdateColumns();
@ -177,9 +197,16 @@ public partial class Main : Node
ItemParser parser = new RawParser(_configs); ItemParser parser = new RawParser(_configs);
if (_mainTreePanel.Query(root, out var node)) if (_mainTreePanel.Query(root, out var node))
{
try
{ {
await parser.Parse(node); await parser.Parse(node);
} }
catch (Exception ex)
{
GD.Print(ex);
}
}
_doParseButton.Disabled = false; _doParseButton.Disabled = false;
@ -189,6 +216,18 @@ public partial class Main : Node
private const string DataPath = "data.json"; private const string DataPath = "data.json";
private void DoMove()
{
var mover = new FileMover("");
foreach (var node in _mainTreePanel.SelectingNodes)
{
mover.DoMove(node);
}
_mainTreePanel.UpdateColumns();
}
private void DoSave() private void DoSave()
{ {
_mainTreePanel.SaveData(DataPath); _mainTreePanel.SaveData(DataPath);

View File

@ -17,12 +17,13 @@ grow_vertical = 2
[node name="FileDirDialog" type="FileDialog" parent="."] [node name="FileDirDialog" type="FileDialog" parent="."]
script = ExtResource("1_d2g23") script = ExtResource("1_d2g23")
[node name="Main" type="Node" parent="." node_paths=PackedStringArray("_dirSelector", "_openDirButton", "_doParseButton", "_doTMDBParseButton", "_saveButton", "_resetButton", "_loadButton", "_reloadConfigButton", "_nodeInfoEditPanel", "_addKeyButton", "_removeKeyButton", "_inspectorPanel", "_mainTreePanel", "_columnIndexText", "_columnWidthText", "_columnText", "_addColumnButton", "_removeColumnButton", "_clearColumnButton", "_expandAllButton", "_foldAllButton")] [node name="Main" type="Node" parent="." node_paths=PackedStringArray("_dirSelector", "_openDirButton", "_doParseButton", "_doTMDBParseButton", "_doMoveButton", "_saveButton", "_resetButton", "_loadButton", "_reloadConfigButton", "_nodeInfoEditPanel", "_addKeyButton", "_removeKeyButton", "_inspectorPanel", "_mainTreePanel", "_columnIndexText", "_columnWidthText", "_columnText", "_addColumnButton", "_removeColumnButton", "_clearColumnButton", "_expandAllButton", "_foldAllButton")]
script = ExtResource("2_0727o") script = ExtResource("2_0727o")
_dirSelector = NodePath("../FileDirDialog") _dirSelector = NodePath("../FileDirDialog")
_openDirButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/ScanDir") _openDirButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/ScanDir")
_doParseButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/DoParse") _doParseButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/DoParse")
_doTMDBParseButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/DoTMDBParse") _doTMDBParseButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/DoTMDBParse")
_doMoveButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作/DoMove")
_saveButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Save") _saveButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Save")
_resetButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Reset") _resetButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Reset")
_loadButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Load") _loadButton = NodePath("../MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/保存操作/Load")
@ -108,6 +109,11 @@ layout_mode = 2
size_flags_vertical = 4 size_flags_vertical = 4
text = "搜刮选中部分" text = "搜刮选中部分"
[node name="DoMove" type="Button" parent="MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer/文件夹操作"]
layout_mode = 2
text = "整理选中部分
"
[node name="保存操作" type="VBoxContainer" parent="MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer"] [node name="保存操作" type="VBoxContainer" parent="MarginContainer/HSplitContainer/ScrollContainer/VBoxContainer2/FoldableContainer4/VBoxContainer2/TabContainer"]
visible = false visible = false
layout_mode = 2 layout_mode = 2

View File

@ -47,6 +47,9 @@ public static class ItemFields
public static string Key_TMDBID => "TMDBID"; public static string Key_TMDBID => "TMDBID";
public static string Key_Offset => "EpisodeOffset";
public static ItemType Type(this Item item) public static ItemType Type(this Item item)
{ {
if (item.Info.TryGetValue(Key_Type, out var typeName)) if (item.Info.TryGetValue(Key_Type, out var typeName))

View File

@ -86,12 +86,20 @@ public class TMDBParser(Configs configs) : ItemParser
return; return;
} }
var showInfo = await _client.GetTvShowAsync(result.Id, language: "zh-CN");
if (result.FirstAirDate != null) if (result.FirstAirDate != null)
{ {
node.Info.Info[ItemFields.Key_Year] = result.FirstAirDate.Value.Year.ToString(); node.Info.Info[ItemFields.Key_Year] = showInfo.FirstAirDate.Value.Year.ToString();
} }
node.Info.Info[ItemFields.Key_Title] = result.Name; node.Info.Info[ItemFields.Key_Title] = showInfo.Name;
node.Info.Info[ItemFields.Key_TMDBID] = result.Id.ToString(); node.Info.Info[ItemFields.Key_TMDBID] = showInfo.Id.ToString();
node.Info.Info["SeasonInfo"] = string.Join(", ", showInfo.Seasons.Select(season => season.Name));
if (showInfo.Seasons.Count == 1)
{
node.Info.Info.TryAdd(ItemFields.Key_Season, "1");
}
} }
} }

View File

@ -1,10 +1,8 @@
## TODO ## TODO
- [ ] 添加匹配Season功能 - [x] 添加匹配Season功能
- [ ] 添加剧集偏移功能 - [x] 添加剧集偏移功能
- [ ] 解析操作添加异常保护 - [x] 解析操作添加异常保护
- [ ] 实现Popout提示功能
- [ ] 添加检查是否完全解析完成 - [ ] 添加检查是否完全解析完成
- 添加一个字段,如果是文件夹,就是文件夹下剩余未完全解析的数量;如果是文件,就是文件是否已经完全解析 - 添加一个字段,如果是文件夹,就是文件夹下剩余未完全解析的数量;如果是文件,就是文件是否已经完全解析
- 完全解析是指已经包含Title、Episode、Season、TMDBID的剧集 - 完全解析是指已经包含Title、Episode、Season、TMDBID的剧集
- [ ] 添加转移选中的项目,转移完成添加一个标记。有这个标记的被隐藏起来