Mar 2007
Checking for existance
There's no built-in for checking if a file exists
when developing Dashboard Widgets in JavaScript. But
with a little help from the BSD subsystem we can do
it easily. For this to work, you obviously need to
have
set in your Info.plist. That opens up a whole world of possibilities. I find that the most effective way is to use file. This way we can even check if something is of the correct type. For example, for a recent UnitInfo update, I had to check if InCrease was installed:
<key>AllowSystem</key>
<true/>
set in your Info.plist. That opens up a whole world of possibilities. I find that the most effective way is to use file. This way we can even check if something is of the correct type. For example, for a recent UnitInfo update, I had to check if InCrease was installed:
foldingBase = "~/Library/InCrease/cpu1/";
var fp = widget.system ('/usr/bin/file -bi ' + foldingBase + 'unitinfo.txt', null).outputString.replace ("\n", "");
if (fp.indexOf ("text/plain") != -1)
{
clientVersion = 'InCrease';
} else {
foldingBase = "~/Library/Folding@Home/";
clientVersion = 'Folding@Home';
}
unitInfoFile = foldingBase + 'unitinfo.txt';
clientConfigFile = foldingBase + 'client.cfg';
|
Why I switched to Safari (again)
I've been a big fan of Camino. Ever since it was
called Chimera. Whenever a new Mac OS would come out,
that I didn't have the hardware for, I could still
use a modern, native Mac browser thanks to them.
These guys have done a tremendous job bridging the
Gecko rendering engine with a OS X L&F that in
many ways feels more "Mac" than Apple's own browser.
I originally switched to Camino because:
I liked Camino because it eliminated most of my biggest problems with Safari, but now I'm forced to swtich back because:
In general, I'm not quite sure what to think of Camino anymore. Firefox is getting more and more Mac-like (to some extent) so maybe it would make sense for the two projects to just merge? That Camino would just be the Mac port of Firefox? Camino will run on anything starting with Jaguar which might be (?) a reason it's still so rough around the edges. It is my understanding that backwards compaitility often comes at a high price on OS X (although maybe not so much in Camino's case since it probably doesn't depend on OS services that much).
In any case, I replaced Camino with Safari in my Dock today. Bookmarks would've been trivial but let's be honest - who uses them anymore? del.icio.us, maybe. The biggest nag will actually be all the cookies that have already been set in Camino and the keychain items...
- Many JavaScript WYSIWYG HTML editors will just disregard Safari. And if you have to use two browsers sometimes, why not just completely switch to the one that does everything, right?
- It tries to render XML instead of showing me the tree
- Safari has a weird habit of tagging .txt to downloaded files it can't recognize
- Source view is very primitive with no highlighting and it also can't be reloaded.
- Bookmarks menu has to be one of the slowest in the world
- Downloads can't be opened with a double-click in the Downloads window. EDIT: Actually they can. Just click on the icon.
I liked Camino because it eliminated most of my biggest problems with Safari, but now I'm forced to swtich back because:
- Flash performance in Camino is just terrible. I tried to go around this by disabling it altogether, but in this YouTube day and age it's very difficult to do. And why should you? This got incredibly annoying at times - not being able to swtich tabs when a flash object was loaded in the frontmost window or having the whole app freeze for tens of seconds. Any kind of Flash content would also heaviliy bump Camino's CPU usage.
- Other plugins behaved oddly as well. Often an embedded QuickTime movie would stay inside every tab I would switch to. Clearly something's not right with the Gecko View.
- Window resize performance. I typically have about 10 tabs open at once and window resizing gets really slow. It's not disastrous, but definitely not something you would expect on a 2-year old Mac with 1,25GB RAM.
- Stability issues. Especially in the latest 1.1 branch which would regularly crash every day for me. Downloading the latest nightly seemed to help though.
- No AcidSearch. You can add search sites to Camino by editing an XML file but that won't give you the shorcuts that you have in Safari. This is clearly something that could be added to Camino but I personally have neither the skill nor the time and motivation to do this right now.
- Startup time. Clearly no biggie, but in this day and age you would expect your browse to come up instantly. Almost so with Safari, but not quite with Camino. Here're some completely bogus test results, Camino takes more like 5 secs to start up actually (open returns before all the NIBs are unpacked, I would guess):
> time open -a Camino
real 0m0.504s
user 0m0.080s
sys 0m0.073s
> time open -a Safari
real 0m0.272s
user 0m0.079s
sys 0m0.051s
In general, I'm not quite sure what to think of Camino anymore. Firefox is getting more and more Mac-like (to some extent) so maybe it would make sense for the two projects to just merge? That Camino would just be the Mac port of Firefox? Camino will run on anything starting with Jaguar which might be (?) a reason it's still so rough around the edges. It is my understanding that backwards compaitility often comes at a high price on OS X (although maybe not so much in Camino's case since it probably doesn't depend on OS services that much).
In any case, I replaced Camino with Safari in my Dock today. Bookmarks would've been trivial but let's be honest - who uses them anymore? del.icio.us, maybe. The biggest nag will actually be all the cookies that have already been set in Camino and the keychain items...
Data URL-s
I had known about this before, but never really tried
them until yesterday. I needed a really quick way to
post photos from my phone, so decided to write a
little photogallery app. You just dump pictures off
the phone to a certain directory and that's it. In
the process of reading about imagecopyresized, I
stumbled on this method of embedding images:
That's called a data URL and they were first introduced in 1998.
In PHP, I couldn't find a way to read the data directly with imagejpeg(), so I end up overwriting 1 thumbnail for a whole directory of images. Working with images, the PHP memory limitations will quickly come into play. Anyways, here's the whole PHP code that goes through a directory and creates 80x80 px thumnails of a random portion of the picture. You can also see it in action. The source for that script is here.
<img src='data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD' alt='Picture' />
That's called a data URL and they were first introduced in 1998.
In PHP, I couldn't find a way to read the data directly with imagejpeg(), so I end up overwriting 1 thumbnail for a whole directory of images. Working with images, the PHP memory limitations will quickly come into play. Anyways, here's the whole PHP code that goes through a directory and creates 80x80 px thumnails of a random portion of the picture. You can also see it in action. The source for that script is here.
$id = 'data/';
$dh = opendir($id);
while ($f = readdir($dh))
{
$fp = $id . $f;
if (is_file($fp) && $f != 'tmp')
{
list($w, $h) = getimagesize($fp);
$scale = 0.6;
$ns = 80;
$im = imagecreatetruecolor($ns, $ns);
imagecopyresized($im, imagecreatefromjpeg($fp), 0, 0,
rand(0, $w*$scale), rand(0, $h*$scale), $w*$scale, $h*$scale, $w, $h);
imagejpeg($im, 'data/tmp');
echo "
<a href='javascript:show(\"$fp\",$w,$h)' title='" . date('r', filectime($fp)) . "'>
<img src='data:image/jpg;base64," . base64_encode(file_get_contents('data/tmp')) . "' />
</a>";
}
}
Finding things
I find it ironic, that things get increasingly
difficult to find as their physical proximity
increases (ie distance decreases). Thanks to Google,
I can easily find a solution to a problem some guy,
thousands of kilometres, and more importantly, years
away, found and yet I'm utterly helpless if I have to
find my bus ticket under the pile of papers that is
my workdesk. In other words, filipp's search theorem
states that "the physical distance of an
object is inversely proportionate to the chance of me
actually finding it".
My media library is another example (and I'm sure I'm not alone in this). I have tends of thousands of files stored on different mediums (computers and discs) within the confines of my home, only tens of meters apart, but when I need to find a single file, it's pretty much hopeless. While I can't really do much about my workdesk - it's just a force of habit, I'm still convinced I can do something about those files.
1) Consolidate your storage by getting a bigger disk. I could image this working quite well. If I had about a terabyte of local storage, I could just store everything in one place, and use Spotlight to find it. Ofcourse, this is a band-aid. In a year, would need 2 TB, at least. It's also expensive.
2) Stop hoarding stuff. This isn't really an option for me, but the basic idea is that the really important data doesn't really take up much space. For me it's mostly text, with some pictures that I could easily fit within 10 GB. The rest (music and videos) is just enterntainment, right? Applications have become the most disposable form of "hd filler". But then you also have music bought from iTunes that you simply have nowhere else.
3) Start using some sort of DAM (Digital Asset Management) system. We're already doing this in many areas - Mail.app, iTunes, iPhoto, del.icio.us etc are all managing some assets. But which one should I install? I'm sure there's a really fancy and expensive solution out there, but all I really need is a generic tool that would essentially take snapshots of folders and then allow me to search them.
4) Hack Spotlight to support read-only media and file servers. I actually tried this, but after failed attempts to index SMB shares, I quickly gave up. Who knows, Leopard might help here, but I'm not betting on it.
Taking a snapshot of a folder on a UNIX system, is ofcourse a piece of cake:
With more effort this could be made to check for duplicates (when importing the same folder again) and all sorts of cool things. Things will get hairy when I ned to store some extra attributes for an item. Like the QuickTime comment of a .mov or the year of an MP3. For this, a flat file becomes tedious.
The snapshot approach is really all I need - a medium-agnostic method for indexing and offline-searching file archives. So I'm starting yet another sideproject - a Python script that does exactly that. Here's what I have so far:
The datastore is XML. Every import action has it's own Import element. This allows me to do things like undos etc. Oh, and the trick to getting Unicode to work somewhat properly, is to add:
to /Library/Frameworks/Python.framework//Versions/2.4/lib/python2.4/site-packages/sitecustomize.py
While all of this is very basic ATM, it's already useful for me. No longer do I have to boot up a file server just find out if some music video is stored on it.
My media library is another example (and I'm sure I'm not alone in this). I have tends of thousands of files stored on different mediums (computers and discs) within the confines of my home, only tens of meters apart, but when I need to find a single file, it's pretty much hopeless. While I can't really do much about my workdesk - it's just a force of habit, I'm still convinced I can do something about those files.
So what are the solutions?
1) Consolidate your storage by getting a bigger disk. I could image this working quite well. If I had about a terabyte of local storage, I could just store everything in one place, and use Spotlight to find it. Ofcourse, this is a band-aid. In a year, would need 2 TB, at least. It's also expensive.
2) Stop hoarding stuff. This isn't really an option for me, but the basic idea is that the really important data doesn't really take up much space. For me it's mostly text, with some pictures that I could easily fit within 10 GB. The rest (music and videos) is just enterntainment, right? Applications have become the most disposable form of "hd filler". But then you also have music bought from iTunes that you simply have nowhere else.
3) Start using some sort of DAM (Digital Asset Management) system. We're already doing this in many areas - Mail.app, iTunes, iPhoto, del.icio.us etc are all managing some assets. But which one should I install? I'm sure there's a really fancy and expensive solution out there, but all I really need is a generic tool that would essentially take snapshots of folders and then allow me to search them.
4) Hack Spotlight to support read-only media and file servers. I actually tried this, but after failed attempts to index SMB shares, I quickly gave up. Who knows, Leopard might help here, but I'm not betting on it.
Taking a snapshot of a folder on a UNIX system, is ofcourse a piece of cake:
> find ~/Movies \! -name '.*' > movies.txt
With more effort this could be made to check for duplicates (when importing the same folder again) and all sorts of cool things. Things will get hairy when I ned to store some extra attributes for an item. Like the QuickTime comment of a .mov or the year of an MP3. For this, a flat file becomes tedious.
The snapshot approach is really all I need - a medium-agnostic method for indexing and offline-searching file archives. So I'm starting yet another sideproject - a Python script that does exactly that. Here's what I have so far:
> ./lumi.py /Volumes/300G/Movies/
> importing /Volumes/300G/Movies/
> Successfully imported 1296 items
> ./lumi.py list
> 2007-03-17T12:22:23.827078 (/Volumes/300G/Movies/)
> 2007-03-17T12:23:04.715256 (/Volumes/Shared Items/Movies/)
> ./lumi.py name blender
> /Volumes/300G/Movies/Training (blendervt-interface-3dviewport-v234-r0.avi)
> /Volumes/300G/Movies/Training (blendervt-interface-concept-v234-r0.avi)
> /Volumes/Shared Items/Movies/Animation (Blender3d_SIGGRAPH2005_DVDRip.avi)
> /Volumes/Shared Items/Movies/Animation (BlenderTricksEpisode.avi)
The datastore is XML. Every import action has it's own Import element. This allows me to do things like undos etc. Oh, and the trick to getting Unicode to work somewhat properly, is to add:
import sys
sys.setdefaultencoding('iso-8859-1')
to /Library/Frameworks/Python.framework//Versions/2.4/lib/python2.4/site-packages/sitecustomize.py
While all of this is very basic ATM, it's already useful for me. No longer do I have to boot up a file server just find out if some music video is stored on it.
Running iCal Server on FreeBSD 6.2
13.03.07 21:05 |
Permalink
Mac Mythbusters: white on black saves battery
While reviewing Journler, I stumbled upon a
funny command - Toggle Low Light Display. This
is the same as System Preferences > Universl
Acces > White on Black and is obviously meant
for writing in the dark, but it made me wonder
if it would also help to conserve battery power.
The reasoning being that in a standard
black-on-white configuration we're burning
pixels that really don't have to be lit at all.
Granted, the majority of power consumption in a
TFT comes from backlighting, which has nothing
to do with the video signal, but I was convinced
that there would be a difference. The only
question is - how big of a difference?
To set up a consistent environment, I compiled an Automator workflow that triest to simulate a typical writing scenario - grab a few HTML pages, find some words, create a new document in TextEdit, paste the text, pause for 5 mins, check email, repeat (I created the loop by adding an Open Workflow step that opened the same file). The display in this case is a 20" ACD. I also have a bunch of USB devices, but they're not used during the test so they should all be constants. Display Sleep was disabled and brightness was at maximum. The Mac was left to charge overnight between the test runs.
I run the script twice, fiirst with Black-on-White and then the opposite and measure how long the battery lasts, from being totally charged until it completely blacks out.
Luckily, Automator comes with a cli interface making it easy to measure the time. This is not 100% accurate because to stop the timer, I had to reconnect the power cord and kill automator:
The results:
Black on white: 54m55.852s
White on black: 52m39.918s
So there's a difference, but it's the exact opposite - WOB actually gave a shorter battery life. I guess this could happen if the Mac is actually doing more graphics processing for the inversion and eating more power as a result. The difference is also so small, that there's really no need for another test.
To set up a consistent environment, I compiled an Automator workflow that triest to simulate a typical writing scenario - grab a few HTML pages, find some words, create a new document in TextEdit, paste the text, pause for 5 mins, check email, repeat (I created the loop by adding an Open Workflow step that opened the same file). The display in this case is a 20" ACD. I also have a bunch of USB devices, but they're not used during the test so they should all be constants. Display Sleep was disabled and brightness was at maximum. The Mac was left to charge overnight between the test runs.
I run the script twice, fiirst with Black-on-White and then the opposite and measure how long the battery lasts, from being totally charged until it completely blacks out.
Luckily, Automator comes with a cli interface making it easy to measure the time. This is not 100% accurate because to stop the timer, I had to reconnect the power cord and kill automator:
> time automator /Users/filipp/Desktop/Loop\ test.workflow/
The results:
Black on white: 54m55.852s
White on black: 52m39.918s
So there's a difference, but it's the exact opposite - WOB actually gave a shorter battery life. I guess this could happen if the Mac is actually doing more graphics processing for the inversion and eating more power as a result. The difference is also so small, that there's really no need for another test.
Thou shalt not delete!
This came up in the chatroom. How do you protect a
folder from being deleted while still being able to
write to it? Locking it directly doesn't work,
locking the parent one does, but then you can't write
to the parent dir. Standard UNIX permissions are of
little help, as there is no such thing as a "delete
bit". Enter Access Control Lists. First let's make
sure they're on:
and then we can just take away the right to delete:
+a always adds an ACE, so to delete one:
Suhweet!
> sudo fsaclctl -p / -e
and then we can just take away the right to delete:
> chmod +a "filipp deny delete" test
> ls -e
drwxr-xr-x + 5 filipp filipp 170 Mar 11 19:15 test
0: user:filipp deny delete
+a always adds an ACE, so to delete one:
> chmod -a# 0 test
Suhweet!
with(something)
I discovered a nice little shortcut for setting
element properties in JavaScroüt. Instead of saying:
You can "zoom into" the element:
nameDiv = document.createElement('div')
nameDiv.className = 'nameDiv';
nameDiv.innerText = showName;
nameDiv.title = showName;
You can "zoom into" the element:
with (nameDiv = document.createElement('div'))
{
className = 'nameDiv';
innerText = showName;
title = showName;
}
XML stuff
A (weird) way to convert PHP arrays to XML. I came up
with this for Collective - passing media metadata
(with getid3) from the server to
JavaScript. It's not supposed to work with
numeric indexes - the algorithm doesn't break,
it just produces invalid XML (can't have numbers
as element names):
A way to convert a file system hierarchy into an XML document, in Python. This is for a simple file-cataloguer I desperately needed (more on this later, hopefully):
function array2xml($array)
{
foreach ($array as $k => $v)
{
if (is_array($v))
$out .= "<$k " . array2xml($v) . "</$k>\n";
else {
$out .= "\n\t$k='$v'";
if (is_array(next($array)) || (array_pop(array_keys($array)) == $k))
$out .= ">\n";
}
}
return $out;
}
A way to convert a file system hierarchy into an XML document, in Python. This is for a simple file-cataloguer I desperately needed (more on this later, hopefully):
imp = minidom.getDOMImplementation()
ldoc = imp.createDocument(None, 'Library', None)
def parsedir(dirname, parent = ldoc.documentElement):
global ldoc
if os.path.isdir(dirname):
newDir = ldoc.createElement('Directory')
newDir.setAttribute('path', dirname)
parent.appendChild(newDir)
parent = newDir
for i in os.listdir(dirname):
fp = os.path.join(dirname, i)
if os.path.isdir(fp):
parsedir(fp, newDir)
else:
newFile = ldoc.createElement('File')
newFile.setAttribute('name', i)
parent.appendChild(newFile)
Geek humour
Punk 1.0.6
Released yesterday. The version
number is completely made up. Supports the new
IMDb layout and is rewritten to use
XMLHttpRequest which should also work through
proxies by using the system settings.
XMLHttpRequest seems to also give you caching
for free (at least it creates cruft in the Cache
folder).
This is probably the most succesful thing I've ever made (thanks largely to Martin's nice UI) and this version even got some comments from actual users! 140 downloads in less than 24 hours is not exactly VLC rate, but makes me happy nevertheless. Apple actually featured it again in the Widgets section which always gives you that nice tingly feeling. :)
It's amazing how much energy some feedback gives to a freeware developer. If there's anyone reading this, I would encourage you to send even a small comment or thank-you to your favourite freeware developer(s). It makes all the difference in the world.
This is probably the most succesful thing I've ever made (thanks largely to Martin's nice UI) and this version even got some comments from actual users! 140 downloads in less than 24 hours is not exactly VLC rate, but makes me happy nevertheless. Apple actually featured it again in the Widgets section which always gives you that nice tingly feeling. :)
It's amazing how much energy some feedback gives to a freeware developer. If there's anyone reading this, I would encourage you to send even a small comment or thank-you to your favourite freeware developer(s). It makes all the difference in the world.
Concatenating a bunch of files
XMLHttpRequest and redirects
07.03.07 19:27 |
Permalink
Hit a major bummer while updating the Punk widget
today. Essentially, the onreadystatechange
handler will miss the redirect/Location: (302) header
being sent. IMDb uses 302 when there's only one
search result and catching that was a really nice
shortcut instead of having to parse the page to find
out what has happened. The widget used curl
before with which it's super-easy to notice these
things, but for some other reasons, I had already
re-written everything to use XMLHttpRequest.
Solutions? None so far, at least not for JavaScript. I think I've tried every possible way and place to stick getAllResponseHeaders(), including combining it with setTimeout(), but no cigar. The problem is, this behaviour is in the spec, so there's really no-one to blame. (?)
It really seems that, in my case, the "easiest" thing to do is just taking a peak at the page title. With IMDb it's easy - it's "IMDb Title Search" whenever there's more than 1 result.
Solutions? None so far, at least not for JavaScript. I think I've tried every possible way and place to stick getAllResponseHeaders(), including combining it with setTimeout(), but no cigar. The problem is, this behaviour is in the spec, so there's really no-one to blame. (?)
It really seems that, in my case, the "easiest" thing to do is just taking a peak at the page title. With IMDb it's easy - it's "IMDb Title Search" whenever there's more than 1 result.
Scraping google.com
I don't know when this happened, but it seems you
can't scrape (parse) google.com as easily as before.
At least with Python, a simple
will give you this notice instead:
And here's me not understanding why my regexes are not matching. :) Apparently, you should at least show some kind of User Agent. In Python you can do this easily by subclassing URLopener and setting it's version property to something, like:
And I'm really starting to love TextMate. Just type your little Python script and hit Cmd-R. Brilliant.
s=urllib.urlopen('http://www.google.com/search?q=define%3Aesoteric')
r=s.read()
print r
will give you this notice instead:
/snip/ Your client does not have permission to get URL /search?q=define%3Aesoteric from this server /snip/
And here's me not understanding why my regexes are not matching. :) Apparently, you should at least show some kind of User Agent. In Python you can do this easily by subclassing URLopener and setting it's version property to something, like:
class MyOpener(urllib.URLopener):
version = "InternetExploiter/666"
urllib._urlopener = MyOpener()
And I'm really starting to love TextMate. Just type your little Python script and hit Cmd-R. Brilliant.