Dumping very bad idea of updating this post. New place for this script:
https://gist.github.com/Wogol/66e9936b6d49cc5fecca59eaeca1ca2e
Im trying to create a .command macOS script (Should also work under GNU/Linux) that use Youtube-dl that is simple to use. I have fixed so it downloads description, thumbnail, subtitles, json, creates folder structure and also saves video ID, uploader and upload date.
ISSUES WITH THE MAIN SCRIPT:
FIXED (13th august) Problem I struggle with is the option of Audio & Video or Audio only. For some reason only audio works in the script. The download script for the video&audio dont work but if I paste that same command line ("The DEBUG output line") in a terminal window it works. Scratching my head.
Youtube-dl gives me this message:
ERROR: requested format not available
FIXED (31th august) Get max resolution of video working. Have found information to force mp4 or max resolution but not combined them.
ISSUES WITH INFORMATION FILE:
Also creating a info file with title, channel name, release date, description. Im now struggling with getting video information from .json and youtube-dl to be exported into the info.txt file.
FIXED (5th september) textfile=""$folder"info.txt" not working. Gives this error: (There I want to add youtube-dl folder.
ytdl.command: line 104: ~/Downloads/ytdl/dog_vids/info.txt: No such file or directory
FIXED (5th september) Find youtube-dl folder and get it to work with grep.
Something like this:
youtube-dl --simulate --SHOW_THE_OUTPUT_PATH -o $folder'/%(title)s/%(title)s - (%(id)s) - %(uploader)s - %(upload_date)s.%(ext)s' https://www.youtube.com/watch?v=dQw4w9WgXcQ
FIXED (5th september) With grep command i named the json file "*.json" because there will only be one per directory but I dont like that solution. (Could be answered with point above)
FIXED (5th september) How to make so grep dont grab "? It now adds them before and after everything.
FIXED (5th september) How to get the tags information from json file? Tags look like this:
"tags": ["music", "video", "classic"]
FIXED (5th september) Run the creation of info file part of the script in the background of downloading the video?
CURRENT VERSION TRYING TO GET IT WORKING
(12 august)
textfile=""$folder"info.txt"
echo TITLE >> ~/Downloads/ytdl/dog_vids/info.txt
youtube-dl -e $url >> ~/Downloads/ytdl/dog_vids/info.txt
echo \ >> ~/Downloads/ytdl/dog_vids/info.txt
echo CHANNEL >> $textfile
echo \ >> $textfile
echo CHANNEL URL >> $textfile
echo \ >> $textfile
echo UPLOAD DATE >> $textfile
echo \ >> $textfile
echo URL >> $textfile
echo $url >> $textfile
echo \ >> $textfile
echo TAGS >> $textfile
echo \ >> $textfile
echo DESCRIPTION >> $textfile
youtube-dl --get-description $url >> $textfile
EXPERIMENT FUTURE VERSION - EXTRACTING INFORMATION FROM JSON FILE
This isnt a working script. Showing how I want it with $textfile, $ytdlfolder and $jsonfile.
url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
textfile=""$folder""$YOUTUBE-DL_PATH"info.txt"
ytdlfolder="$folder""$YOUTUBE-DL_PATH"
jsonfile="$folder""$YOUTUBE-DL_JSON-FILE"
Echo TITLE >> $textfile
grep -o '"title": *"[^"]*"' $jsonfile | grep -o '"[^"]*"$' >> $textfile
Echo \ >> $textfile
Echo CHANNEL >> $textfile
grep -o '"uploader": *"[^"]*"' $jsonfile | grep -o '"[^"]*"$' >> $textfile
Echo \ >> $textfile
Echo CHANNEL URL >> $textfile
grep -o '"uploader_url": *"[^"]*"' *.json | grep -o '"[^"]*"$' >> $textfile
Echo \ >> $textfile
Echo UPLOAD DATE >> $textfile
grep -o '"upload_date": *"[^"]*"' *.json | grep -o '"[^"]*"$' >> $textfile
Echo \ >> $textfile
Echo TAGS >> $textfile
grep -o '"tags": *"[^"]*"' *.json | grep -o '"[^"]*"$' >> $textfile
Echo \ >> $textfile
echo URL >> $textfile
echo $url >> $textfile
echo \ >> $textfile
Echo DESCRIPTION >> $textfile
youtube-dl --get-description $url >> $textfile
THE SCRIPT:
12 august.
Moved url to the top so when user paste the url they get the videos title back. This so the user know they got the right video.
Added max resolution 1920x1080. (Do not work)
13 august.
Downloading Audio & Video works.
31 august.
Fixed force mp4 and max heigh of 1080.
5 september.
Finally working script. Read more about it here (Or scroll down):
Youtube-dl download script debug
2020-09-17
Folders can now have spaces in them.
2020-09-22
Select menus is now one column.
Minor fixes.
Now all the bugs is fixed. Issues left is only optimizations.
#! /bin/bash
################################################################################
# Script Name: Youtube-dl Easy Download Script
# Description: Easy to use script to download YouTube videos with a couple of
# options.
#
# What this script do:
# - Downloads video in MP4 with highest quality and max resolution 1920x1080.
# - Downloads thumbnail and subtitles.
# - Gives user option where to download the video and video or only audio.
# - Creates a folder with same name as video title and puts all files there.
# - Creates a .txt file with information about the video.
#
#
# Author: Wogol - Stackoverflow.com, Github.com
# License: The GNU General Public License v3.0 - GNU GPL-3
#
#
# Big thanks to the people at youtube-dl GitHub and Stack Overflow. Without
# their help this would never ever been possible for me.
#
# Special thanks to:
# Reino # Stack Overflow
#
# #####
#
# Software required: youtube-dl, xidel, printf
#
# macOS: 1. Install Homebrew: https://brew.sh
# 2. Terminal command: brew install youtube-dl xidel
#
# Linux: Depends on package manager your distribution use.
#
# #####
#
# Version history:
# 2020-09-22
# - Select menus is now one column.
# - Minor fixes.
# - Now all the bugs is fixed. Issues left is only optimizations.
#
# 2020-09-17
# - Folders can now have spaces in them.
#
# 2020-09-05
# - First working version.
#
# #####
#
# Issues left:
# - In the beginning there is a confirmation that show the title of the
# video so user know they got the correct video. It takes youtube-dl a
# couple of seconds. To speed up the script it is DISABLED by default.
#
# - Have found out that the script dont need xidel to get json information
# but youtube-dl can get it. Dont know how to use youtube-dl --dump-json
# to get the same result.
#
# - To get the path to the .txt file script use youtube-dl. This gives the
# script a pause for a few seconds. Best would get to get the path some how
# without connecting to YouTube again but use the output from youtube-dl
# some how. ... or run it in the background when video is downloading.
#
################################################################################
clear
# - WELCOME MESSAGE -
echo
COLUMNS=$(tput cols)
title="-= Youtube-dl Easy Download Script =-"
printf "%*s\n" $(((${#title}+$COLUMNS)/2)) "$title"
# - PASTE URL -
echo -e "\n*** - Paste URL address and hit RETURN. Example:\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ --OR-- https://youtu.be/dQw4w9WgXcQ\n"
read url
# - VIDEO TITLE -
# So users know they have the correct URL.
#echo -e "\nThe video is: (This takes 3-4 seconds, or more ...)"
#youtube-dl -e $url
#echo
# - DOWNLOAD LOCATION -
# DIRECTORY MUST END WITH SLASH: /
echo -e "\n\n*** - Choose download folder:\n"
COLUMNS=0
PS3='Choose: '
select directory in "~/Downloads/ytdl/Rick Astley/" "~/Downloads/ytdl/Never Gonna Give You Up/" "~/Downloads/ytdl/Other Rick Videos/" ; do
echo -e "\nOption $REPLY selected. Download directory is:\n $directory"
# - AUDIO/VIDEO SETTINGS -
echo -e "\n\n*** - Choose download settings:\n"
COLUMNS=0
PS3='Choose: '
options=("Audio & Video" "Audio only")
select settingsopt in "${options[#]}"
do
case $settingsopt in
"Audio & Video")
av="-f bestvideo[ext=mp4][height<=1080]+bestaudio[ext=m4a]/best[ext=mp4]/best --merge-output-format mp4"
;;
"Audio only")
av="-f bestaudio[ext=m4a]/bestaudio"
;;
esac
echo -e "\nOption $REPLY selected:\n $settingsopt"
# - THE DOWNLOAD SCRIPT -
echo -e "\n\n*** - Starting download:\n"
youtube-dl $av --write-thumbnail --all-subs --restrict-filenames -o "$directory%(title)s/%(title)s.%(ext)s" $url
# - INFORMATION FILE -
textfile=$(youtube-dl --get-filename --restrict-filenames -o "$directory%(title)s/%(title)s.txt" $url)
xidel -s "$url" -e '
let $json:=json(
//script/extract(.,"ytplayer.config = (.+?\});",1)[.]
)/args,
$a:=json($json/player_response)/videoDetails,
$b:=json($json/player_response)/microformat
return (
"- TITLE -",
$a/title,"",
"- CHANNEL -",
$a/author,"",
"- CHANNEL URL -",
$b//ownerProfileUrl,"",
"- UPLOAD DATE -",
$b//publishDate,"",
"- URL -",
$json/loaderUrl,"",
"- TAGS -",
$a/keywords,"",
"- DESCRIPTION -",
$a/shortDescription
)
' --printed-json-format=compact >> "$textfile"
# - THE END -
echo
COLUMNS=$(tput cols)
ending="Download Complete!"
printf "%*s\n\n" $(((${#ending}+$COLUMNS)/2)) "$ending"
exit
done
done
Finally got the script working.
Have got a lot of help from many people but big thanks to Reino with his help in this thread:
Grep command questions - Grep text from program output?
The script has issues and it can be optimized but I dont know how to fix them. This is the first bash script I have created.
The goals with this was to create a script that:
Simple and easy to use.
No terminal commands.
Initial sorting in different directories.
Video or only audio.
MP4 with max resolution 1920x1080 because everything supports it out of the box.
Text file with additional information about the video.
These are features I miss in programs like Downie (macOS) and Clipgrab.
For other people to use this script and future fixing I tried to create a Github page... not my cup of tea so to say.
Script is in the first post on this page.
Youtube-dl download script debug
I fixed so Audio & Video download now works. The problem was ' in the av line. Removed them and now it works fine. Also updated the av line from the man/manual for youtube-dl.
Not working:
av="-f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio' --merge-output-format mp4"
Working:
av="-f bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best --merge-output-format mp4"
Have now fixed so the script force mp4 and max height of 1080.
-f bestvideo[ext=mp4][height<=1080]+bestaudio[ext=m4a]/best[ext=mp4]/best --merge-output-format mp4
Now the rest of the issues left.
I'm trying to download an entire Youtube channel and that worked.
But I'm having the directories' names like below and thus I need to change that all manually.
I need a way to pass channel / playlist name and id to the script instead of fetching the url.
Script I used :
# get working/beginning directory
l=$(pwd);
clear;
# get playlists data from channel list
youtube-dl -j --flat-playlist \
"https://www.youtube.com/channel/UC-QDfvrRIDB6F0bIO4I4HkQ/playlists" \
|cut -d ' ' -f4 \
|cut -c 2-73 \
|while IFS= read -r line;
do;
# loop: do with every playlist link
# make a directory named by the playlist identifier in url
mkdir ${line:38:80};
# change directory to new directory
cd $l/${line:38:80};
# download playlist
youtube-dl -f mp4 "$line";
# print playlist absolute dir to user
pwd;
# change directory to beginning directory
cd $l;
done;
Names of directories :
.
├── PLxl69kCRkiI0oIqgQW3gWjDfI-e7ooTUF
├── PLxl69kCRkiI0q0Ib8lm3ZJsG3HltLQDuQ
├── PLxl69kCRkiI1Ebm-yvZyUKnfoi6VVNdQ7
├── ...
└── PLxl69kCRkiI3u-k02uTpu7z4wzYLOE3sq
This is not working :
https://github.com/ytdl-org/youtube-dl/issues/23442
# any playlist is seen as private
youtube-dl -J \
https://m.youtube.com/playlist?list=PL3GeP3YLZn5jOiHM8Js1_S0p_5HeS7TbY \
| jq -r '.title'
How to use youtube-dl from a python program?
I need it for bash script not for python
Edit: simply explained
How to get channel name from bash youtube-dl and replace it with list id for file name in this script
Consider the following:
#!/usr/bin/env bash
# if we don't delete slashes from titles there are serious security issues here
slash=/
# note that this url, being in quotes, needs no backslash escaping.
url='https://www.youtube.com/playlist?list=PLXmMXHVSvS-CoYS177-UvMAQYRfL3fBtX'
# First, get the name for the whole playlist
playlist_title=$(youtube-dl -J --flat-playlist "$url" | jq -r .title) || exit
# ...and, for the rest of the script, work under a directory named by that title
mkdir -p -- "${playlist_title//$slash/}" || exit
cd "${playlist_title//$slash/}" || exit
# Finally, loop over the individual videos and download them one at a time.
# ...arguably, you could tell youtube-dl to do this itself; call it an exercise.
youtube-dl -j --flat-playlist "$url" | # one JSON document per playlist entry
jq -r '[.id, .title] | #tsv' | # write id, then title, tab-separated
while IFS=$'\t' read -r id title; do ( # read that id and title from jq
# because of the ()s, this is a subshell; exits just go to the next item
# ...which is also why exec doesn't end the entire script.
dir=${title//$slash/} # take slashes out of the title to form directory
mkdir -p -- "$dir" || exit
cd -- "$dir" || exit # if cd fails, do not download anything
exec youtube-dl "$id" # exec is a minor perf optimization; consume subshell
); done
Note:
We're using jq to convert the JSON to a more safely-readable format that we can parse without byte offsets.
The extra backslashes were removed from the URL to prevent the 404 error described in the comments to the question.
Putting the body of the loop in a subshell with parenthesis means that the cd inside that subshell is automatically reversed when the parenthesized section is exited.
We don't trust titles not to contain slashes -- you don't need someone naming their video /etc/sudoers.d/hi-there or otherwise placing files in arbitrarily-chosen places.
Note the use of "$dir" instead of just $dir. That's important; see shellcheck warning SC2086.
There is this solved topic about opening .html files via command line.
I use the solution and it works well for using
open ./myfile.html
However, it opens the file always in a new tab. I would like to open it always in the same tab (using the browser target). This is an easy thing to do in JavaScript, but I can't figure out a way to do it in combination with the above mentioned code.
My assumption for now is, that there must be a way to pass the target as a parameter to the open command. The man open reveals the following for the parameter --args:
All remaining arguments are passed to the opened application in the
argv parameter to main(). These arguments are not opened or
interpreted by the open tool.
So I tried the following:
open ./myfile.html --args target=myfile_target # still opens in new tab
open ./myfile.html --args target="myfile_target" # still opens in new tab
open ./myfile.html --args target:myfile_target # still opens in new tab
I am not sure if this even works but I think there must be a way to do this.
Edit: for now it is enough to make this work with chrome.
This Bash script incorporates a bit of AppleScript in order to open a browser window with a reference that the script can keep track of and continue to target with ongoing URL requests.
You ought to be able to copy-n-paste it into a text editor and save it as whatever you wish to call this replacement open function. I saved it as url, in one of the directories listed in my $PATH variable. That way, I could simply type url dropbox.com from the command-line, and it would run.
You will have to make it executable before you can do that. So, after it's saved, run this command:
chmod +x /path/to/file
Then you should be good to go. Let me know if you encounter any errors, and I'll fix them.
#!/bin/bash
#
# Usage: url %file% | %url%
#
# %file%: relative or absolute POSIX path to a local .html file
# %url%: [http[s]://]domain[/path/etc...]
IFS=''
# Determine whether argument is file or web address
[[ -f "$1" ]] && \
_URL=file://$( cd "$( dirname "$1" )"; pwd )/$( basename "$1" ) || \
{ [[ $1 == http* ]] && _URL=$1 || _URL=http://$1; };
# Read the value on the last line of this script
_W=$( tail -n 1 "$0" )
# Open a Safari window and store its AppleScript object id reference
_W=$( osascript \
-e "use S : app \"safari\"" \
-e "try" \
-e " set S's window id $_W's document's url to \"$_URL\"" \
-e " return $_W" \
-e "on error" \
-e " S's (make new document with properties {url:\"$_URL\"})" \
-e " return id of S's front window" \
-e "end try" )
_THIS=$( sed \$d "$0" ) # All but the last line of this script
echo "$_THIS" > "$0" # Overwrite this file
echo -n "$_W" >> "$0" # Appened the object id value as final line
exit
2934
When I'm validating my app I get this error:
the application bundle does not contain an icon in ICNS format, containing both a 512x512 and a 512x512#2x image.
I use to make the icns icons with Img2icns app and until today it always worked properly. But now I'm getting that error and there's no way to make it work. I tried to put two PNG files together (512x512 and 1024x1024) in Img2icns but I always get that error. I also tried to follow the instructions in Apple's OS X Human Interface Guideline but when I try to make the icon sets I get this terminal error:
-bash: syntax error near unexpected token 'newline'
I am not very good with terminal commands so maybe I'm doing something wrong. I wrote:
iconutil -c icns </Users/myname/SDK Mac Apps/MyApp/grafica/icon.iconset>
If anyone could help it would be very much appreciated. Thanks, Massy.
Here's a script to convert a 1024x1024 png (named "Icon1024.png") to the required icns file. Save it to a file called "CreateICNS.src" in the folder where your png file is then in terminal "cd" to the same folder and type "source CreateICNS.src" to call it:
mkdir MyIcon.iconset
sips -z 16 16 Icon1024.png --out MyIcon.iconset/icon_16x16.png
sips -z 32 32 Icon1024.png --out MyIcon.iconset/icon_16x16#2x.png
sips -z 32 32 Icon1024.png --out MyIcon.iconset/icon_32x32.png
sips -z 64 64 Icon1024.png --out MyIcon.iconset/icon_32x32#2x.png
sips -z 128 128 Icon1024.png --out MyIcon.iconset/icon_128x128.png
sips -z 256 256 Icon1024.png --out MyIcon.iconset/icon_128x128#2x.png
sips -z 256 256 Icon1024.png --out MyIcon.iconset/icon_256x256.png
sips -z 512 512 Icon1024.png --out MyIcon.iconset/icon_256x256#2x.png
sips -z 512 512 Icon1024.png --out MyIcon.iconset/icon_512x512.png
cp Icon1024.png MyIcon.iconset/icon_512x512#2x.png
iconutil -c icns MyIcon.iconset
rm -R MyIcon.iconset
Checkout the following instructions (link):
Use iconutil to Create an icns File Manually
The iconutil command-line tool converts iconset folders to deployment-ready, high-resolution icns files. (You can find complete documentation for this tool by entering man iconutil in Terminal.) Using this tool also compresses the resulting icns file, so there is no need for you to perform additional compression.
To convert a set of icons to an icns file
Enter this command into the Terminal window:
iconutil -c icns <iconset filename>
where <iconset filename> is the path to the folder containing the set of icons you want to convert to icns. The output is written to the same location as the iconset file, unless you specify an output file as shown:
iconutil -c icns -o <icon filename> <iconset filename>
In other words, you need to replace <iconset filename> by the path:
/Users/myname/SDK Mac Apps/MyApp/grafica/icon.iconset
Since the path contains spaces, you need to use double quotes, for example:
iconutil -c icns "/Users/myname/SDK Mac Apps/MyApp/grafica/icon.iconset"
This command should work properly.
While using all kinds of scripts to convert hi-res PNG image to a pleiad of different low-resolution copies may seem handy (and it really is), one should not forget that this kind of automatic resizing will render perceptibly imperfect images.
Lesser the resolution — blurrier the icon!
I mean, I love imagemagick too, but it is not the right tool for this task!
Instead, you should always request a logo in some vector format from your designer, for example in SVG. With this on hand, you can manually prepare perfect PNG files in all required resolutions and then make a single .icns file, which will make your app icon look beautiful on every single screen, from a cheap iPhone SE to some high-end Retina display of the latest iMac. You can use Photoshop, GIMP or any other tool of your choice to generate these PNGs.
From the latest Apple's Human Interface Guidelines as of 2020, you should prepare the following PNG files:
+---------------------+--------------------+--------------+
| filename | resolution, pixels | density, PPI |
+---------------------+--------------------+--------------+
| icon_16x16.png | 16x16 | 72 |
| icon_16x16#2x.png | 32x32 | 144 |
| icon_32x32.png | 32x32 | 72 |
| icon_32x32#2x.png | 64x64 | 144 |
| icon_128x128.png | 128x128 | 72 |
| icon_128x128#2x.png | 256x256 | 144 |
| icon_256x256.png | 256x256 | 72 |
| icon_256x256#2x.png | 512x512 | 144 |
| icon_512x512.png | 512x512 | 72 |
| icon_512x512#2x.png | 1024x1024 | 144 |
+---------------------+--------------------+--------------+
After all the PNG files are prepared, place them into some directory with .iconset extension (Logos.iconset for example) and execute the following from the Terminal:
iconutil --convert icns Logos.iconset
If there were no errors after executing this command, then all the files were processed properly, and you got the Logos.icns file in the same directory, containing all the beautiful crisp logos for your app which will suit any modern screen.
There's a command-line node module that does all the work of converting a PNG file into an iconset directory:
npm install -g node-icns
nicns --in adventure-cat.png --out adventure-cat.icns
These commands (entered in Terminal) worked for me to convert an old icns file to the new format:
cd Folder_With_Icns_File
iconutil -c iconset Your_Icon_Name.icns
rm Your_Icon_Name.icns
iconutil -c icns Your_Icon_Name.iconset
rm -R Your_Icon_Name.iconset
Update
The -c parameter to iconutil is no longer supported. Use --convert instead:
cd Folder_With_Icns_File
iconutil --convert iconset Your_Icon_Name.icns
rm Your_Icon_Name.icns
iconutil --convert icns Your_Icon_Name.iconset
rm -R Your_Icon_Name.iconset
Additional comment, when you create .icns file, you need to rename all the pic files with prefix "icon_", otherwise, iconutil will fail with error message: ".iconset:error: Failed to generate ICNS." which is not informative at all.
Same as #Henry (comment above) but takes as argument the PNG filename and outputs the ICNS with the same name.
NOTE : The PNG file name is only expected to have 1 point to separate extension, i.e. xpto.png .
(updated for shell script)
So, save the code below to a filed called "CreateICNS.sh" in the folder where your png file is, and give it execution permisisons.
Code:
#!/bin/bash
IFS='.' read -ra ADDR <<< "$1"
ICONSET=${ADDR[0]}.iconset
mkdir $ICONSET
sips -z 16 16 $1 --out $ICONSET/icon_16x16.png
sips -z 32 32 $1 --out $ICONSET/icon_16x16#2x.png
sips -z 32 32 $1 --out $ICONSET/icon_32x32.png
sips -z 64 64 $1 --out $ICONSET/icon_32x32#2x.png
sips -z 128 128 $1 --out $ICONSET/icon_128x128.png
sips -z 256 256 $1 --out $ICONSET/icon_128x128#2x.png
sips -z 256 256 $1 --out $ICONSET/icon_256x256.png
sips -z 512 512 $1 --out $ICONSET/icon_256x256#2x.png
sips -z 512 512 $1 --out $ICONSET/icon_512x512.png
cp $1 $ICONSET/icon_512x512#2x.png
iconutil -c icns $ICONSET
rm -R $ICONSET
HOW TO USE :
Then in the terminal, "cd" to the same folder and type :
./CreateICNS.sh {PNG filename}
where {PNG filename} is the name of your PNG file, i.e. xpto.png .
If your file would be named abc.png you would use :
./CreateICNS.sh abc.png
UPDATE 2021-05-20 :
I have an updated version of this but I'm not sure where I found it to leave here the correct reference so if someone is the owner of this or knows a link on some internet page, please comment so I can update credits :
This is a complete bash script, so you should save it as for example png2icns.sh and give it execution permissions.
Then you can call png2icns.sh pngfile1.png and it will generate the ICNS file as well as a iconset folder containing all icon resolutions between 16x16 and 512x512 .
#!/bin/bash
# Creates an icns file from a source image
src_image="$1"
if [ -z "$1" ]; then
echo "No source image was passed to this script"
exit 1
fi
icns_name="$2"
if [ -z "$2" ]; then
icns_name="iconbuilder"
fi
if [ "${src_image:(-3)}" != "png" ]; then
echo "Source image is not a PNG, making a converted copy..."
/usr/bin/sips -s format png "$src_image" --out "${src_image}.png"
if [ $? -ne 0 ]; then
echo "The source image could not be converted to PNG format."
exit 1
fi
src_image="${src_image}.png"
fi
iconset_path="./${icns_name}.iconset"
if [ -e "$iconset_path" ]; then
/bin/rm -r "$iconset_path"
if [ $? -ne 0 ]; then
echo "There is a pre-existing file/dir $iconset_path the could not be deleted"
exit 1
fi
fi
/bin/mkdir "$iconset_path"
icon_file_list=(
"icon_16x16.png"
"icon_16x16#2x.png"
"icon_32x32.png"
"icon_32x32#2x.png"
"icon_128x128.png"
"icon_128x128#2x.png"
"icon_256x256.png"
"icon_256x256#2x.png"
"icon_512x512.png"
"icon_512x512#2x.png"
)
icon_size=(
'16'
'32'
'32'
'64'
'128'
'256'
'256'
'512'
'512'
'1024'
)
counter=0
for a in ${icon_file_list[#]}; do
icon="${iconset_path}/${a}"
/bin/cp "$src_image" "$icon"
icon_size=${icon_size[$counter]}
/usr/bin/sips -z $icon_size $icon_size "$icon"
counter=$(($counter + 1))
done
echo "Creating .icns file from $iconset_path"
/usr/bin/iconutil -c icns "$iconset_path"
if [ $? -ne 0 ]; then
echo "There was an error creating the .icns file"
exit 1
fi
echo "Done"
exit 0
I have written a bash script for making icns from a svg file:
#!/usr/bin/env bash
sizes=(16 32 64 128 256 512)
largfile='icon_512x512#2x.png'
if [ ! -f "$largfile" ]; then
convert -background none -resize 1024x1024 "$1" "$largfile"
fi
for s in "${sizes[#]}"; do
echo $s
convert -background none -resize ${s}x${s} "$largfile" "icon_${s}x$s.png"
done
cp 'icon_32x32.png' 'icon_16x16#2x.png'
mv 'icon_64x64.png' 'icon_32x32#2x.png'
cp 'icon_256x256.png' 'icon_128x128#2x.png'
cp 'icon_512x512.png' 'icon_256x256#2x.png'
mkdir icon.iconset
mv icon_*x*.png icon.iconset
iconutil -c icns icon.iconset
Make sure you have imagemagick installed with librsvg support, on mac:
brew install imagemagick --with-librsvg
This script has served me quite well.
Update
For a more thorough treatment, I have created a command line tool (written in Swift) for generating AppIcon.appiconset with the correct layout and format:
https://github.com/kindlychung/genicon
I've refactored #Henry's script to make it look better:
#!/bin/zsh
NAME=$(basename $1 .png); DIR="$NAME.iconset"
mkdir -pv $DIR
for m r in 'n' '' '((n+1))' '#2x'; do
for n in $(seq 4 9 | grep -v 6); do
p=$((2**$m)); q=$((2**$n))
OUT="$DIR/icon_${q}x${q}${r}.png"
sips -z $p $p $1 --out $OUT
done
done
iconutil -c icns $DIR
rm -frv $DIR
Update
The -c parameter to iconutil is no longer supported. Use -—convert instead:
#!/bin/zsh
NAME=$(basename $1 .png); DIR="$NAME.iconset"
mkdir -pv $DIR
for m r in 'n' '' '((n+1))' '#2x'; do
for n in $(seq 4 9 | grep -v 6); do
p=$((2**$m)); q=$((2**$n))
OUT="$DIR/icon_${q}x${q}${r}.png"
sips -z $p $p $1 --out $OUT
done
done
iconutil -—convert icns $DIR
rm -frv $DIR
Dead simple .png 👉 .icns
Download IconMaker.app - It's just an .applescript won't bite
Drag and drop your .png onto the IconMaker.app you just created.
More info:
http://eon.codes/blog/2016/12/06/Creating-an-app-icon/
High sierra update iconutil is now more strict about the source .png size. More about this in the blog post after the jump. ✌️
When I'm validating my app I get this error:
the application bundle does not contain an icon in ICNS format, containing both a 512x512 and a 512x512#2x image.
⋮
I am not very good with terminal command and so maybe I'm doing something wrong. I wrote:
iconutil -c icns </Users/myname/SDK Mac Apps/MyApp/grafica/icon.iconset>
For one thing, as I mentioned in a comment on Anne's answer, you probably don't need to use iconutil. You should be able to just add the iconset to your project and let Xcode convert it for you as part of the build.
Either way, this may be your problem:
I tryed to put two PNG files togheter (512x512 and 1024x1024) … but I always get the error.
There is no 1024 by 1024 point size. The 1024 by 1024 pixel element (which was 1024 points before Mountain Lion) is now used for 512 by 512 points #2x.
Your PNG file must be named appropriately: icon_512x512#2x.png
Apple's older Icon Composer version 2.2 works just fine, you just open the .ICNS in it, press the 1024x1024 button and add your image.
#dardo82's shell code is good & worked.
Here is a simpler one in sh (for all *nix's) and faster (like it really matters):
#!/bin/sh
# This runs silent, be as verbose as you wish
NAME=$(basename ${1} .png)
DIR="${NAME}.iconset"
mkdir -p ${DIR}
for i in 16 32 128 256 512 ; do
x=""
for p in $i $(($i+$i)) ; do
sips -z $p $p ${1} --out "${NAME}.iconset/icon_${i}x${i}${x}.png"
x="#2x"
done
done >/dev/null # /dev/null in lieu of a "-s" silent option
iconutil -—convert icns $DIR
rm -r $DIR
Here's a function mostly based off Henry's example (could be useful in ~/.bash_profile):
mkicns() {
if [[ -z "$*" ]] || [[ "${*##*.}" != "png" ]]; then
echo "Input file invalid"
else
filename="${1%.*}"
mkdir "$filename".iconset
for i in 16 32 128 256 ; do
n=$(( i * 2 ))
sips -z $i $i "$1" --out "$filename".iconset/icon_${i}x${i}.png
sips -z $n $n "$1" --out "$filename".iconset/icon_${i}x${i}#2x.png
[[ $n -eq 512 ]] && \
sips -z $n $n "$1" --out "$filename".iconset/icon_${n}x${n}.png
(( i++ ))
done
cp "$1" "$filename".iconset/icon_512x512#2x.png
iconutil -c icns "$filename".iconset
rm -r "$filename".iconset
fi
}
Usage:
$ mkicns "filename.png" # double-quote if spaces exist in filename
Creates 10 sizes from 16x16 to 512x512#2x; accepts input images in
.png format only.
Run iconutil -c icns Icon.iconset
Note
Icon.iconset is a folder
Name start with lowercase icon_
When you see Icon.icns with correct image, you know it works
There are 2 tasks:
- create 10 correct icns files
- get your Xcode project to use them correctly
As I had hour long problems with both of these tasks, and also do not like when I don't 'see' what is going on, here a path for the cautious ones:
Create 10 correct icns files:
I used the script above from Henry: It still works for HighSierra and Xcode 9.2, including the 'c' command.
The icns file I got, appeared as only one icon size in Finder/Quicklook and in Preview showed only 8 of 10 sizes.
So I used terminal, went with cd to my folder and used the command: iconutil -c iconset (icns filename) on the just created icns file to revert the icns file back to an iconset folder, and - lo & behold - I could see my newly created 10 icon files. Using Quick look on the iconset folder (and using full screen mode to be able to zoom through them with the slider), I could check that all sizes are actually looking very well.
As an aside: they looked better than my resizing attempts with PSE, most likely because I did not take the time to play with all the resizing options in PSE. If you do use PSE, make sure your png files are saved without colour profile. Also, confessing my ignorance, for me a 256x256#2 file is the same as a 512x512 file - both in 72dpi. Trying to follow the 144 dpi comments above did not work for me.
Get your Xcode project to use them correctly:
First I deleted all my fruitless attempts within Xcode and committed a clean version to the git repository (what would have been clever, would have been to commit a clean version first - before I frantically started the icon addition odyssee).
I also made sure that in the info.plist file there is no pointer linked to the 'icon file' entry and that in my General project settings I had chosen AppIcon for App Icons.
Then I added an assets.asset catalog and within the assets catalog a new 'AppIcons and Launch Images' AppIcon Folder for OS.
Then I copied (drag and drop with option pressed) from the iconset folder each png picture file into the respective AppIcon Spaceholder. So again, I could see what is happening. Xcode did convert that into icns files, or maybe - as my iconset folder derived from an icns folder - the file formats were accepted.
Then archive and validate and there will be no errors upon uploading or validating.
I needed this, but for CMake. I also wanted the option of giving it an SVG.
Here is the gist: https://gist.github.com/Qix-/f4090181e55ea365633da8c3d0ab5249
And the CMake code:
# LICENSE: CC0 - go nuts.
# Hi :) This is what I used to generate the ICNS for my game, Tide.
# Both input formats (SVG vs PNG) work just fine, but in my experience
# the SVG came out with noticeably better results (although PNG wasn't
# a catastrophe either). The logo for the game was simple enough that
# SVG was indeed an option.
# To use:
#
# make_icns( INPUT "path/to/img.{svg,png}"
# OUTPUT ICNS_PATH )
#
# Then add it as a custom target or use it as a
# dependency somewhere - I give you that option.
#
# For example:
#
# add_custom_target( my-icns ALL DEPENDS "${ICNS_PATH}" )
#
# For the associated utilities:
#
# - PNG: brew install imagemagick
# - SVG: brew cask install inkscape
#
# Enjoy!
function (make_icns_from_png)
cmake_parse_arguments (
ARG
"" # Boolean args
"INPUT;OUTPUT" # List of single-value args
"" # Multi-valued args
${ARGN})
find_program (
convert_exe
NAMES "convert" "convert.exe"
DOC "Path to ImageMagick convert")
if (NOT convert_exe)
message (FATAL_ERROR "Could not find ImageMagick's 'convert' - is ImageMagick installed?")
endif ()
get_filename_component (ARG_INPUT_ABS "${ARG_INPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
get_filename_component (ARG_INPUT_ABS_BIN "${ARG_INPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
get_filename_component (ARG_INPUT_FN "${ARG_INPUT_ABS_BIN}" NAME_WE)
get_filename_component (ARG_INPUT_DIR "${ARG_INPUT_ABS_BIN}" DIRECTORY)
set (sourceimg "${ARG_INPUT_ABS}")
set (basepath "${ARG_INPUT_DIR}/${ARG_INPUT_FN}")
set (output_icns "${basepath}.icns")
set (iconset "${basepath}.iconset")
set (deplist "")
foreach (size IN ITEMS 16 32 128 256 512)
math (EXPR size2x "2 * ${size}")
set (ipath "${iconset}/icon_${size}x${size}.png")
set (ipath2x "${iconset}/icon_${size}x${size}#2x.png")
list (APPEND deplist "${ipath}" "${ipath2x}")
add_custom_command (
OUTPUT "${ipath}"
COMMAND "${convert_exe}" ARGS "${sourceimg}" -resize "${size}x${size}" "${ipath}"
MAIN_DEPENDENCY "${sourceimg}"
COMMENT "ICNS resize: ${ipath}"
VERBATIM)
add_custom_command (
OUTPUT "${ipath2x}"
COMMAND "${convert_exe}" ARGS "${sourceimg}" -resize "${size2x}x${size2x}" "${ipath2x}"
MAIN_DEPENDENCY "${sourceimg}"
COMMENT "ICNS resize: ${ipath2x}"
VERBATIM)
endforeach ()
add_custom_command (
OUTPUT "${output_icns}"
COMMAND iconutil ARGS --convert icns --output "${output_icns}" "${iconset}"
MAIN_DEPENDENCY "${sourceimg}"
DEPENDS ${deplist}
COMMENT "ICNS: ${output_icns}"
VERBATIM)
if (ARG_OUTPUT)
set ("${ARG_OUTPUT}" "${output_icns}" PARENT_SCOPE)
endif ()
endfunction ()
function (make_icns_from_svg)
cmake_parse_arguments (
ARG
"" # Boolean args
"INPUT;OUTPUT" # List of single-value args
"" # Multi-valued args
${ARGN})
set (CMAKE_FIND_APPBUNDLE NEVER) # otherwise, it'll pick up the app bundle and open a shit ton of windows
find_program (
inkscape_exe
NAMES "inkscape" "inkscape.exe"
DOC "Path to Inkscape"
PATHS "/usr/local/bin" "/usr/bin")
message (STATUS "Inkscape path: ${inkscape_exe}")
if (NOT inkscape_exe)
message (FATAL_ERROR "Could not find Inkscape - is it installed?")
endif ()
get_filename_component (ARG_INPUT_ABS "${ARG_INPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
get_filename_component (ARG_INPUT_ABS_BIN "${ARG_INPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
get_filename_component (ARG_INPUT_FN "${ARG_INPUT_ABS_BIN}" NAME_WE)
get_filename_component (ARG_INPUT_DIR "${ARG_INPUT_ABS_BIN}" DIRECTORY)
set (sourceimg "${ARG_INPUT_ABS}")
set (basepath "${ARG_INPUT_DIR}/${ARG_INPUT_FN}")
set (output_icns "${basepath}.icns")
set (iconset "${basepath}.iconset")
set (deplist "")
foreach (size IN ITEMS 16 32 128 256 512)
math (EXPR size2x "2 * ${size}")
set (ipath "${iconset}/icon_${size}x${size}.png")
set (ipath2x "${iconset}/icon_${size}x${size}#2x.png")
list (APPEND deplist "${ipath}" "${ipath2x}")
add_custom_command (
OUTPUT "${ipath}"
COMMAND "${inkscape_exe}" ARGS -z -e "${ipath}" -w ${size} -h ${size} "${sourceimg}"
MAIN_DEPENDENCY "${sourceimg}"
COMMENT "ICNS resize: ${ipath}"
VERBATIM)
add_custom_command (
OUTPUT "${ipath2x}"
COMMAND "${inkscape_exe}" ARGS -z -e "${ipath2x}" -w ${size2x} -h ${size2x} "${sourceimg}"
MAIN_DEPENDENCY "${sourceimg}"
COMMENT "ICNS resize: ${ipath2x}"
VERBATIM)
endforeach ()
add_custom_command (
OUTPUT "${output_icns}"
COMMAND iconutil ARGS --convert icns --output "${output_icns}" "${iconset}"
MAIN_DEPENDENCY "${sourceimg}"
DEPENDS ${deplist}
COMMENT "ICNS: ${output_icns}"
VERBATIM)
if (ARG_OUTPUT)
set ("${ARG_OUTPUT}" "${output_icns}" PARENT_SCOPE)
endif ()
endfunction ()
function (make_icns)
cmake_parse_arguments (
ARG
"" # Boolean args
"INPUT;OUTPUT" # List of single-value args
"" # Multi-valued args
${ARGN})
if (NOT ARG_INPUT)
message (FATAL_ERROR "INPUT is required")
endif ()
if (NOT IS_ABSOLUTE "${ARG_INPUT}")
get_filename_component (ARG_INPUT "${ARG_INPUT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endif ()
if (NOT EXISTS "${ARG_INPUT}")
message (FATAL_ERROR "INPUT does not exist: ${ARG_INPUT}")
endif ()
file (RELATIVE_PATH ARG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}" "${ARG_INPUT}")
get_filename_component (ARG_INPUT_EXT "${ARG_INPUT}" EXT)
if ("${ARG_INPUT_EXT}" STREQUAL ".png")
make_icns_from_png (INPUT "${ARG_INPUT}" OUTPUT child_output)
elseif ("${ARG_INPUT_EXT}" STREQUAL ".svg")
make_icns_from_svg (INPUT "${ARG_INPUT}" OUTPUT child_output)
else ()
message (FATAL_ERROR "INPUT must refer to a .png or .svg, but a ${ARG_INPUT_EXT} was provided")
endif ()
if (ARG_OUTPUT)
set ("${ARG_OUTPUT}" "${child_output}" PARENT_SCOPE)
endif ()
endfunction ()
Hello, for my needs I made a droplet that works in drag and drop icons alone or icons to search in a folder (I limited to folders as the searches on Volumes of all the icons can take a lot of time ). So in drag and drop you can drop folders or application, anything that can contain an icon. The iconset created bears the name of the original icon, it is placed in a directory "/ aaaicones" and the path of the icon. Example in the folder "/ aaaicones if you submit xcode.app you will find
"/aaaicones/Applications/xcode.app/access.iconset" and /aaaicones/Applications/xcode.app/access.icns (the recreated icon) there will be a text file where it is traced the full path of the icons , And the path to the corresponding iconset example
"/Applications/xcode.app/Contents/Applications/Instruments.app/Contents/Frameworks/InstrumentsPlugIn.framework/Versions/A/Resources/access.icns"
"/aaaicones/Applications/xcode.app/access.iconset" in the example taken (xcode) this can create a folder at the end (with all icons and iconset) of 214 MB in size. If you treat an icon alone, it will be placed in the directory "/ aaaicones / aIconeseule /" and its original path, example
/aaaicones/aIconeseule/Mac Elcapitan/.VolumeIcon.icns and /aaaicones/aIconeseule/Mac Elcapitan /.VolumeIcon.iconset with /aaaicones/aIconeseule/Mac Elcapitan/aalisticones.txt
The droplet is below
on open draggedItems
set input to draggedItems
set fich to draggedItems
set media to {}
set theInfo to {}
set n to "0"
repeat with currentItem in draggedItems
set dirchoisi to POSIX path of fich
if ".icns" is not in dirchoisi then
if "Volumes" is not in dirchoisi then
set origi to do shell script "echo /aaaicones" & dirchoisi
set fich to do shell script "echo " & fich & " | xxd -p -c 100000 | sed 's#3a#2f#g' | xxd -r -p | sed 's#" & dirchoisi & "#" & "/aaaicones" & dirchoisi & "#g' | xxd -p -c 100000 | sed 's#2f#3a#g' | xxd -r -p"
tell application "Finder"
if exists (folder fich) then
set nn to "0"
repeat with nn from 1 to 5
set origi to do shell script "echo " & origi & "/" & " | sed 's#//#" & nn & "/" & "#'"
set fich to do shell script "echo " & fich & " | sed 's#:aaaicones*.*#" & origi & "#'" & " | xxd -p -c 100000 | sed 's#2f#3a#g' | xxd -r -p"
if not (exists folder (fich as Unicode text)) then
try
set origi to do shell script "echo " & origi
exit repeat
end try
end if
end repeat
end if
end tell
tell application "Finder"
if not (exists folder (fich as Unicode text)) then
do shell script "mkdir -p -m 0777 " & quoted form of origi
end if
end tell
try
set theInfo to do shell script "find " & (quoted form of dirchoisi) & " -name *.icns "
end try
set AppleScript's text item delimiters to return
set theList to text items of theInfo
set AppleScript's text item delimiters to ""
set n to count theList
repeat with i from 1 to n
if "Volumes" is not in item i of theList then
set end of media to item i of theList
end if
end repeat
set n to count media
set cheminicns to do shell script " > " & quoted form of (origi & "aalisticones.txt") & " | chmod 777 " & quoted form of (origi & "aalisticones.txt")
set cheminicns to do shell script "ls " & quoted form of (origi & "aalisticones.txt")
tell application "Finder"
set letext to (POSIX file cheminicns as alias)
set label index of letext to 2
end tell
repeat with i from 1 to n
set hdd to item i of media
try
set input to do shell script "echo " & hdd & " | sed 's#//#/#g; s#(#\\(#g;s#)#\\)#g' "
do shell script "echo " & quoted form of input & " >>" & quoted form of cheminicns
set png to do shell script "echo " & quoted form of input & " | sed 's#.*/##' "
do shell script "cp -f " & quoted form of input & " " & quoted form of origi
set input to do shell script "iconutil -c iconset " & quoted form of (origi & png)
do shell script "echo " & quoted form of (origi & png) & " | sed 's#.icns#.iconset#' >>" & quoted form of cheminicns
end try
end repeat
tell application "Finder"
if exists (folder fich) then
open fich
end if
end tell
end if
else
set input to do shell script "echo " & dirchoisi & " | sed 's#//#/#g; s#(#\\(#g;s#)#\\)#g' "
set png to do shell script "echo " & quoted form of input & " | sed 's#.*/##' "
set origi to do shell script "echo " & quoted form of ("/aaaicones/aIconeseule/" & input) & " | sed 's#/Volumes/##; s#" & quoted form of png & "##'"
do shell script "mkdir -p -m 0777 " & quoted form of origi
do shell script "echo " & quoted form of input & " >>" & quoted form of origi & "aalisticones.txt"
do shell script "cp -f " & quoted form of input & " " & quoted form of origi
set input to do shell script "iconutil -c iconset " & quoted form of (origi & png)
do shell script "echo " & quoted form of (origi & png) & " >>" & quoted form of origi & "aalisticones.txt"
end if
end repeat
end open
Is there a way in the Fish Interactive shell for the full path to be displayed. Currently when I navigate to a directory I get the following shell.
millermj#Dodore ~/o/workspace
but I would rather see
millermj#Dodore ~/o-town/workspace
With the new fishshell (v2.3) you can do set -U fish_prompt_pwd_dir_length 0. And it will use the full path. I also use dartfish for my theme. See example below:
Here's my version of prompt_pwd that should display what you're looking for:
function prompt_pwd --description 'Print the current working directory, NOT shortened to fit the prompt'
if test "$PWD" != "$HOME"
printf "%s" (echo $PWD|sed -e 's|/private||' -e "s|^$HOME|~|")
else
echo '~'
end
end
This will display the tilde for the home directory, as usual, but removes the sed command that only pulls the first letter from each directory when you're a few directories deep.
To edit prompt_pwd use funced. It will allow you to interactively alter the function. From the command line type funced prompt_pwd. Once the prompt is displaying to your liking, use funcsave prompt_pwd to make the behavior persist in future sessions.
I personally don't like touching the shared/defaults. Fish has a great functions design, so leverage that.
Create ~/.config/fish/functions/prompt_long_pwd.fish with the contents:
function prompt_long_pwd --description 'Print the current working directory'
echo $PWD | sed -e "s|^$HOME|~|" -e 's|^/private||'
end
Then simply edit your ~/.config/fish/functions/fish_prompt.fish to use prompt_long_pwd. Here is the custom prompt that I use:
~/.config/fish/config.fish:
set -g __fish_git_prompt_show_informative_status 1
set -g __fish_git_prompt_hide_untrackedfiles 1
set -g __fish_git_prompt_color_branch magenta bold
set -g __fish_git_prompt_showupstream "informative"
set -g __fish_git_prompt_char_upstream_ahead "↑"
set -g __fish_git_prompt_char_upstream_behind "↓"
set -g __fish_git_prompt_char_upstream_prefix ""
set -g __fish_git_prompt_char_stagedstate "●"
set -g __fish_git_prompt_char_dirtystate "✚"
set -g __fish_git_prompt_char_untrackedfiles "…"
set -g __fish_git_prompt_char_conflictedstate "✖"
set -g __fish_git_prompt_char_cleanstate "✔"
set -g __fish_git_prompt_color_dirtystate blue
set -g __fish_git_prompt_color_stagedstate yellow
set -g __fish_git_prompt_color_invalidstate red
set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal
set -g __fish_git_prompt_color_cleanstate green bold
~/.config/fish/functions/fish_prompt.fish
function fish_prompt --description 'Write out the prompt'
set -l last_status $status
if not set -q __fish_prompt_normal
set -g __fish_prompt_normal (set_color normal)
end
# PWD
set_color $fish_color_cwd
echo -n (prompt_long_pwd)
set_color normal
printf '%s ' (__fish_git_prompt)
if not test $last_status -eq 0
set_color $fish_color_error
end
echo -n '$ '
end
Create ~/.config/fish/functions/prompt_long_pwd.fish with:
function prompt_long_pwd --description 'Print the full working directory'
echo $PWD
end
In ~/.config/fish/functions/fish_prompt.fish change (prompt_pwd) (set_color normal) to (prompt_long_pwd) (set_color normal).
(prompt_pwd) replaces the directory name with the first letter of the directory, which I too sometimes find annoying. You can also probably modify the original command prompt_pwd, I have not tried it.
This answer is modified from the earlier answer and shows the full directory while keeping the other parts of the default prompt. The key change is in the expression in ~/.config/fish/functions/prompt_long_pwd.fish.
Just thought I'd give everyone a heads up if you stumbled on this and your fish is not responding to the fish_prompt_pwd_dir_length variable try upgrading omf, and then update your omf themes that you're using. Then do some combination of selecting a different theme, reopening the shell, and selecting a different theme again. This is what worked for me. Good luck!
The prompt_pwd function determines the function to be displayed. You should be able to write your own version to get what you want.