איך להוריד סרטי יוטיוב לצפיה אופליין באייפד/אייפון

כן, אני יודע שמזמן לא היה פה פוסט, תתבעו אותי ;).

אחד הדברים שאף פעם לא עובד כמו שצריך באייפון ובאייפד (ובאייפוד) זה YouTube. כלומר – יש ישום של אפל לצורך העניין, אבל החוויה לא מרנינה:
בדרך כלל לא תצליחו לראות סרטון בלי גמגומים, גם אם אתם משתמשים ברשת אלחוטית מהירה עם חיבור אינטרנט שמן וטוב.

לאחרונה אני צופה בדי הרבה משחקי סטארקרפט ביוטיוב, והפריע לי חוסר היכולת לראות את המשחקים על האייפד.
זה הביא לי רעיון:
מה אם נוריד מראש את הסרטונים, וניתן אותם לאייפד בצורת פיד RSS – ממש כמו שצופים בפוקסט וידאו – ככה שנוכל לראות אותם בלי להיות תלויים בחסדי מהירות התקשורת ליוטיוב?

אז בתור התחלה, הצצה למטרה של כל הפוסט הזה:
הנה פיד הרסס של הערוצים שאני מנוי עלהם ביוטיוב (Subscriptions).
את הפיד הזה הוספתי לאייטונז (כפודקאסט), ומאותו רגע הוא מוריד את הסרטונים ומסנכרן אותם לאייפד.

אז איך כל זה קורה?

הכל מתחיל בAPI הנתונים של יוטיוב, שמאפשר קבלת פידי RSS שונים ומשונים, כשהשימושי שבהם מבחינתי הוא פיד שמאפשר קבלת הסרטונים החדשים בערוצים שמשתמש מסויים מנוי עליהם:

http://gdata.youtube.com/feeds/base/users/omryy/newsubscriptionvideos

שימו לב ששם המשתמש שלי הוא חלק מהURL.
קל למדי לחלץ מפה את רשימת הURLים לסרטונים שמופיעים בפיד, למשל עם הסקריפט הזה:
[CODE]
GET "http://gdata.youtube.com/feeds/base/users/omryy/newsubscriptionvideos" | grep http://www.youtube.com/watch?v=[a-zA-Z0-9_\-]* -o | sort -u
[/CODE]
הסקריפט, בפשטות – מוריד את הפיד, מחפש בתוכו URLים שנראים כמו לינקים לסרטון ביוטיוב, ומבטל כפילויות שמופיעות מסיבה לא חשובה בפיד.

הצעד הבא הוא להוריד את הסרטים עצמם (הלינקים בפיד הם לינקים לצפיה ישירות ביוטיוב).
לשם כך נשתמש בכלי החביב במיוחד, youtube-dl,

הפקודה הבאה תוריד את הסרטון מהURL הנתון, ותשמור אותו בפורמט נחמד וברור שכולל את השם של מי שהעלה, וכן שם קובץ של הכותרת של הסרטים.
youtube-dl -w YOUTUBE_URL -o "%\(uploader\)s-%\(stitle\)s.%\(ext\)s"
אפשר לחבר את הפקודה הזו לפקודה שמחלצת את הURLים מהפיד תוך שימוש בxargs, אבל בחרתי להשתמש בGNU Parallel שמאפשר הפעלה של פקודה מסויימת כמה פעמים במקביל (בדומה מאוד לxargs, רק במקביל).

אז הפקודה השלמה להורדת הסרטונים שמעניינים אותי היא:
[CODE]
#!/bin/bash
source conf
pushd $YT_DOWNLOAD_DIR
GET "http://gdata.youtube.com/feeds/base/users/omryy/newsubscriptionvideos" | grep http://www.youtube.com/watch?v=[a-zA-Z0-9_\-]* -o | sort -u | $BIN_DIR/parallel -j $CONCURRENT_DOWNLOADS $BIN_DIR/youtube-dl -w {} -o "%\(uploader\)s-%\(stitle\)s.%\(ext\)s"
popd
[/CODE]

הסקריפט הזה (והבאים אחריו) משתמש בקובץ ההגדרות הבא:
[CODE]
BIN_DIR=/home/omry/youtube-rss/bin
YT_DOWNLOAD_DIR=/home/omry/youtube-rss/download
CONCURRENT_DOWNLOADS=20
RSS_WEB_DIR=/home/omry/www/youtube-rss.firefang.net
RSS_BASE_URL=http://youtube-rss.firefang.net

