Get newspapers on your Kindle - or other e-reader!
Yes, you can often subscribe to Amazon's commercial offerings. Unfortunately, not all newspapers are available (and many are rather overpriced). If you've got a spare machine that's always on and connected to the Internet, then here's a way to get newspapers, magazines, etc delivered automatically to your Kindle.
Note: if you don't like working with scripts, you can do all this perfectly well from the Calibre GUI. This didn't work for me because when I last tried it, one failure to download the news meant that no more articles got though. Annoying when you're on holiday...
These instructions are for Mac OS X 10.7.5 and are designed to give a reliable, scheduled news feed. It'll work with minor tweaks on any Unix-based OS, and it should be adaptable to work with any e-reader that's supported by Calibre.
Download and install Calibre. You don't need to run or configure it - we just need the binaries.
The files will be sent to your Kindle by email. Get the email address from your Kindle account page here.
You probably don't want these files cluttering up your main email account, so create a new free Gmail account now and note down the username and password,
Copy and paste the following script into a text editor, substitute your Kindle and Google details and save it to ~/Library/Scripts/calibre_fetch.sh (create the Scripts folder if it isn't there already). This script uses a number of odd techniques and I make no apologies for my rotten programming skills. Shell script purists, look away now:
#!/bin/bash
### CHANGE THESE VALUES ###
KindleMail='###yourkindlename###@free.kindle.com'
GoogleUser='###yourgooglename###@gmail.com'
GooglePassword='###yourgooglepassword###'
### CHANGE THESE VALUES ###
BinDir='/Applications/calibre.app/Contents/MacOS'
FetchTimeout=7200 # Allow up to 120 minutes to fetch news
SendTimeout=300 # Allow up to 5 minutes to send email
Iterations=3
PN=`basename "$0"`
VER='1.1'
TempFile="/tmp/${PN}.$$.mobi"
CmdFetch="nice ${BinDir}/ebook-convert \"${1}\" $TempFile --output-profile kindle $2 $3"
CmdSend="nice ${BinDir}/calibre-smtp -r smtp.gmail.com --port 587 --username $GoogleUser --password $GooglePassword --a $TempFile $GoogleUser $KindleMail \"${1}\""
# Usage instructions
if [ ! "$1" ]; then
echo "error: you must supply a recipe file" 1>&2
exit 1
fi
# Start fetch process
ErrorFlag=0
i=1
while [ $i -le $Iterations ]
do
# Wait a number of seconds before retrying
if [ $ErrorFlag -eq 1 ]; then
PauseTime=$(expr 30 \* $i)
echo "`date` $PN warning: failed to fetch $1, making attempt $i of $Iterations in $PauseTime seconds" 1>&2
sleep $PauseTime
fi
# Start fetch task in the background and get its process id
eval $CmdFetch >/dev/null 0<&1 2>&1 &
TaskID1=$!
# Set watchdog timer to kill fetch task after specified time
(
sleep $FetchTimeout
kill $TaskID1 >/dev/null 0<&1 2>&1
) >/dev/null 0<&1 2>&1 &
WatchdogID1=$!
# Bring the fetch task back to the foreground
trap "kill $TaskID1 $WatchdogID1 >/dev/null 0<&1 2>&1; exit" INT TERM EXIT
wait $TaskID1
TaskSuccess=$?
trap - INT TERM EXIT
kill $WatchdogID1 >/dev/null 0<&1 2>&1
# Exit loop if task completed successfully
if [ $TaskSuccess -eq 0 ]; then
ErrorFlag=0
break
else
ErrorFlag=1
i=$(($i+1))
continue
fi
done
# If fetching failed, print error and quit
if [ $ErrorFlag -eq 1 ]; then
rm $TempFile >/dev/null 0<&1 2>&1
echo "`date` $PN error: failed to fetch $1, giving up" 1>&2
exit 1
fi
# Start sending process
ErrorFlag=0
i=1
while [ $i -le $MaxIterations ]
do
# Wait a number of seconds before retrying
if [ $ErrorFlag -eq 1 ]; then
PauseTime=$(expr 30 \* $i)
echo "`date` $PN warning: failed to send $1, making attempt $i of $MaxIterations in $PauseTime seconds" 1>&2
sleep $PauseTime
fi
# Start send task in the background and get its process id
eval $CmdSend >/dev/null 0<&1 2>&1 &
TaskID2=$!
# Set watchdog timer to kill send task after specified time
(
sleep $SendTimeout
kill $TaskID2 >/dev/null 0<&1 2>&1
) >/dev/null 0<&1 2>&1 &
WatchdogID2=$!
# Bring the send task back to the foreground
trap "kill $TaskID2 $WatchdogID2 >/dev/null 0<&1 2>&1; exit" INT TERM EXIT
wait $TaskID2
TaskSuccess=$?
trap - INT TERM EXIT
kill $WatchdogID2 >/dev/null 0<&1 2>&1
# Exit loop if task completed successfully
if [ $TaskSuccess -eq 0 ]; then
ErrorFlag=0
break
else
ErrorFlag=1
i=$(($i+1))
continue
fi
done
# Clean up
kill $(jobs -pr) >/dev/null 0<&1 2>&1
rm $TempFile >/dev/null 0<&1 2>&1
# If sending failed, print error and quit
if [ $ErrorFlag -eq 1 ]; then
echo "`date` $PN error: failed to send $1 to $KindleMail, giving up" 1>&2
exit 2
else
echo "`date` $PN sent $1 to $KindleMail"
exit 0
fi
Go to Terminal and set the file permissions with the following:
cd Library/Scripts
chmod 700 calibre_fetch.sh
Test the script with the following command:
./calibre_fetch.sh xkcd.recipe
If all is well, the script should complete successfully after a few minutes and the lastest issue of the wonderful xkcd comic will magically arrive on your Kindle. Yay!
So, now you can get publications 'on demand'. Calibre has a huge number of built-in recipes. You can get a list with the command:
/Applications/calibre.app/Contents/MacOS/ebook-convert --list-recipes
Now you just need to automate it. This is fairly easy on most flavours of Unix. On a Mac it is done using XML launchd scripts placed in ~/Library/LaunchAgents. Here's an example to get the xkcd comic every Monday at 09:30:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.paul.calibre.xkcd.monday</string>
<key>LowPriorityIO</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/Users/paul/Library/Scripts/calibre_fetch.sh</string>
<string>xkcd.recipe</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Weekday</key>
<integer>1</integer>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>30</integer>
</dict>
</dict>
</plist>
You get the idea. Just change it to invoke the recipe you want at the required time. Spaces are OK in the recipe name - the script will deal with these so no need for quotation marks or anything.
Once that's done, save it as something logical like 'com.paul.calibre.xkcd.monday.plist' in the LaunchAgents folder. Now load it into the scheduler with:
launchctl load com.paul.calibre.xkcd.monday.plist
That's it! The job will run as scheduled. If you want to give it a quick test (or you're impatient), you can run it at any time with:
launchctl start com.paul.calibre.xkcd.monday.plist
All done. Add as many launchd scripts as you want to keep your Kindle busy. Enjoy!