Skip to content

Commit b0e1b27

Browse files
bot0419jim60105
authored andcommitted
refactor: follow DRY principle in YtdlpService.cs
- Extract BuildYtdlpArgs method to centralize yt-dlp argument building - Add ArgsToCommandString helper method for shell command string conversion - Refactor cookies workaround to reuse BuildYtdlpArgs and eliminate duplication - Add XML documentation comments for new methods Signed-off-by: Jim Chen <Jim@ChenJ.im>
1 parent 7c98490 commit b0e1b27

1 file changed

Lines changed: 83 additions & 67 deletions

File tree

SingletonServices/Downloader/YtdlpService.cs

Lines changed: 83 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,36 @@ public Task CreateJobAsync(Video video,
2424
string fileName = NameHelper.GetFileName(video, Name);
2525
video.Filename = fileName;
2626
string[]? command = null;
27-
string[] args;
27+
string[] args = BuildYtdlpArgs(fileName, url, liveFromStart);
2828

29+
if (useCookiesFile)
30+
//args = ["--cookies", $"/cookies/{video.ChannelId}.txt", .. args];
31+
// Remove this workaround if issue resolved.
32+
// https://github.com/yt-dlp/yt-dlp/issues/5977#issuecomment-2121742572
33+
(command, args) = BuildCookiesHackCommand(fileName, url, liveFromStart, video.ChannelId, mountPath);
34+
35+
return jobService.CreateInstanceAsync(deploymentName: instanceName,
36+
containerName: instanceName,
37+
imageName: "yt-dlp",
38+
fileName: fileName,
39+
command: command,
40+
args: args,
41+
mountPath: mountPath,
42+
cancellation: cancellation);
43+
}
44+
45+
/// <summary>
46+
/// Builds yt-dlp command line arguments.
47+
/// </summary>
48+
private string[] BuildYtdlpArgs(string fileName, string url, bool liveFromStart)
49+
{
2950
// Live-from-start mode uses minimal args to avoid compatibility issues
3051
// Working example: yt-dlp --live-from-start --no-part -o filename url
3152
if (liveFromStart)
3253
{
33-
args =
54+
return
3455
[
56+
"-v",
3557
"--live-from-start",
3658
"--embed-thumbnail",
3759
"--embed-metadata",
@@ -40,76 +62,70 @@ public Task CreateJobAsync(Video video,
4062
url
4163
];
4264
}
43-
else
65+
66+
var args = new List<string>
4467
{
45-
args =
46-
[
47-
"--ignore-config",
48-
"--retries", "30",
49-
"--concurrent-fragments", "16",
50-
"--merge-output-format", "mp4",
51-
"-S", "+proto:http,+codec:h264",
52-
"--embed-thumbnail",
53-
"--embed-metadata",
54-
"--no-part",
55-
"-o", fileName,
56-
url
57-
];
68+
"-v",
69+
"--ignore-config",
70+
"--retries", "30",
71+
"--concurrent-fragments", "16",
72+
"--merge-output-format", "mp4",
73+
"-S", "+proto:http,+codec:h264",
74+
"--embed-thumbnail",
75+
"--embed-metadata",
76+
"--no-part",
77+
"-o", fileName,
78+
url
79+
};
5880

59-
// Workaround for twitcasting ERROR:
60-
// Initialization fragment found after media fragments, unable to download
61-
// https://github.com/yt-dlp/yt-dlp/issues/5497
62-
if (url.Contains("twitcasting.tv")) args = ["--downloader", "ffmpeg", .. args];
81+
// Workaround for twitcasting ERROR:
82+
// Initialization fragment found after media fragments, unable to download
83+
// https://github.com/yt-dlp/yt-dlp/issues/5497
84+
if (url.Contains("twitcasting.tv"))
85+
{
86+
args.InsertRange(0, ["--downloader", "ffmpeg"]);
6387
}
6488

65-
if (useCookiesFile)
66-
//args = ["--cookies", $"/cookies/{video.ChannelId}.txt", .. args];
67-
// Remove this workaround if issue resolved.
68-
// https://github.com/yt-dlp/yt-dlp/issues/5977#issuecomment-2121742572
69-
(command, args) = copyCookiesHack();
89+
return [.. args];
90+
}
7091

71-
return jobService.CreateInstanceAsync(deploymentName: instanceName,
72-
containerName: instanceName,
73-
imageName: "yt-dlp",
74-
fileName: fileName,
75-
command: command,
76-
args: args,
77-
mountPath: mountPath,
78-
cancellation: cancellation);
92+
/// <summary>
93+
/// Converts arguments array to shell command string with proper quoting.
94+
/// </summary>
95+
private static string ArgsToCommandString(string[] args)
96+
{
97+
return string.Join(" ", args.Select(arg =>
98+
arg.Contains(' ') || arg.Contains('\'') ? $"'{arg.Replace("'", "'\\''")}'" : arg));
99+
}
79100

80-
(string[] command, string[] args) copyCookiesHack()
81-
{
82-
// Cookies file has to be mounted elsewhere and then cp.
83-
// Because yt-dlp does not support using cookies file in Read-only file system.
84-
// Which it is how K8s handle secrets.
85-
86-
// original ENTRYPOINT: "dumb-init", "--", "yt-dlp", "--no-cache-dir"
87-
88-
command = ["dumb-init", "--", "sh", "-c"];
89-
90-
// cp under mountPath to make sure the permission is writable
91-
// Live-from-start mode uses minimal args to avoid compatibility issues
92-
if (liveFromStart)
93-
{
94-
args =
95-
[
96-
$"cp -r /cookies {mountPath}/cookies && yt-dlp --live-from-start --no-part --cookies {mountPath}/cookies/{video.ChannelId}.txt -o '{fileName}' '{url}'"
97-
];
98-
}
99-
else
100-
{
101-
args =
102-
[
103-
$"cp -r /cookies {mountPath}/cookies && yt-dlp --ignore-config --retries 30 --concurrent-fragments 16 --merge-output-format mp4 -S '+proto:http,+codec:h264' --embed-thumbnail --embed-metadata --no-part --cookies {mountPath}/cookies/{video.ChannelId}.txt -o '{fileName}' '{url}'"
104-
];
105-
106-
// Workaround for twitcasting ERROR:
107-
// Initialization fragment found after media fragments, unable to download
108-
// https://github.com/yt-dlp/yt-dlp/issues/5497
109-
if (url.Contains("twitcasting.tv")) args[0] = args[0].Replace("--ignore-config", "--ignore-config --downloader ffmpeg");
110-
}
111-
112-
return (command, args);
113-
}
101+
/// <summary>
102+
/// Builds command for cookies workaround.
103+
/// Cookies file has to be mounted elsewhere and then copied,
104+
/// because yt-dlp does not support using cookies file in Read-only file system,
105+
/// which is how K8s handles secrets.
106+
/// </summary>
107+
private (string[] command, string[] args) BuildCookiesHackCommand(
108+
string fileName,
109+
string url,
110+
bool liveFromStart,
111+
string channelId,
112+
string mountPath)
113+
{
114+
// original ENTRYPOINT: "dumb-init", "--", "yt-dlp", "--no-cache-dir"
115+
var command = new[] { "dumb-init", "--", "sh", "-c" };
116+
117+
// Build yt-dlp args and convert to command string
118+
var ytdlpArgs = BuildYtdlpArgs(fileName, url, liveFromStart);
119+
var ytdlpCommand = ArgsToCommandString(ytdlpArgs);
120+
121+
// Insert cookies parameter
122+
var cookiesPath = $"{mountPath}/cookies/{channelId}.txt";
123+
ytdlpCommand = ytdlpCommand.Replace("-o ", $"--cookies {cookiesPath} -o ");
124+
125+
// cp under mountPath to make sure the permission is writable
126+
var shellScript = $"cp -r /cookies {mountPath}/cookies && yt-dlp {ytdlpCommand}";
127+
var args = new[] { shellScript };
128+
129+
return (command, args);
114130
}
115131
}

0 commit comments

Comments
 (0)