# Delete older than X days
DELETE_OLDER=14
[/CODE]
עד פה, הכל טוב.
הפקודה הזו תוריד רק קבצים חדשים שלא הורדו כבר (הדגל -w).

כשניסיתי להעלות לאייפד את הקבצים האלו, שמתי לב שהוא החליט לדלג על חלק גדול מהם.
מסתבר שכל הקבצים שדולגו היו ברזולוציה של HD מלא (1920X1080).
אוקיי, אז זה הדבר הבא שצריך לתקן.
בהתחלה חשבתי להשתמש בffmpeg – שהיא הפתרון המקובל לקידוד קבצי וידאו.
הבעיה היא שצריך לקמפל גרסא שלה שתומכת בH264 (הקידוד שמתאים למכשירי אפל), ושבאופן כללי די קשה לגרום לה לעשות מה שאתם רוצים.
אחרי המשך חפירות מצאתי את Handbreak שהיא תוכנה (בקוד פתוח כמו כל שאר הדברים בפוסט הזה) שתפקידה בחיים הוא לקודד סרטונים לאייפוד, אייפון אייפד ושאר חברים בצורה מאוד פשוטה.
Handbreak מגיעה עם ממשק GTK שלא ממש עניין אותי, ועם שורת פקודה שמאוד עניינה אותי.
אגב, היא משתמשת בlibavcodec מבית היוצר של מפתחי ffmpeg.
לעניינינו, הפקודה לקידוד סרטון לפורמט אייפד היא :
[CODE]
HandBrakeCLI -Z iPad -i input_file -o output_file.mp4
[/CODE]
לא יכול להיות יותר פשוט מזה.

התהליך עצמו יכול לקחת די הרבה זמן, תלוי באורך הסרט ובעוצמת העיבוד של המחשב שלכם.
Handbreak מספיק מוצלחת כדי להשתמש בכל הליבות שלכם (אפשר לומר לה להשתמש במספר מסויים אם רוצים) ולרוץ בעדיפות נמוכה כדי לא לחנוק תהליכים אחרים.

השתמשתי בתסריט bash קטן כדי לעבור על הקבצים שירדו, ולקודד לספריה חדשה קבצים עם אותו שם אם הם כבר לא נמצאים שם.
[CODE]
$ cat encode_new.sh
#!/bin/bash
source conf
for file in `ls -1 $YT_DOWNLOAD_DIR/*.mp4 $YT_DOWNLOAD_DIR/*.flv $YT_DOWNLOAD_DIR/*.video`
do
base=`basename ${file%.*}`
ipad=${RSS_WEB_DIR}/${base}.mp4
if [ -f $ipad ]
then
echo "Skipping $ipad"
else
$BIN_DIR/HandBrakeCLI -Z iPad -i $file -o $ipad
touch -c -r $file $ipad
./update_feed.sh
fi
done
[/CODE]

אחרי כל קובץ שמקודד, הסקיפט update_feed.sh נקרא.
סקריפט זה משתמש בתוכנית ג'אווה קטנה שכתבתי שמכינה קובץ RSS על בסיס ספריה עם סרטוני וידאו.
לבסוף, נשאר רק למחוק קבצים ישנים יותר מ14 יום כדי למנוע התפוצצות, ולהריץ את כל העסק פעם ביום או משהו עם CRON.

[CODE]
$ cat delete_old.sh
#!/bin/bash
source conf
find -mtime +$DELETE_OLDER -exec echo rm $YT_DOWNLOAD_DIR/{} $RSS_WEB_DIR/{} \;
[/CODE]

אפשר להוריד את כל העסק מפה, שימו לב שזה כולל את HandBreak בגרסאת AMD64 ללינוקס, אבל אפשר להוריד מהאתר שלהם גרסאות אחרות.

לסיום, הנה סרטון סטרקראפט מצחיק:

ג'אווה וHTTPS

