mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-08 06:16:37 +08:00
Unify organize ingest pipeline
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
package service
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/config"
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
)
|
||||
|
||||
// TestOrganizeNaming locks in the rename pipeline used by OrganizeDirectory:
|
||||
// CleanQuery (title/year) + ParseEpisode (season/episode) + titleCaseWords.
|
||||
@@ -24,6 +33,7 @@ func TestOrganizeNaming(t *testing.T) {
|
||||
{"Friends.S03E12.1994.720p.mkv", "Friends", 1994, 3, 12},
|
||||
{"Oppenheimer.2023.2160p.UHD.BluRay.mkv", "Oppenheimer", 2023, 0, 0},
|
||||
{"Rocky.IV.1985.1080p.BluRay.mkv", "Rocky IV", 1985, 0, 0},
|
||||
{"Big.Buck.Bunny.2008.1080p.CodexVerify.mp4", "Big Buck Bunny", 2008, 0, 0},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.file, func(t *testing.T) {
|
||||
@@ -59,3 +69,165 @@ func TestTitleCaseWordsRomanNumerals(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrganizeDirectoryHonorsConfiguredNamingFormats(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
dest := filepath.Join(root, "media")
|
||||
repos := newOrganizerTestRepo(t)
|
||||
for key, value := range map[string]string{
|
||||
"organize.movie_format": "Movies/{title} ({year})/{title} [{year}]",
|
||||
"organize.tv_format": "Series/{title} ({year})/S{season:02}/{title}.S{season:02}E{episode:02}",
|
||||
"organize.anime_format": "Bangumi/{title}/Season {season:02}/{title} - {episode:03}",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
organizer := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
|
||||
movieSrc := filepath.Join(root, "downloads", "Dune.2021.2160p.mkv")
|
||||
writeOrgFile(t, movieSrc, "movie")
|
||||
if _, err := organizer.OrganizeDirectory(t.Context(), OrganizeOptions{
|
||||
SourcePath: movieSrc,
|
||||
DestPath: dest,
|
||||
MediaType: "movie",
|
||||
TransferMode: TransferCopy,
|
||||
}); err != nil {
|
||||
t.Fatalf("organize movie: %v", err)
|
||||
}
|
||||
movieWant := filepath.Join(dest, "电影", "Movies", "Dune (2021)", "Dune [2021].mkv")
|
||||
if _, err := os.Stat(movieWant); err != nil {
|
||||
t.Fatalf("movie naming format not honored, want %q: %v", movieWant, err)
|
||||
}
|
||||
|
||||
tvSrc := filepath.Join(root, "downloads", "Some.Show.S02E03.2024.mkv")
|
||||
writeOrgFile(t, tvSrc, "tv")
|
||||
if _, err := organizer.OrganizeDirectory(t.Context(), OrganizeOptions{
|
||||
SourcePath: tvSrc,
|
||||
DestPath: dest,
|
||||
MediaType: "tv",
|
||||
TransferMode: TransferCopy,
|
||||
}); err != nil {
|
||||
t.Fatalf("organize tv: %v", err)
|
||||
}
|
||||
tvWant := filepath.Join(dest, "电视剧", "Series", "Some Show (2024)", "S02", "Some Show.S02E03.mkv")
|
||||
if _, err := os.Stat(tvWant); err != nil {
|
||||
t.Fatalf("tv naming format not honored, want %q: %v", tvWant, err)
|
||||
}
|
||||
|
||||
animeSrc := filepath.Join(root, "downloads", "Frieren.S01E01.2023.mkv")
|
||||
writeOrgFile(t, animeSrc, "anime")
|
||||
if _, err := organizer.OrganizeDirectory(t.Context(), OrganizeOptions{
|
||||
SourcePath: animeSrc,
|
||||
DestPath: dest,
|
||||
MediaType: "anime",
|
||||
MediaCategory: "日番",
|
||||
TransferMode: TransferCopy,
|
||||
}); err != nil {
|
||||
t.Fatalf("organize anime: %v", err)
|
||||
}
|
||||
animeWant := filepath.Join(dest, "电视剧", "日番", "Bangumi", "Frieren", "Season 01", "Frieren - 001.mkv")
|
||||
if _, err := os.Stat(animeWant); err != nil {
|
||||
t.Fatalf("anime naming format not honored, want %q: %v", animeWant, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrganizeDirectoryHonorsMoviePilotStyleNamingFormat(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
dest := filepath.Join(root, "media")
|
||||
repos := newOrganizerTestRepo(t)
|
||||
template := "{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}}"
|
||||
if err := repos.Setting.Set(t.Context(), "organize.tv_format", template); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
organizer := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
|
||||
src := filepath.Join(root, "downloads", "Verify.Show.S01E02.2026.mkv")
|
||||
writeOrgFile(t, src, "episode")
|
||||
if _, err := organizer.OrganizeDirectory(t.Context(), OrganizeOptions{
|
||||
SourcePath: src,
|
||||
DestPath: dest,
|
||||
MediaType: "tv",
|
||||
TransferMode: TransferCopy,
|
||||
}); err != nil {
|
||||
t.Fatalf("organize tv: %v", err)
|
||||
}
|
||||
|
||||
want := filepath.Join(dest, "电视剧", "Verify Show (2026)", "Season 1", "Verify Show - S01E02 - 第 2 集.mkv")
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Fatalf("MoviePilot-style naming format not honored, want %q: %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrganizePipelineUsesSameNamingFormat(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "Pipeline.Show.S01E02.2026.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
writeOrgFile(t, src, "episode")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.Setting.Set(t.Context(), "organize.tv_format", "{title}/S{season:02}/{title} - EP{episode:02}"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
organizer := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
pipeline := NewOrganizePipelineService(zap.NewNop(), repos, organizer, nil, nil)
|
||||
|
||||
res, err := pipeline.Run(t.Context(), OrganizePipelineRequest{
|
||||
Scope: OrganizeScopeDirectory,
|
||||
Trigger: OrganizeTriggerManual,
|
||||
SourcePath: src,
|
||||
DestPath: dest,
|
||||
TransferMode: string(TransferCopy),
|
||||
MediaType: "tv",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("pipeline organize: %v", err)
|
||||
}
|
||||
if res.Result == nil || res.Result.Organized != 1 {
|
||||
t.Fatalf("pipeline result = %#v, want organized=1", res)
|
||||
}
|
||||
want := filepath.Join(dest, "电视剧", "Pipeline Show", "S01", "Pipeline Show - EP02.mkv")
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Fatalf("pipeline did not use configured naming format, want %q: %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrganizeMediaHonorsConfiguredNamingFormat(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "incoming")
|
||||
source := filepath.Join(sourceDir, "Some Show S01E02.mkv")
|
||||
writeOrgFile(t, source, "episode")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.Setting.Set(t.Context(), "organize.tv_format", "{title}/Season {season:02}/{title} - 第{episode:02}集"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lib := model.Library{Name: "TV", Path: root, Type: "tv", Enabled: true}
|
||||
if err := repos.Library.Create(t.Context(), &lib); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
media := model.Media{
|
||||
LibraryID: lib.ID,
|
||||
Title: "Some Show",
|
||||
Path: source,
|
||||
Container: "mkv",
|
||||
SeasonNum: 1,
|
||||
EpisodeNum: 2,
|
||||
}
|
||||
if err := repos.Media.Upsert(t.Context(), &media); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
organizer := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
dst, err := organizer.OrganizeMedia(t.Context(), media.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := filepath.Join(root, "Some Show", "Season 01", "Some Show - 第02集.mkv")
|
||||
if dst != want {
|
||||
t.Fatalf("dst = %q, want %q", dst, want)
|
||||
}
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Fatalf("organized file missing: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user