ファイル名の上限を拡大し、動画の配信・削除を os.Root に置き換え(エピソード ID は 64 バイト上限)

This commit is contained in:
2026-10-04 13:43:07 +09:00
parent f963dea2d1
commit 9b856d63fa
7 changed files with 345 additions and 115 deletions
+39 -4
View File
@@ -228,6 +228,15 @@ func runningJob() store.Job {
return store.Job{ID: 7, EpisodeID: "ep123", SeriesTitle: "番組名", Title: "第1話", Status: "running"}
}
func relativeVideoPath(t *testing.T, seriesTitle, episodeTitle, episodeID string) string {
t.Helper()
path, err := media.RelativeVideoPath(seriesTitle, episodeTitle, episodeID)
if err != nil {
t.Fatal(err)
}
return path
}
func TestProcessJobDownloadsProbesMovesAndCleans(t *testing.T) {
t.Setenv("FAKE_YTDLP_MODE", "success")
t.Setenv("FAKE_FFPROBE_MODE", "valid")
@@ -236,7 +245,7 @@ func TestProcessJobDownloadsProbesMovesAndCleans(t *testing.T) {
t.Setenv("FAKE_ARGS_FILE", argsFile)
jobs := &fakeStore{job: runningJob()}
testRunner(t, jobs, dir).ProcessJob(context.Background(), jobs.job)
want := filepath.ToSlash(media.RelativeVideoPath("番組名", "第1話", "ep123"))
want := filepath.ToSlash(relativeVideoPath(t, "番組名", "第1話", "ep123"))
if !jobs.completed || jobs.completePath != want || jobs.completeSize == 0 {
t.Fatalf("complete: done=%v path=%q size=%d", jobs.completed, jobs.completePath, jobs.completeSize)
}
@@ -273,6 +282,32 @@ func TestProcessJobDownloadsProbesMovesAndCleans(t *testing.T) {
}
}
func TestProcessJobRejectsLongEpisodeIDAndCleans(t *testing.T) {
dir := t.TempDir()
argsFile := filepath.Join(dir, "yt-dlp-args")
t.Setenv("FAKE_ARGS_FILE", argsFile)
workDir := filepath.Join(dir, ".incoming", "7")
if err := os.MkdirAll(workDir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(workDir, "stale"), []byte("stale"), 0o644); err != nil {
t.Fatal(err)
}
job := runningJob()
job.EpisodeID = strings.Repeat("i", 65)
jobs := &fakeStore{job: job}
testRunner(t, jobs, dir).ProcessJob(context.Background(), job)
if jobs.completed || jobs.failedKind != "invalid_output" || !strings.Contains(jobs.failedMessage, "64バイト以下") {
t.Fatalf("長いIDの失敗: kind=%q message=%q completed=%v", jobs.failedKind, jobs.failedMessage, jobs.completed)
}
if _, err := os.Stat(argsFile); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("yt-dlp が起動された: %v", err)
}
if _, err := os.Stat(workDir); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("作業ディレクトリが残った: %v", err)
}
}
func TestProcessJobKeepsPercentNamesOutOfYTDLPTemplate(t *testing.T) {
t.Setenv("FAKE_YTDLP_MODE", "success")
t.Setenv("FAKE_FFPROBE_MODE", "valid")
@@ -294,7 +329,7 @@ func TestProcessJobKeepsPercentNamesOutOfYTDLPTemplate(t *testing.T) {
if strings.Contains(string(args), job.SeriesTitle) || strings.Contains(string(args), job.Title) {
t.Fatalf("番組情報が yt-dlp 引数に入った: %s", args)
}
want := filepath.ToSlash(media.RelativeVideoPath(job.SeriesTitle, job.Title, job.EpisodeID))
want := filepath.ToSlash(relativeVideoPath(t, job.SeriesTitle, job.Title, job.EpisodeID))
if jobs.completePath != want {
t.Fatalf("保存先=%q want=%q", jobs.completePath, want)
}
@@ -305,7 +340,7 @@ func TestProcessJobReplacesExistingDestination(t *testing.T) {
t.Setenv("FAKE_FFPROBE_MODE", "valid")
dir := t.TempDir()
job := runningJob()
relative := media.RelativeVideoPath(job.SeriesTitle, job.Title, job.EpisodeID)
relative := relativeVideoPath(t, job.SeriesTitle, job.Title, job.EpisodeID)
destination := filepath.Join(dir, relative)
if err := os.MkdirAll(filepath.Dir(destination), 0o755); err != nil {
t.Fatal(err)
@@ -409,7 +444,7 @@ func TestProcessJobDestinationDirectoryFailureCleansIncoming(t *testing.T) {
t.Setenv("FAKE_FFPROBE_MODE", "valid")
dir := t.TempDir()
job := runningJob()
seriesDir := filepath.Join(dir, filepath.Dir(media.RelativeVideoPath(job.SeriesTitle, job.Title, job.EpisodeID)))
seriesDir := filepath.Join(dir, filepath.Dir(relativeVideoPath(t, job.SeriesTitle, job.Title, job.EpisodeID)))
if err := os.WriteFile(seriesDir, []byte("blocks directory"), 0o644); err != nil {
t.Fatal(err)
}