אחד הכאבים המפתיעים והלא צפויים שנתקלתי בהם לאחרונה עם ג'אווה היה כשניסיתי לתקשר עם שרת HTTPS שנחתם בחתימה של StartSSL.
מסתבר שג'אווה מגיעה כמעט בלי חתימות של ספקי חתימות (verisign שם, אבל הרבה מאוד אחרים לא).
חפירות באינטרנט הובילו לכל מני פתרונות שלא עבדו, אולי כי החתימה שלי היא Wildcard certificate (*.site.com).
פתרון אפשרי הוא ליבא את חתימת השורש של StartSSL לתוך הJVM, אבל זו פעולה ידנית שכל משתמש צריך לעשות ובכל מקרה היא לא עבדה לי, אולי בגלל סוג החתימה.
כל זה מעצבן למדי, הדפדפן סומך על האתר אבל ג'אווה לא מסכימה להתחבר:
נסיון להשתמש בURL הרגיל של ג'אווה כדי להתחבר בHTTPS בדרך כלל מוביל לשגיאה הנפלאה הבאה (אלא אם מדובר בחתימה שחתם השורש שלה ידוע לJVM):

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:294)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:200)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1053)
... 16 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:289)

פתרון:
מעבר לHTTPClient של אפאצ'י ושימוש בקוד הבא (דוגמא לGET ולPOST)
התיעוד של הספריה לא משהו, יש כמה גרסאות לא תואמות שלה והתיעוד מתייחס לגרסא ישנה.
הקוד הבא עובד עם גרסא 4.0.3 של HTTPClient ו4.1.0 של HTTPCore (זו תלות נדרשת לHTTPClient, אפשר להוריד מאותו אתר).
קחו בחשבון שהפתרון הזה מאפשר תקיפת MAN IN THE MIDDLE כדי לזייף את החתימה, אבל מבחינה פרקטית עדיף משהו שעובד אבל קצת פגיע מאשר משהו שלא עובד בכלל. (וכמובן ששימוש בHTTPS הוא עדיף על שימוש בHTTP נקי בכל מקרה).

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;

public class HTTPClientSSLExample
{
public static void main(String[] args) throws MalformedURLException, IOException
{
byte[] bytes = getURLBytes_httpclient("https://www.startssl.com/", 30000, 30000);
System.out.println(new String(bytes));
}

public static byte[] getURLBytes_httpclient(String url, int connectionTimeout, int readTimeout) throws IOException
{
long now = System.currentTimeMillis();

DefaultHttpClient httpclient = getHttpClient(url);

httpclient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout);
httpclient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);

HttpGet get = new HttpGet(url);
HttpResponse response = httpclient.execute(get);
int res = response.getStatusLine().getStatusCode();
if (res == 200)
{
HttpEntity entity = response.getEntity();
int len = (int) entity.getContentLength();
InputStream in = entity.getContent();
ByteArrayOutputStream bout = new ByteArrayOutputStream(len > 0 ? len : 1000);
pump(in, bout);
return bout.toByteArray();
}
else
{
String bs = "";
try
{
HttpEntity entity = response.getEntity();
bs = entity == null ? null : EntityUtils.toString(entity);
}
catch (IOException e)
{
bs += " || Exception while trying to read data from stream : " + e.getMessage();
}

throw new IOException("Server returned HTTP " + res + " after " + (System.currentTimeMillis() - now) + " ms, URL : " + url + " data: " + bs);
}
}

public static ByteArrayInputStream openInputStream_httpclient(String url, int connectionTimeout, int readTimeout, byte[] postdata) throws IOException
{
if (postdata != null)
{
DefaultHttpClient httpClient = getHttpClient(url);
HttpPost post = new HttpPost(url);
InputStreamEntity reqEntity = new InputStreamEntity(new ByteArrayInputStream(postdata), postdata.length);
reqEntity.setContentType("binary/octet-stream");
post.setEntity(reqEntity);
HttpResponse response = httpClient.execute(post);
int res = response.getStatusLine().getStatusCode();
if (res == 200)
{
HttpEntity entity = response.getEntity();
int len = (int) entity.getContentLength();
InputStream in = entity.getContent();
ByteArrayOutputStream bout = new ByteArrayOutputStream(len > 0 ? len : 1000);
pump(in, bout);
return new ByteArrayInputStream(bout.toByteArray());
}
else
{
throw new IOException("Http response code " + res);
}
}
else
{
byte[] bytes = getURLBytes_httpclient(url, connectionTimeout, readTimeout);
return new ByteArrayInputStream(bytes);
}
}

