Skip to content

Fix orphan "-(p)" suffix in filenames for media without resolution metadata - #812

Open
independent-arg wants to merge 11 commits into
jely2002:mainfrom
independent-arg:fix-orphan-characters
Open

Fix orphan "-(p)" suffix in filenames for media without resolution metadata#812
independent-arg wants to merge 11 commits into
jely2002:mainfrom
independent-arg:fix-orphan-characters

Conversation

@independent-arg

@independent-arg independent-arg commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

#797

This PR fixes the long-standing bug where filenames for audio‑only content (e.g., SoundCloud) would incorrectly end with -(p) or -(k).

Example: Música sin Copyright de Fondo-(p).mp4 → now Música sin Copyright de Fondo.mp4

The root cause was the default video template:
%(title).200s-(%(height)sp%(fps).0d).%(ext)s
When height and fps are absent, they become empty strings, leaving a stray p literal.


My first instinct was to nest %(fps) inside the conditional replacement for %(height&...)s. Before suggesting it to you, I tested it against actual yt-dlp, and it breaks the parser:

%(title).200s%(height&-(%(height)sp%(fps).0d)|)s.%(ext)s
→ KeyError: 'height&-(%\x00(height\x00s)sp%(fps\x00.0d)s)|'

Solution
Replace the templates with conditional blocks using yt-dlp's & operator and auto‑reference {}:

Video:
%(title).200s%(height&-{:.0f}p|)s%(fps&-{:.0f}fps|)s.%(ext)s

Audio:
%(title).200s%(abr&-{:.0f}k|)s.%(ext)s

This only renders the resolution/bitrate part when the field exists, removing orphan characters.

Note: Check out the visual change: it goes from -(1080p30) to -1080p-30fps. There’s honestly no way to keep the exact style with parentheses and combine two fields into a single conditional block, it’s a hard limitation of yt-dlp’s template engine, not something we can bypass in Rust. If you really want about keeping the brackets instead of dashes, I also tried -[{:.0f}p][{:.0f}fps] and it works just as fine, it’s purely aesthetic, so your call.


Changes

  • config_models.rs - updated default templates.
  • config.rs - added auto‑migration for users with old default templates (custom templates are not overwritten).
  • template_context.rs - extended the preview engine to handle {} and format specifiers like {:.0f}.
  • PreferencesFilename.vue - updated built‑in presets to use the new syntax.
  • src/tauri/types/config.ts - updated templates.

Testing

Scenario Before After
SoundCloud (video+audio) ...-(p).mp4 ....mp4
YouTube 1080p 24fps ...-(1080p24).mp4 ...-1080p-24fps.mp4

I also found the same latent bug pattern in the audio template (%(abr)dk-(k) when abr is absent), even though it doesn't currently trigger in practice, abr is virtually always present for real audio streams, which matches what I reported ("audio mode works flawlessly"). Fixed proactively with the same approach for consistency.

  • I tested this with the SoundCloud clip I downloaded, first doing a regular video + audio download like a default user would, and I got the right filename. Then I tried audio-only mode, and it gave me the correct filename with the audio quality. I also tested a random YouTube video, and the filename came out perfect with the video quality.
  • Users who have never changed the default templates will be automatically migrated to the new ones.
  • Users with custom templates retain their settings - no forced override.
  • Known limitation: if an extractor reports height/fps as literal 0
    instead of omitting the field, yt-dlp's & operator treats 0 as present,
    producing -0p-0fps. Still strictly better than the original bug, but not
    perfect, there's no >0 comparison available inside -o templates.
  • Verified against the actual yt-dlp binary (not just reasoning about the
    syntax) with edge cases: very long titles, missing title, special
    characters, and --restrict-filenames, all resolve cleanly.

@independent-arg

independent-arg commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

With this proposed solution, we can definitively solve these orphan character issues once and for all, at least covering all possible use cases.

We'll also need to update the following all locale files to keep them in sync with these changes:
example outputs (e.g., -1080p-30fps instead of -(1080p30)).

Back then, {} self-references for its OWN fields (playlist_title, playlist_index, etc.) weren't supported, even though yt-dlp does support them for its own. If someone built a custom template mixing a playlist field with that syntax, it would just break silently. So, the addition of BRACE_RE is important.

Please let me know if there are any other files related to the templates that need to be changed to this new syntax.

@independent-arg

Copy link
Copy Markdown
Contributor Author

@jely2002 when you have time, it would be interesting to know your thoughts on this, thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant