fix: normalize List.aspx?folderID= to fragment form for yt-dlp
Canvas stores folder links as List.aspx?folderID=X (query param). yt-dlp's PanoptoList extractor requires List.aspx#folderID="X" (fragment with quoted ID) to scope the download to that folder. Without the fragment form it downloaded the entire Panopto instance (1806 items instead of 3). Also drop --no-playlist for list URLs since they are intentional playlists, and use title/%(title)s.%(ext)s output template for them.
This commit is contained in:
@@ -17,6 +17,27 @@ import (
|
|||||||
"git.directme.in/Joren/CanvasArchiver/internal/utils"
|
"git.directme.in/Joren/CanvasArchiver/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// normalizePanoptoURL converts the query-param form that Canvas stores
|
||||||
|
// (List.aspx?folderID=X) to the fragment form that yt-dlp's PanoptoList
|
||||||
|
// extractor understands (List.aspx#folderID="X"). Without this yt-dlp
|
||||||
|
// ignores the folder filter and downloads the entire Panopto instance.
|
||||||
|
func normalizePanoptoURL(rawURL string) string {
|
||||||
|
parsed, err := url.Parse(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return rawURL
|
||||||
|
}
|
||||||
|
if strings.Contains(parsed.Path, "List.aspx") {
|
||||||
|
folderID := parsed.Query().Get("folderID")
|
||||||
|
if folderID != "" {
|
||||||
|
// Strip query, set fragment: List.aspx#folderID="<id>"
|
||||||
|
parsed.RawQuery = ""
|
||||||
|
parsed.Fragment = fmt.Sprintf(`folderID="%s"`, folderID)
|
||||||
|
return parsed.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rawURL
|
||||||
|
}
|
||||||
|
|
||||||
func getYoutubeDLCommand() string {
|
func getYoutubeDLCommand() string {
|
||||||
exePath, err := os.Executable()
|
exePath, err := os.Executable()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -192,13 +213,35 @@ func DownloadVideo(httpClient *http.Client, accessToken, courseID, modDir, input
|
|||||||
fmt.Printf(" [*] Downloading video: %s\n", title)
|
fmt.Printf(" [*] Downloading video: %s\n", title)
|
||||||
|
|
||||||
ytCmd := getYoutubeDLCommand()
|
ytCmd := getYoutubeDLCommand()
|
||||||
cmd := exec.Command(ytCmd,
|
|
||||||
|
// Normalize folder URLs so yt-dlp scopes to the right folder.
|
||||||
|
normalizedURL := normalizePanoptoURL(targetURL)
|
||||||
|
|
||||||
|
// Folder/list URLs are intentional playlists; don't pass --no-playlist.
|
||||||
|
isList := strings.Contains(normalizedURL, "List.aspx")
|
||||||
|
var outputTpl string
|
||||||
|
var args []string
|
||||||
|
if isList {
|
||||||
|
outputTpl = utils.Sanitize(title) + "/%(title)s.%(ext)s"
|
||||||
|
args = []string{
|
||||||
|
"--cookies", cookieFile,
|
||||||
|
"--referer", config.BaseURL + "/",
|
||||||
|
"-P", modDir,
|
||||||
|
"-o", outputTpl,
|
||||||
|
normalizedURL,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outputTpl = utils.Sanitize(title) + ".%(ext)s"
|
||||||
|
args = []string{
|
||||||
"--no-playlist",
|
"--no-playlist",
|
||||||
"--cookies", cookieFile,
|
"--cookies", cookieFile,
|
||||||
"--referer", config.BaseURL+"/",
|
"--referer", config.BaseURL + "/",
|
||||||
"-P", modDir,
|
"-P", modDir,
|
||||||
"-o", utils.Sanitize(title)+".%(ext)s",
|
"-o", outputTpl,
|
||||||
targetURL)
|
normalizedURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd := exec.Command(ytCmd, args...)
|
||||||
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|||||||
Reference in New Issue
Block a user