private static DefaultHttpClient getHttpClient(String url1) throws IOException
{
DefaultHttpClient httpclient = new DefaultHttpClient();

try
{
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager()
{

public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException
{
}

public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException
{
}

public X509Certificate[] getAcceptedIssuers()
{
return null;
}
};
ctx.init(null, new TrustManager[]
{
tm
}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = httpclient.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));
httpclient = new DefaultHttpClient(ccm, httpclient.getParams());
}
catch (Exception ex)
{
ex.printStackTrace();
return null;
}

// This block handles urls with user:password@server block
URL u = new URL(url1);
String userInfo = u.getUserInfo();
if (userInfo != null)
{
String user;
String password;
int i = userInfo != null ? userInfo.indexOf(':') : -1;
if (i == -1)
{
user = userInfo.substring(0);
password = "";
}
else
{
user = userInfo.substring(0, i);
password = userInfo.substring(i + 1);
}
httpclient.getCredentialsProvider().setCredentials(new AuthScope(u.getHost(), u.getPort()), new UsernamePasswordCredentials(user, password));
}
return httpclient;
}

/**
* Writes the bytes read from the given input stream into the given output
* stream until the end of the input stream is reached. Returns the amount
* of bytes actually read/written.
*/
public static int pump(InputStream in, OutputStream out) throws IOException
{
byte[] buf = new byte[4096];
int count;
int amountRead = 0;

while ((count = in.read(buf)) != -1)
{
out.write(buf, 0, count);
amountRead += count;
}

return amountRead;
}
}

העולם כרשת

פאול באטלר – מתמחה בפייסבוק – קנה לו קצת תהילה כשרינדר את הגרף החברתי של פייסבוק במערכת קורדינטות גאוגרפיות לפי המיקום של המשתמשים, כשכל שני חברים מתוך דגימה של עשרה מליון משתמשים אקראיים בפייסבוק מצויירת כקו.
התוצאה מרשימה מאוד, ולמעשה מציירת את המפה של רוב העולם.

(לחצו כדי לקבל תמונה ברזולוציה גבוהה).

בזק בינלאומי – האנטי וירוס שחזר מהכפור

בזק בין לאומי החליטו לצ'פר אותי באנטי וירוס בלי שאני אבקש.

הכל היה טוב ויפה כי על עוד לא ידעתי על זה, עד שהבחנתי בתקלות תקשורת מוזרות. פורטים שהיו לי חסומים בתקשורת החוצה, והקש ששבר את גב הגמל: שגיאה בSVN CHECKOUT מהשרת שלי.

אחרי שבזבזתי איזה שעה בלנסות להבין מה נשבר בשרת וגיליתי שהכל נראה תקין ועובד – בדקתי דרך נטוויז'ן ודרך שרת אחר בחו"ל ובשני במקרים זה עבר.
כאן כבר היה לי ברור שמישהו שבזק בין לאומי אשמים.

ניסיתי לדבר איתם בצ'אט בתמיכה, ולא הגיבו אז התקשרתי.
למזלי נפלתי על נציג שלא הניח שאני לא יודע על מה אני מדבר, הוא שינה את הניתוב שלי כך שאני לא אעבור דרך האנטי-וירוס שלהם, עשיתי ריסטרט למודם והנה זה פלא, הכל בסדר.

