Files
douyin/storage/StorageMigrationPathPolicy.cs

156 lines
7.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.RegularExpressions;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.utils;
namespace dy.net.storage
{
public sealed class StorageMigrationPathPlan
{
public string VideoPath { get; set; }
public string CoverPath { get; set; }
public string AvatarPath { get; set; }
public string DirectoryPath => StoragePath.DirectoryName(VideoPath);
}
public static class StorageMigrationPathPolicy
{
private static readonly Regex EpisodeName = new("^S(?<season>\\d{2})E(?<episode>\\d{2,})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static StorageMigrationPathPlan Build(
DouyinVideo video,
DouyinCookie cookie,
DouyinCollectCate category,
DouyinFollowed followed,
AppConfig config,
int episodeNumber = 1)
{
if (video == null) throw new ArgumentNullException(nameof(video));
if (cookie == null) throw new InvalidOperationException($"视频 {video.AwemeId} 缺少所属账号");
var root = GetRemoteRoot(cookie, video.ViedoType);
if (string.IsNullOrWhiteSpace(root)) throw new InvalidOperationException($"账号 {cookie.UserName} 未配置 {video.ViedoType} 的远端目标路径");
var extension = NormalizeExtension(Path.GetExtension(video.VideoSavePath));
var awemeId = string.IsNullOrWhiteSpace(video.AwemeId) ? video.Id : video.AwemeId;
var titleFolder = DouyinFileNameHelper.SanitizeLinuxFileName(video.VideoTitle, awemeId, true);
var authorFolder = DouyinFileNameHelper.SanitizeLinuxFileName(video.Author, video.AuthorId ?? "未知博主", true);
string directory;
string fileName;
switch (video.ViedoType)
{
case VideoTypeEnum.dy_custom_collect:
var customCategory = DouyinFileNameHelper.SanitizeLinuxFileName(category?.SaveFolder, category?.Name ?? "未分类", true);
directory = StoragePath.CombineRemote(root, customCategory, titleFolder + "_" + awemeId);
fileName = awemeId + extension;
break;
case VideoTypeEnum.dy_mix:
case VideoTypeEnum.dy_series:
var seriesFolder = DouyinFileNameHelper.SanitizeLinuxFileName(category?.SaveFolder, category?.Name ?? video.CateXId ?? "未分类", true);
directory = StoragePath.CombineRemote(root, seriesFolder);
var oldEpisode = EpisodeName.Match(Path.GetFileNameWithoutExtension(video.VideoSavePath) ?? string.Empty);
fileName = oldEpisode.Success
? oldEpisode.Value.ToUpperInvariant() + extension
: $"S01E{Math.Max(1, episodeNumber):D2}{extension}";
break;
case VideoTypeEnum.dy_follows:
var followedFolder = string.IsNullOrWhiteSpace(followed?.SavePath) ? authorFolder : followed.SavePath;
directory = StoragePath.CombineRemote(root, followedFolder, titleFolder);
fileName = BuildFollowedFileName(video, config, extension, awemeId);
break;
case VideoTypeEnum.dy_favorite:
case VideoTypeEnum.dy_collects:
default:
directory = StoragePath.CombineRemote(root, authorFolder, titleFolder + "_" + awemeId);
fileName = awemeId + extension;
break;
}
var videoPath = StoragePath.CombineRemote(directory, fileName);
var coverName = video.ViedoType is VideoTypeEnum.dy_mix or VideoTypeEnum.dy_series
? "poster.jpg"
: Path.GetFileNameWithoutExtension(fileName) + "-poster.jpg";
return new StorageMigrationPathPlan
{
VideoPath = videoPath,
CoverPath = StoragePath.CombineRemote(directory, coverName),
AvatarPath = string.IsNullOrWhiteSpace(video.AuthorAvatar) && string.IsNullOrWhiteSpace(video.AuthorAvatarUrl)
? string.Empty
: StoragePath.CombineRemote(root, "author", (string.IsNullOrWhiteSpace(video.AuthorId) ? awemeId : video.AuthorId) + ".jpg")
};
}
public static StorageMigrationPathPlan Build(
Aweme item,
VideoTypeEnum type,
DouyinCookie cookie,
DouyinCollectCate category,
DouyinFollowed followed,
AppConfig config)
{
if (item == null) throw new ArgumentNullException(nameof(item));
var bitrate = item.Video?.BitRate?.FirstOrDefault();
var format = string.IsNullOrWhiteSpace(bitrate?.Format) ? "mp4" : bitrate.Format.TrimStart('.');
var createdAt = DateTimeUtil.Convert10BitTimestamp(item.CreateTime);
var title = string.IsNullOrWhiteSpace(item.Desc)
? $"{item.Author?.Nickname}-{item.CreateTime}"
: item.Desc;
var video = new DouyinVideo
{
ViedoType = type,
AwemeId = item.AwemeId,
VideoTitle = title,
VideoSavePath = $"{item.AwemeId}.{format}",
Author = item.Author?.Nickname,
AuthorId = item.Author?.Uid,
AuthorAvatarUrl = item.Author?.AvatarLarger?.UrlList?.FirstOrDefault()
?? item.Author?.AvatarThumb?.UrlList?.FirstOrDefault(),
CreateTime = createdAt,
FileHash = bitrate?.PlayAddr?.FileHash,
Resolution = bitrate?.PlayAddr == null ? string.Empty : $"{bitrate.PlayAddr.Width}×{bitrate.PlayAddr.Height}"
};
var episode = item.MixInfo?.Statis?.CurrentEpisode ?? 1;
return Build(video, cookie, category, followed, config, Math.Max(1, episode));
}
public static string GetRemoteRoot(DouyinCookie cookie, VideoTypeEnum type) => type switch
{
VideoTypeEnum.dy_favorite => cookie.WebDavFavoritePath,
VideoTypeEnum.dy_follows => cookie.WebDavFollowPath,
VideoTypeEnum.dy_mix => cookie.WebDavMixPath,
VideoTypeEnum.dy_series => cookie.WebDavSeriesPath,
_ => cookie.WebDavCollectPath
};
public static IReadOnlyList<string> GetLocalRoots(DouyinCookie cookie)
{
if (cookie == null) return Array.Empty<string>();
return new[] { cookie.SavePath, cookie.FavSavePath, cookie.UpSavePath, cookie.MixPath, cookie.SeriesPath }
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(Path.GetFullPath)
.Distinct(StringComparer.Ordinal)
.ToArray();
}
private static string BuildFollowedFileName(DouyinVideo video, AppConfig config, string extension, string awemeId)
{
if (string.IsNullOrWhiteSpace(config?.FullFollowedTitleTemplate)) return awemeId + extension;
var generated = VideoTitleGenerator.Generate(config.FullFollowedTitleTemplate, new VideoTitleDataTemplate
{
Id = awemeId,
ReleaseTime = video.CreateTime,
VideoTitle = video.VideoTitle,
Author = video.Author,
FileHash = video.FileHash,
Resolution = video.Resolution
});
generated = DouyinFileNameHelper.SanitizeLinuxFileName(generated, awemeId);
return generated + extension;
}
private static string NormalizeExtension(string extension) => string.IsNullOrWhiteSpace(extension) ? ".mp4" : extension.ToLowerInvariant();
}
}