diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a0999..dc6221a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## Unreleased +- **Sharper App Icon**: The icon shipped at a single small size, so Windows stretched it for shortcuts, folder views and notifications — it looked fuzzy nearly everywhere. It now includes every size Windows asks for, up to 256px + ## v1.6.0 - **Progress Bar No Longer Sticks at 50%**: Downloads that come as a single file — an mp3, or a video served directly by the developer — used to leave the bar stuck at half forever, even though the download had finished. Fixed - **Accurate Progress Bar**: The bar now tracks how many bytes are actually left, instead of giving the video and the audio half each. A 1080p download's audio is a small part of the total, so the bar no longer crawls and then jumps diff --git a/CHANGELOG_ro.md b/CHANGELOG_ro.md index 17e5233..04d4dc3 100644 --- a/CHANGELOG_ro.md +++ b/CHANGELOG_ro.md @@ -1,5 +1,8 @@ # Istoric versiuni +## Unreleased +- **Pictogramă mai clară**: Pictograma era livrată într-o singură dimensiune mică, așa că Windows o întindea pentru scurtături, foldere și notificări — arăta neclară aproape peste tot. Acum include toate dimensiunile cerute de Windows, până la 256px + ## v1.6.0 - **Bara nu mai rămâne blocată la 50%**: Descărcările care vin ca un singur fișier — un mp3 sau un video servit direct de dezvoltator — lăsau bara blocată la jumătate pentru totdeauna, chiar dacă descărcarea se terminase. Rezolvat - **Bară de progres corectă**: Bara urmărește acum câți octeți au rămas de fapt, în loc să dea câte o jumătate imaginii și sunetului. La o descărcare 1080p sunetul este o parte mică din total, așa că bara nu mai avansează greu și apoi sare brusc diff --git a/app/frontend/assets/icon4.icns b/app/frontend/assets/icon4.icns new file mode 100644 index 0000000..fe135cf Binary files /dev/null and b/app/frontend/assets/icon4.icns differ diff --git a/app/frontend/assets/icon4.ico b/app/frontend/assets/icon4.ico index e6692e3..9a99977 100644 Binary files a/app/frontend/assets/icon4.ico and b/app/frontend/assets/icon4.ico differ diff --git a/scripts/build_icon.py b/scripts/build_icon.py new file mode 100755 index 0000000..83b523d --- /dev/null +++ b/scripts/build_icon.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Regenerate app/frontend/assets/icon4.ico from the .icns master. + +The shipped .ico used to contain **a single 32x32 image**, so Windows scaled it +up everywhere it wanted something bigger - shortcuts and toast notifications ask +for 256, Explorer for 48 - and the icon looked soft in every one of those +places. The .icns beside it holds real renders up to 1024, so the fix is simply +to emit the sizes Windows actually asks for. + +Native .icns entries are preferred at each size; only 16, 24 and 48 have no +native entry and are resampled from the 1024 bitmap. + + venv/bin/python scripts/build_icon.py +""" + +import os +import sys + +from PIL import Image, IcnsImagePlugin + +ASSETS = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "app", "frontend", "assets") +ICNS = os.path.join(ASSETS, "icon4.icns") +ICO = os.path.join(ASSETS, "icon4.ico") + +# 16 tray/menus, 24+32 taskbar and title bar, 48 Explorer, 64/128 large icons, +# 256 shortcuts and notification toasts. +SIZES = [16, 24, 32, 48, 64, 128, 256] + + +def main(): + if not os.path.isfile(ICNS): + sys.exit(f"missing master icon: {ICNS}") + + with open(ICNS, "rb") as f: + icns = IcnsImagePlugin.IcnsFile(f) + native = {} + for key in icns.itersizes(): + img = icns.getimage(key).convert("RGBA") + native.setdefault(img.size[0], img) + if not native: + sys.exit("no images found in the .icns") + + biggest = native[max(native)] + frames, notes = [], [] + for size in SIZES: + if size in native: + frames.append(native[size]) + notes.append(f"{size}=native") + else: + frames.append(biggest.resize((size, size), Image.LANCZOS)) + notes.append(f"{size}=scaled") + + frames[-1].save(ICO, format="ICO", sizes=[(s, s) for s in SIZES]) + print(" ".join(notes)) + + written = sorted(Image.open(ICO).ico.sizes()) + print(f"wrote {ICO}: {written}") + assert written == sorted((s, s) for s in SIZES), written + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 3e729e0..02dcae9 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -98,3 +98,38 @@ def test_diaspora_exclusion(): } url, _ = find_video_url("https://youtube.com/c/Test", "10.05.2025") assert url == "https://www.youtube.com/watch?v=vid2" + + +# --------------------------------------------------------------------------- +# The app icon +# +# The shipped .ico once held a single 32x32 image, so Windows upscaled it for +# shortcuts (256), Explorer (48) and toast notifications (256) - it looked soft +# everywhere. Regenerate with scripts/build_icon.py. +# --------------------------------------------------------------------------- + +def test_app_icon_has_the_sizes_windows_asks_for(): + import os + from PIL import Image + + root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + ico = os.path.join(root, "app", "frontend", "assets", "icon4.ico") + sizes = {w for w, h in Image.open(ico).ico.sizes()} + + for needed in (16, 32, 48, 256): + assert needed in sizes, ( + f"icon4.ico has no {needed}px image (has {sorted(sizes)}); " + "run scripts/build_icon.py") + + +def test_app_icon_master_is_kept_alongside(): + """Without the .icns there is nothing to regenerate the .ico from, which is + how it ended up 32px-only in the first place.""" + import os + from PIL import Image + + root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + icns = os.path.join(root, "app", "frontend", "assets", "icon4.icns") + assert os.path.isfile(icns), "the high-resolution master must stay in the repo" + im = Image.open(icns) + assert max(w for w, h, s in im.info["sizes"]) >= 256 diff --git a/youtubeweekly.spec b/youtubeweekly.spec index c135a29..f24f7bd 100644 --- a/youtubeweekly.spec +++ b/youtubeweekly.spec @@ -4,6 +4,13 @@ PyInstaller spec file for YoutubeWeekly. Build with: pyinstaller youtubeweekly.spec """ import os +import sys + +# The .ico carries every size Windows asks for (16-256) and the .icns is the +# high-resolution master both are generated from - see scripts/build_icon.py. +# macOS wants the .icns natively rather than a converted .ico. +APP_ICON = os.path.join("app", "frontend", "assets", + "icon4.icns" if sys.platform == "darwin" else "icon4.ico") block_cipher = None @@ -58,7 +65,7 @@ exe = EXE( strip=False, upx=True, console=False, - icon="app/frontend/assets/icon4.ico", + icon=APP_ICON, ) coll = COLLECT(