בזבזתי עוד עשר דקות בלבטל את האנטי וירוס בשרות הלקוחות (מסתבר שלא שילמתי עליו, זה היה צ'ופר מכל הלב!), ואני מקווה שזה סוף הסאגה הזו.

בקיצור, לקוחות בינ"ל, זהירות.

להצביע בארנק

לפני כמה שבועות xmarks, החברה לסינכרון סימניות הודיעה שהיא סוגרת את שעריה מחוסר כסף.
החברה סיפקה שרות שימושי של סינכרון סימניות בין דפדפנים שונים בחינם, מתוך הנחה שהתבררה כשגויה שהם יצליחו לעשות כסף מבסיס הנתונים של הסימניות.
אני אישית לא השתמשתי בxmarks (ומאז נראה שכל מני דברים קורים שאולי יצילו את החברה), אבל האפיזודה הזו גרמה לי לחשוב:

יש כמה שירותים חינמיים שאני כן משתמש בהם, ואני די אתעצב אם הם יופסקו פתאום.
שרות כזה לדוגמא הוא lastpass, שמשמש אותי נאמנה כבר כמה חודשים מאז הפוסט על התורכים.

לlastpass יש שירות פרימיום ב12$ לשנה שנותן כל מיני פיצ'רים כמו תמיכה בסלולריים מסויימים וכו'.
למרות שאני לא ממש צריך את הפיצ'רים המיוחדים שמקבלים בפרימיום, החלטתי לשלם על השירות.
הסיבה היא – כמובן – שאני לא רוצה שlastpass ילכו בדרכו של הדודו, או של הxmarks.

אני קורא לכולכם, אם יש שירות שאתם אוהבים – תראו למפתחים את אהבתכם עם כרטיס האשראי או חשבון הפייפאל שלכם.

StartSSL

כשהגדרתי את הדואר באייפד, גיליתי שאפל לא ממש מסתדרים עם החתימה הדיגיטלית המצ'וקמקת שהתקנתי בשרת הדואר – ופצחתי בחיפוש אחר מקור לחתימה זולה.
מצאתי את StartSSL, חברה שמספקת חתימות דיגיטליות במחירים נמוכים במיוחד שמתחילים ב.. חינם.
כן, אפשר לקבל מStartSSL חתימה דיגיטלית לשרת שלכם, בין אם זה שרת דואר, ווב, ג'אבר ועוד – ללא תשלום.
חוץ מזה שמחיר הוא ללא תחרות, האתר שלהם די מקל על כל התהליך, וכולל מידע רב והנחיות שפשוט עובדות.
במהלך התקנת החתימה בשרת הדואר (דבר שלא מתועד אצלם באתר) נתקלתי בקשיים – ולמרבה הפלא קיבלתי תשובות מועילות כשפניתי לתמיכה שלהם (ואני מזכיר – בתור לקוח לא משלם).
כל הדפדפנים העיקריים תומכים בהם, ולא נתקלתי בבעיות מיוחדות בגלישה לאתר שמוצפן עם החתימה שלהם, או בשימוש בדואר IMAP וSMTP שמוצפן עם החתימה (כולל כאמור ממכשירים ניידים של אפל – אייפון ואייפד).

את הכסף הם עושים מחתימות מאומתות ברמה זו או אחרת, החל באימות זהות אישי ב$50, וכלה בחתימה מסוג Extended validation – אלו שנותנות את הריבוע הירוק הענק בדפדפן – שנמצאת כרגע במבצע ועולה $150 לשנתיים (בווריסיין חתימה כזו עולה כ2695$).
עכשיו – 50$ לאימות זהות זה מעט מאוד יחסית לשאר התעשיה, ומפתה לחשוב שהם לא בודקים כמו שצריך.
אתמול התחלתי תהליך אימות זהות כזה – כשהמטרה היא להשיג חתימה דיגיטלית מלאה לface.com, והרושם שלי בינתיים הוא שהם בודקים מאוד ברצינות ולא מחפפים.
ואנקדוטה קטנה: בזמן החלפת האימיילים עם נציגי החברה, סיפרתי להם שאין עלי את תעודת הזהות כי היא גדולה מדי ורוב הישראלים לא מסתובבים איתה. בתגובה נאמר לי בחיוך שהם חברה ישראלית שממוקמת באילת ושעל פי חוק אני אמור להסתובב עם הת"ז :).
אני ממליץ בחום לנסות אותם למי שרוצה לארגן SSL בשרת ולא בא לו לשלשל לארנק של ווריסיין סכומים מופרכים.

אייפד חדש, ורשימת אפליקציות מומלצות

לפני שלושה ימים קניתי בפאלו אלטו אייפד, גרסאת WiFi עם 64GB זכרון.
בתור התחלה, התלבטתי אם אני בכלל רוצה אייפד, הטיעונים בעד כללו:

  • הוא מתאים לטיסה הרבה יותר מלפטופ ויש לי טיסה של 18 שעות בעוד יומיים
  • אני בטח אמצא זמן להשתמש בו לפני השינה כפלטפורמה לקריאה
  • היה כיף מאוד לשחק בו באפל סטור (אני חושב שביקרתי באפל שלוש פעמים לפני שקניתי אותו), בפעם הראשונה אמרתי נו טוב, בפעם השניה שיחקתי קצת פלייט קונטרול וקראתי קצת פופלר סיינס על המכשיר בחנות ובפעם השלישית כבר קניתי.

הטיעונים נגד הם:

  • מתי אני אשתמש בו? הוא לא מתאים לחדר כושר, הוא גדול מדי מכדי להסתובב איתו בכיס ובדרך כלל יש לי מחשב איפה שאני נמצא.
  • האם אני באמת רוצה לקנות עוד מוצר של אפל? נסיון העבר מראה שהם לא חוסכים מאמץ להקשות על החיים של משתמשים מתקדמים, ובמיוחד משתמשים מתקדמים לא אמריקאיים.

אחרי שהחלטתי שאני הולך על זה, נשארה השאלה של האם לקנות את גרסת ה3G או את גרסאת הWiFI:
התוספת למחיר היא עוד כ130$, לא זניח בכלל – ובארץ מחירי התקשורת הסלולרית הם גבוהים מאוד (כמדומני שאפשר לקנות מיקרו סים לאייפד עם חבילת נתונים ב100 שקל תשלום ראשוני ובכ90 שקל תשלום חודשי עבור חבילת נתונים לא מרשימה, בנוסף חשבתי שהמכשיר יהיה נעול לAT&T, ולא הייתי בטוח שאני רוצה לפרוץ אותו.
בחנות התברר לי שלמרות שאפל לא מפרסמת את העניין, מכשירי האייפד מגרסאת ה3G לא נעולים לAT&T, ולא צריכה להיות בעיה להשתמש בו בארץ. למרות זאת החלטתי ללכת על גרסאת הWiFi כדי לחסוך את התשלום החודשי. כשיגיע הזמן אני אמצא פתרון של שימוש בנקודת WiFi ניידת (אפשר לעשות את זה עם טלפונים של אנדרואיד ועם אייפון פרוץ).

כאמור – ברגע האמת הלכתי על גרסאת הWiFi עם 64GB, מתוך הבנה שזכרון תמיד חסר ומה שנראה הרבה היום יראה צפוף מאוד בעוד שנתיים.

אחרי כל ההקדמות:
המכשיר מדהים, מסך המגע הוא טוב כמו שאפשר לצפות מאפל והתחושה הכללית של המכשיר היא מהירות ותגובתיות מעולה.
דבר מעניין ולא צפוי – האייפד הוא שובר קרח רציני:
מייד אחרי שקניתי אותו ישבתי לבירה עם חברים במסעדה קרובה לחנות, אחרי כשעה ניגש אלי אדם זקן ובעל אייפד בעצמו, התחלנו לדבר על הא ועל דא ולבסוף הוא נתן לי במתנה כיסוי מגן לאייפד, שהוא כבר לא היה צריך כי הוא קנה אחד אחר.
בשדה התעופה בקונקשן לישראל, נגשו אלי שלושה ישראלים (בנפרד) בזמן שישבתי והתעניינו במכשיר.
מעניין אם תהיה תגובה דומה בבית קפה בארץ.

כצפוי : הרבה מהאפליקציות לאייפד הן פורטים של אפליקציות מהאייפון. מה שקצת חבל זה שבדרך כלל המפתחים מנצלים את ההזדמנות כדי להעלות את המחיר בצורה משמעותית לעומת גרסאת האייפון.
למרות זאת, רוב האפליקציות שקניתי שוות את הכסף. הנה רשימה קצרה של אפליקציות שקניתי/הורדתי עד עכשיו:
להמשיך לקרוא אייפד חדש, ורשימת אפליקציות מומלצות

סן פרנסיסקו, Inception ומהירות הורדה מטורפת

השקט שהשתרר בבלוג לא נובע משתקנות שפקדה אותי, אלא מזה שבשבועות האחרונים אני נמצא בארצות הברית במסגרת העבודה ולא היה לי יותר מדי זמן לנשום.
אני נמצא באיזור סאן פרנסיסקו (פאלו אלטו), ואני אשאר פה בשבועיים הקרובים.
הייתי רוצה להרחיב על מה אני עושה פה, אבל לצערי אני לא יכול בשלב זה, אולי עוד כמה חודשים.
יש לי רק ממצא אחד להציג בינתיים:
Downloading at 15.5MB/Sec

אציין שזו קצת רמאות:
יש לי בלפטופ חיבור של 100 מגה ביט, והמהירות הזו היא בלתי אפשרית והיתה רגעית (המקסימום הוא 12.5MB/SEC), מצד שני – אני חותם שאם אני אנסה עם חיבור ג'יגביט אני אעבור את המהירות הזו בקלות.

ברגעים אלו אני נמצא בסן פרנסיסקו, בסטארבק כלשהו ליד יוניון סקוור.
סיימתי לראות את Inception בIMAX לפני כמה שעות.
ראיתי אותו כבר לפני שבוע, והחלטתי שאני צריך לראות אותו שוב.
לא התכוונתי שזה יקרה כל כך מוקדם, אבל חבר הזמין אותי להצטרף אליו כשהוא שמע שאני בעיר.

הסרט הזה הוא כנראה הסרט הכי טוב ב2010, וזה מעניין כי הסרט הכי טוב של 2009 היה כנראה האביר האפל – כשמה שמשותף לשניהם זה כמובן ששניהם בוימו על ידי כריסטופן נולן.
הסרט הזה עושה למטאריקס מה שהמצאת המחסנית עשתה למדעי המחשב, ואת זה תבינו רק אחרי שתראו את הסרט (אבל רק אם מחסנית בשבילכם היא יותר מאשר מקום לשים בו כדורים).
בקיצור, מומלץ בחום – ולבוא מרוכזים :).

אחרי הסרט הלכנו לאכול במסעדה קוריאנית בשם Brothers, היה מצויין.

לא חושב שאכלתי אי פעם אוכל קוריאני, מישהו מכיר מסעדה קוריאנית בארץ  (רצוי כזו שלא תבייש את הפירמה)?

סן פרנסיסקו היא כנראה העיר המועדפת עלי בארצות הברית,וזה מעניין כי באופן לא מתוכנן היא גם העיר בחו"ל שיצא לי לבקר בה הכי הרבה פעמים.
בשבוע שעבר עשיתי לקולגה שזו הפעם הראשונה שלו בעיר את הסיבוב הרגיל של החוף הצפוני, פיר הדייגים, אופניים בסיבוב על גשר הזהב ואל סולסליטו וחזרה במעבורת.
היה יום טוב.
היום עדיין לא נגמר, אבל הוא באווירה הרבה יותר רגועה מבחינתי.
פעם ראשונה שאני כותב פוסט בבלוג תוך ישיבה בסטרבקס. (למעשה היום היתה הפעם הראשונה שביקרתי בסטארבקס – יש להם קפה שמזכיר את הקפה בארץ, ונראה שבכל הסניפים יש אינטרנט ותנאים לשימוש בלפטופ).

המזג אוויר פה מעולה, אפילו קריר לפעמים.
למעשה, זה הקיץ הכי קר באיזור מזה זמן רב – מה שמסתדר עם הטירוף שהולך בארץ (כל החום הזה צריך ללכת לאנשהו, נכון?)
אם הכל ילך לפי התוכניות, אני אשוב למדינתנו המהבילה בעוד כשבועיים, אשאר בארץ כשבוע ואחזור לפה לעוד כמה שבועות.
ריבוי הטיסות בקונטיננטל כבר הביא אותי לרמת כסף בנוסע המתמיד, אולי נתחיל לראות שדרוגים אקראיים בקרוב.

עד כאן עידכונינו.

הדוור מצלצל פעמיים, ואז כותב את הסיסמא על תיבת הדואר

אחת התוכנות הפופולאריות ביותר לניהול רשימות תפוצה היא mailman.
היום קיבלתי תזכורת מאחת מרשימות התפוצה אליהן אני רשום שאני רשום אליה, ובתזכורת היתה הסיסמא איתה נרשמתי.

מי שקרא את הפוסט על התורכים, זוכר מה זה אומר:

מיילמן שומר את הסיסמא כטקסט נקי.

חיפוש קצר בגוגל הוביל לזה: באג במיילמן מ2006 (!)  שלאחרונה זכה לקצת התייחסות, הכרה מצד מפתחי מיילמן שזו בעיה שתתוקן בגרסא הבאה.
לא ממש מוסיף כבוד להילת האבטחה המוגברת של מוצרי קוד פתוח, באג טיוויאלי וחשוב כזה זוכה להתעלמות במשך ארבע שנים.
הבאג הוא כל  כך ישן שגם שכבר תצא הגרסא הבאה רוב המשתמשים לא יטרחו לשדרג כי הם אפילו לא יזכרו שהם התקינו את מיילמן.

בכל מקרה, המשמעות של זה היא שהסיסמאות שנתתם לרשימות התפוצה בסכנת חשיפה גבוהה. הדבר הזה מדגיש את החשיבות של פתרון כמו LastPass או KeePass שיאפשרו שימוש בסיסמאות אקראיות לכל שרות.