Monday, July 26, 2010

Got back from holidays, and after copying my photos to my Windows I see (again) that the default Windows picture viewer doesn't respect the orientation flag in the EXIF tags.

I fired mogrify from imagemagick to rotate the pixels in the photos according to the EXIF orientation, so that stupid Windows can show them correctly

> mogrify -auto-orient *.JPG

So far so good, but then I suddenly I thought, is ImageMagick rotating JPEG photos loss less? For the non initiated, JPEG compress your photos very well, but at the expense of not storing exactly the color of each pixel. If you read and store again the photo, you're doing it probably with slightly different settings, so the program that stores the photo creates a new JPEG that stores the photo with more incorrect pixels. Even worse, it tries to reproduce the bad pixels the first encoding created, so the result is worse than what you expect. After a few transformations, what used to be a "slight invisible difference" between the original and the first JPEG, becomes a big drop in quality in the last JPEG. Most people regularly rotate several times the photos, resize them, and crop them. If after each step you save the photo and reload it, you're degrading the photo.

I did a quick test to check if ImageMagick rotates lossless-ly the photos, putting the rotated photo over the original in GIMP, choosing the difference filter. If everything is pitch black then it's lossless. I got something that looked black, but looking at the histogram showed that not everything was completely black. I then switched solid black to solid white, and the you can see the result here:


Arggh, so I lost a bit of quality on all my photos from my last trip to Paris. I then tried to make something constructive out of this, looked up this issue and the ImageMagick guys recommend to use jhead & jpegtrans to do lossless rotations, crop, resizes in JPEG images. These tools don't read the pixels and try to encode them again, they know how JPEG works are thus able to do these operations lossless-ly if you don't try to modify any of the JPEG blocks in the image (of 8x8 pixels).

So I tried it, and this time I tested it before I apply it to my new photos from Switzerland:

> jhead -autorot IMG_3778.JPG

And tried to see the difference again the GIMP. This time I got a much better histogram, but still not pitch black. Again, changing solid black but white showed some noise, more or less uniformly distributed over the image. Zooming in to 1:1 I got (cropped a 200x200 part of the image):


I don't understand this time why is there any difference at all. Anybody has any clue?

P.S.: To my girlfriend, that's why I'm so slow copying & selecting the photos from our trips...

Thursday, July 01, 2010

Performance bug in AppStats

I was trying to upload a CSV file with 100 entries to my local dev_appserver, but it was way too slow (30s). I first thought it was due to the datastore file implementation, and tried using the sqlite one, but it still took 30s to complete.

After a few hours putting time.clock all around the place, it turns out the problem was in AppStats. When AppStats collects all the info about a request, it stores in memcache the trace of every AppEngine API call. My code is like this:

@local_or_admin_required
def post(self):
# csv_data comes from FieldStorage, and these fields are not converted to
# Unicode by AppEngine / WebOb. So csv_data is a utf-8 bytestring.
csv_data = self.request.get('file_to_convert')
read_pos = csv.DictReader(csv_data.split('\n'))
generated_pos = [self.convert_press_office(po) for po in read_pos]
po_with_errors = []

if self.request.get('import_file_after_conversion') == 'true':
# Write the result to the datastore
for po in generated_pos:
name = po.name
del po.name

try:
update.add_press_office(name, **po)
except update.DuplicatedPressOfficeName:
...

add_press_office makes 3 API calls, so in total I'm making 300 API calls. For each call AppStats records the entire stack frame, with local variables. This includes csv_data, generated_pos, that are 50K and around 100K respectively. Total more than ~45M.

The 300 stack traces with pointers to local variables (csv_data, generated_pos, ...) are sharing the 50K and 100K of these variables, but as soon as you try to serialize it in a protocol buffer, it copies this information for each stack. And this happends in appstats/recording.py@356 in the function Recorder::get_both_protos_encoded. This function first encodes the full protocol buffer, and wastes 30s doing it. Then it discovers that "oh no! this thing is too big to keep it in memcache!" and it deletes the local variables from the stack traces. But at this point is too late and you already paid the 30s.

If you have this problem, add "appstats_MAX_LOCALS = 0" to your appengine_config.py, you will lose the value of local variables in AppStats but it will be faster.


Tuesday, May 04, 2010

Barcamp Málaga 2010

Al final me he dejado convencer y voy a organizar junto con Jose una Barcamp en Málaga. Sabemos seguro que habrá buen tiempo y bares para salir después de la barcamp, lo que pase en la barcamp ya es más incierto, pero parece que tendremos la asistencia de varios "startaperos", y que todos compartiremos nuestros secretillos (eso el que aún tenga alguno).

Si quieres saber lo que empresas que se crearon con dos duros en el bolsillo pero mucha ilusión y trabajo hicieron, entonces eres bienvenido, para más datos mira la web de la Barcamp en Málaga.

Sunday, April 06, 2008

Escalabilidad en el servidor

Al principio fue Apache, con un modo de funcionamiento extremadamente robusto. Cada petición se sirve en un proceso hijo distinto. Si algo va mal, el proceso hijo se colgaría, pero no afectaría en absoluto al resto de peticiones servidas por los otros procesos. Método lento, ya que crear un nuevo proceso es lento, pero seguro. Para mejorar el rendimiento se suele usar un "pool" (grupo) de procesos latentes, para no tener que crear un nuevo proceso con cada petición. Así nos ahorramos la creación de un nuevo proceso por petición.

Si queremos servir páginas web dinámicas y usamos Apache tenemos principalmente dos posibilidades, usar un "pool" de procesos PHP, qué comunicarán con Apache usando FastCGI, o usar ejecutar PHP directamente desde Apache con mod_php (en lugar de PHP/fastCGI podemos usar Python/SCGI o cualquier otro lenguaje). En el segundo caso, la comunicación entre Apache y el intérprete será más rápida ya que no necesita una comunicación entre procesos (Apache - PHP) por cada petición. Para servir una petición, Apache comunicará con uno de sus procesos libres del pool, y si es una petición dinámica y ese proceso aún no ha cargado mod_php, lo cargará y ejecutará el script correspondiente.

El numero máximo de recursos por segundo que podremos servir antes de agotar la memoría será:

nb_procesos_max = mem_total / mem_proc
recursos_seg = nb_procesos_max / seg_recurso

Donde mem_proc es la memoría que ocupa cada proceso Apache y seg_recurso es el tiempo medio que tardamos en servir un recurso.

Por ejemplo, si tenemos 2 GB de memoria, cada proceso consume 20 MB, y de media tardamos 0.1 segundos en servir una petición, podremos servir un máximo de 2000 / (20 * 0.1) = 1000 peticiones por segundo. Con este modelo, si queremos activar KeepAlives (ver el artículo sobre cómo acelerar la descarga de páginas web) durante 10 segundos, tendremos un límite de 2000 / (20 * 10) = 20 peticiones por segundo. ¡Hemos dividido por 100 nuestro máximo!

Para poder activar KeepAlives sin matar nuestro servidor, o simplemente si queremos consumir menos memoria, tenemos que cambiar de modelo. Sabemos que la mayoría de recursos son simples ficheros estáticos (js, css, png, jpg, etc.). No necesitamos un intérprete PHP para servirlos. De hecho, es tan sencillo que podemos hacerlo directamente desde el proceso principal del servidor web, sin necesidad de usar un proceso secundario ya que la posibilidad de que el proceso se cuelgue haciendo algo tan simple es prácticamente nula. Apache (<= 2.1) no puede funcionar en este modo, y tendremos que usar otro servidor Web, como lighttpd, nginx o cherokee.

Para los recursos dinámicos, podemos mantener un pool de procesos PHP y comunicar con ellos usando fastCGI, o simplemente seguir usando Apache y comunicar con Apache por HTTP (ideal para una migración rápida, ya que podemos seguir usando la configuración previa). La comunicacion entre servidor y PHP será algo más lenta, pero como ventaja desacoplamos el tiempo de vida de la conexión TCP con el del proceso PHP. Lo único que hay que hacer es pedirle a Apache que escuche en otro puerto (Listen 8080) y pedirle al servidor de recursos estáticos que actue de reverse proxy hacia localhost:8080 para todas las peticiones dinámicas.

La mayoría de servidores que permiten este modo de funcionamiento, permiten también usar un pool de procesos para servir los recursos estáticos, pero esta vez un pool pequeño y fijo (por ejemplo de 4 procesos). De este modo podemos aprovechar varios procesadores o un procesador multicore, y paralelizar las peticiones en caso de que alguna llamada al sistema bloquee (en teoría todas las llamadas que se usan son asíncronas, en la práctica a veces bloquean).

Consejos para acelar las páginas web - Parte II

Ya hemos hablado de la importancia de la latencia en el tiempo de descarga de las páginas web. Reducir el número de "three-way handshakes" activando KeepAlives, el número de ficheros a descargar, paralelizarlos sobre un máximo de 4 dominios, etc. son consejos importantes para reducir el impacto de la latencia, pero hay algo que sobrepasa a cualquier otra técnica para reducir el tiempo de espera cuando queremos usar un recurso (imágen, hoja de estilo, javascript, etc.), y es no mandarle nada al navegador, siempre que este tenga en caché una copia del recurso en cuestión.

El navegador divide los recursos que tiene en su caché en dos tipos, los "frescos" y los "dudosos". Los recursos frescos pueden usarse sin preguntarle nada al servidor. Los recursos dudosos se pueden usar, pero antes hay que preguntarle al servidor que nos lo envió si sigue siendo válido, o si hay que tirarlo a la basura y usar una nueva versión que el servidor le mandará al navegador.

¿Cómo controlar si un recurso entra en la caché, y si lo hace el tiempo qué permanece fresco? Con las cabeceras Cache-Control o Expires. La cabecera Expires contiene una fecha a partir de la cual el recurso debe ser considerado como dudoso. Si la fecha está en el pasado, o si no es una fecha (como "0"), se considerará inmediatamente al recurso como dudoso. La cabecera Cache-Control ofrece muchas más opciones, permitiendo por ejemplo especificar si un proxy que se encuentre entre el usuario y el servidor puede guardar el recurso en caché o si sólo el navegador puede hacerlo. Podéis leer la especificación para ver todas las subopciones de Cache-Control, siendo una de las más importantes max-age, que especifica durante cuantos segundos se tiene que considerar el recurso como fresco. Si se usa Cache-Control, toma prioridad sobre Expires.

Recordemos que si un recurso es fresco, el navegador lo usa directamente sin decirle nada al servidor. Más rápido imposible.

Si el recurso es dudoso, ¿cómo puede el navegador comprobar si ha cambiado en el servidor o no? La pregunta, en prácticamente todos los casos es completamente irrelevante. Es mejor que vuestros recursos siempre se mantengan frescos, poniendo un Expires o un Cache-Control max-age de muchos años. O están en la caché y pueden usarse directamente, o no están y hay que pedírselos al servidor.

¿Por qué muchas páginas los dejan pasar al estado "dudoso" (poniendo pequeños tiempos de expiración)? Porque quieren que si cambian el recurso, el navegador se baje la nueva versión. Esto es extremadamente problemático, porque en la práctica es imposible predecir cuando se va a cambiar un recurso, asi que no le podemos decir al navegador hasta cuando lo puede mantener fresco, o nos arriesgamos a que el navegador use una versión antigua de algunas de nuestras imágenes, páginas de estilo, javascript, etc.

Tenemos tres alternativas para atacar este problema:


  1. Ignorarlo. Es lo que hacen todos los que ponen un tiempo corto en Expires. Es incorrecto porque el cliente puede ver una nueva versión de nuestra página web con una mezcla de recursos antiguos y nuevos. Es ineficiente, porque hace que el cliente le haga una petición al servidor para comprobar la validez del recurso una vez caduca, requiriendo un mínimo de una ida y vuelta al servidor, y más típicamente dos idas y vueltas (ver el artículo anterior sobre la importancia de la latencia)

  2. Poner el Expires a 0. Es correcto, ya que el cliente siempre ve la última versión del recurso. Es ineficiente, ya que siempre es necesario comprobar la validez del recurso.

  3. Nunca cambiar un recurso. Más especificamente, cuando quieras crear una versión nueva de un recurso, no le des el mismo nombre que otro recurso ya existente en tu página web. Es correcto, ya que la versión en la caché y en el servidor de un recurso siempre es la misma. Es eficiente, ya que si se sigue usando ese recurso y está en la caché, el navegador nunca le pregunta al servidor.



La última opción es obviamente la ideal, y la que más trabajo necesita por parte del desarrollador, ergo es la opción menos usada. En realidad no es tan dificil de implementar. En lugar de poner en el HTML enlaces como "imagen.png", pondremos un enlace llamado "imagen.png?v=1". El servidor evidentemente ignorará el "?v=1", pero el navegador eso no lo sabe. Cuando actualicemos "imagen.png", cambiamos el número de versión, y en el HTML ponemos "imagen.png?v=2". Asi pues, en el mismo instante en que tengamos una versión nueva de "imagen.png" el navegador tendrá que pedírsela al servidor (él no tiene en su caché nada sobre "imagen.png?v=2"). Si queremos aumentar la legibilidad, o evitar que algún proxy no cachée nuestra imagen por usar un ? en la URL, podemos escribir imagen.v2.png, y eliminar el .v2 con mod_rewrite en Apache. Con un poco de esfuerzo, todo el proceso puede automatizarse. En Panoramio por ejemplo tenemos una tabla con el MD5 de todos los recursos estáticos qué usamos, y su número de versión (excepto para las imágenes que suben los usuarios). Cuándo subimos una nueva versión de Panoramio, un script comprueba el MD5 de todos los recursos, y si este cambia, aumentamos su número de versión. Todas las URLs a nuestros recursos las generamos a través de una función que le añade a la URL el número de versión que hemos calculado anteriormente. Todo esto se basa en que nuestro HTML no debe de estar cacheado, o los clientes puede recibir una versión antigua del HTML con una versión nueva de algunos recursos.

Si teneis algún recurso para el que este tipo de solución no es práctica, entonces tendreis que dejar que vuestros recursos pasen al estado dudoso (o no cachearlo en absoluto). Para comprobar si un recurso sigue siendo válido, el navegador le pregunta al servidor si ese recurso ha cambiado desde que se metió en la caché, usando la cabecera If-Modified-Since. Si no ha cambiado, el servidor responde con HTTP/304 Not Modified, y si ha cambiado responde enviando el nuevo contenido.

Si nuestro recurso no tiene una fecha de modificación clara (por ejemplo, si se ha generado dinámicamente), este método de verificación no nos servirá. Si ese es nuestro caso, al enviar el recurso la primera vez tendremos que añadir la cabecera ETag, que contendrá un identificador único que deberá cambiar cuando cambie el recurso. Los ETag que tanto Apache como IIS generan no dependen exclusivamente del contenido del fichero, si no que también dependen de datos especificos del servidor (por ejemplo, el inode donde se encuentra el fichero). Si usamos varios servidores, este ETag será inutilizable. En la práctica hay muy pocas razones para usar un ETag, siendo la validación por fecha suficiente en la mayoría de los casos.

En el anterior artículo de esta serie preguntasteis por herramientas para poder medir el tiempo de descarga de páginas web. Una formidable es Fiddler, desgraciadamente sólo disponible para Windows. Especificamente para Firefox tenemos firebug, sin duda el plugin de desarrollo más útil que existe para Firefox, y que incluye un panel donde podemos ver el tiempo que tarda cada petición al servidor, pero ¡cuidado al interpretar los resultados!, firebug muestra cuando las peticiones entran en la cola de peticiones de Firefox, no cuando se despacha la petición realmente al servidor.

El autor de Fiddler tiene un artículo donde habla también sobre el rendimiento de páginas web, y donde explica con algo más de detalle las diferentes cabeceras involucradas en todo el proceso de cacheo.

Queda bastante que hablar de este tema, sobre la compresión de los recursos en el servidor, y sobre todo de los distintos bugs en servidores y clientes que le ponen un poco de pimienta a la vida, pero ya es tarde. ¡Hasta la próxima!

Consejos para acelerar las páginas web - Parte I

La velocidad de carga de las páginas web viene determinada por muchos factores, pero los más importantes son la combinación de la latencia y el número de recursos a descargar, y por otro lado el ancho de banda y el tamaño de estos recursos.

Como diseñadores web, lo único que podemos hacer para disminuir la latencia entre nuestros servidores y nuestros usuarios es colocar los servidores lo más cerca posible de nuestros usuarios, o distribuirlos por todo el planeta si nuestra página web no tiene un uso mayoritario en un solo país. Pero están soluciones suelen ser muy caras y no están al alcance de todo el mundo.

En la práctica, la latencia y el ancho de banda del usuario lo tendremos que tomar como elementos incontrolables. Ya que el ancho de banda ha ido creciendo exponencialmente desde hace unos años, este elemento ya no tiene la importancia tan crítica que tenía hace tan solo unos años, y el tiempo de espera debido a la latencia y el número de recursos a descargar ha cobrado más importancia.

Veamos pues en detalle el proceso de descarga de una página web, y por qué afecta tanto la latencia al tiempo total de descarga. Cuando el navegador tiene que mostrar una página web nueva, digamos http://www.ejemplo.com/, primero tiene que convertir el nombre del dominio a una dirección IP con la que poder conectar, asi que contacta con los DNS de nuestro ISP para conseguir esta dirección IP.

Con la dirección IP en su mano, intenta establecer una conexión TCP con ese servidor, siguiendo el proceso conocido como “three-way handshake”. El navegador envía un paquete SYN y espera pacientemente la respuesta del servidor, que será un paquete SYN + ACK. Cuando el navegador reciba el paquete respuesta del servidor ya puede mandar un paquete ACK de respuesta e inmediatamente lanzar una petición HTTP GET del recurso que nos interesa (en nuestro ejemplo, “/”), a lo que el servidor responderá por ejemplo, mandando el contenido del fichero “index.html”.

Si el tiempo que tarda un paquete en llegar de nuestro navegador al servidor es de 100 ms, necesitaremos 400 ms debido a la latencia antes de que el navegador consiga el primer byte de contenido real, y ya podemos tener una flamante conexión a 20 Mb/s que no reducirá nada este tiempo de espera. A partir de aquí, y hasta que el navegador termina de recibir “index.html” pasará un tiempo dominado por el tamaño de este fichero y el ancho de banda de que dispongamos. Si “index.html” pesa 100 KB y tenemos una conexión de 4 Mb/s tardará unos (100 * 8) Kb / (4 * 1024) Kb/s * 1.2 = 235 ms. El factor “mágico” de 1.2 tiene cuenta de la sobrecarga típica de datos a enviar debido a la encapsulación de estos datos en paquetes.

Una vez con el contenido del HTML, el navegador procede a interpretarlo, y típicamente encontrará dentro del HTML referencias a otros recursos, como hojas de estilo CSS, Javascript, imágenes, etc. Para cada uno de estos recursos extra se repite todo el proceso anterior, asi pues si hacemos referencia a 10 recursos, un número muy conservador hoy en día, perderíamos 10 * 400 ms = 4 s extra debido a la latencia.

Afortunadamente el navegador paraleliza estas peticiones, pero para no sobrecargar al servidor no hace más de dos peticiones a la vez al mismo dominio. Mención especial merecen los ficheros Javascript, que no se descargan en paralelo. Si en nuestro HTML no usamos ningún fichero Javascript, los 10 recursos añadirán entonces 10 * 400 ms / 2 = 2 s extra.

¿Cómo podemos “engañar” al navegador para que descargue más ficheros en paralelo de nuestro servidor? Hay un truco muy sencillo, y es crear varios subdominios que apunten al mismo servidor, y dividir el enlace en el HTML a nuestros recursos entre estos subdominios. El navegador empezar a descargar en paralelo un máximo de 2 recursos por subdominio, sobre un máximo de 4 dominios simultáneamente. Haciendo esto el tiempo perdido estableciendo las conexiones TCP y empezando a descargar los recursos será de 0.8 s (400 ms para establecer las primeras 8 conexiones + 400 ms para las 2 últimas conexiones).

Hay una forma aún más sencilla de reducir este tiempo de espera. Si el servidor no cierra la conexión TCP con el cliente en cada petición HTTP, usando KeepAlives, dividimos por dos el efecto de la latencia en cada recurso que se descargue en una conexión ya abierta. El efecto de KeepAlives depende del número de subdominios que tengamos, si lo servimos todo desde el mismo dominio, en lugar de los 2 s anteriores tardaremos 400 ms para establecer las 2 primeras conexiones en paralelo + 200 ms por cada uno de los otros 8 recursos a bajar / 2 (número de recursos que se descargan en paralelo) = 400 + 200 * 4 = 1.2 s. Sobre 4 subdominios tendremos una espera usando KeepAlives de 0.6 s, ya que los 2 últimos recursos se descargan sin necesidad de establecer otra vez la conexión TCP.

Hay que tener en cuenta que en algunos servidores web la activación de KeepAlives tiene un impacto sobre el rendimiento muy significativo. Os recomiendo utilizar un servidor web alternativo, como lighttpd, nginx o cherokee al menos para vuestro contenido estático, con KeepAlives activado.

Un cambio que puede tener un efecto significativo en el tiempo de descarga es concatenar todos los ficheros CSS a descargar en un solo fichero (o 2, uno general al sitio y otro particular a la página), concatenar los ficheros Javascript, e incluso concatenar las imágenes, seleccionando la subimagen a mostrar usando CSS Sprites. Así podremos reducir significativamente el número de recursos a descargar.

Otro de los factores que sorprendentemente puede influir en el tiempo de descarga es el tamaño de las peticiones que hace el navegador. Cuando el navegador le pide al servidor una página, le envía también información extra, como el nombre del navegador, el idioma preferido por el usuario, las cookies, etc. Muchos de estos datos (como las cookies) no suelen influir en el resultado devuelto por el servidor para los ficheros CSS, JS o imágenes, y si nuestras cookies son cross-subdomain podemos evitar que el navegador tenga que mandarlas si colocamos los ficheros estáticos en otro dominio. Yahoo! por ejemplo usa el dominio yimg.com para servir este tipo de ficheros. A pesar de que las cookies son relativamente pequeñas, hemos de recordar que típicamente los usuarios navegan con conexiones asimétricas, donde la velocidad de subida puede ser 50 veces inferior a la velocidad de bajada de datos.

Una vez aplicados estos cambios en nuestras páginas, podremos concentrarnos en la segunda parte de mayor influencia en la velocidad, el tamaño de los recursos a descargar, y las distintas formas en que podemos influenciar la caché del navegador para evitar tener que descargar todo cada vez que un usuario llegue a nuestra página. Pero esa es otra historia de la que hablaremos otro día.

Thursday, August 18, 2005

Re: Ctrl+N in IE

Havoc comments on Internet Explorer showing the same page you're already viewing when you open a new window:

I'm pretty sure this is the only page on the Internet that I could not possibly want to see in the new window, since I already had it open.


Havoc is a smart guy, and usually he's right in the money, but he is soooo wrong on this one.

First, it's not always the case that you do not want to see the same page on the new window. You may want to check the bottom of the page to have a window with the footnotes, but still retain a window on the current reading position. But granted it's extremelly rare.

The thing that is invaluable is having the whole history of previous / next links on the new window. When you create a new window it's usually just because you want to fork your lecture. You have found two interesting links to follow on the same page, for instance. In no way is one of the windows more important than the other one to the user. Both are part of the current lecture “tree”.

All the Mozilla derivated I've seen handles new windows (or tabs) from a programmer point of view. They are just new, virgin, history-less windows. A pity.

Wednesday, August 17, 2005

¿Es ilegal compartir música con eDonkey / eMule / bittorrent / KaZaa?

La respuesta intuitiva a esta pregunta es que sí, ya conoceis el discurso oficial, pirateria, chicos malos, etc.

En pocos paises se ha puesto en duda que algo asi sea ilegal, siendo bastante controvertido el método usado contra “los culpables”.

Los EE.UU. fueron pioneros de los ataques en justicia hacia este tipo de programas, primero contra sus creadores. Estos no siempre fueron condenados, argumentando que no eran responsables de las obras que los usuarios copiaban con sus programas. Cuando este método falló, los ataques cambiaron de objetivo, pasando a ser contra cualquiera que compartiese música sin la conveniente autorización del autor.

¿Como atacar a millones de personas? No hace falta atacar a millones, solo tenían que seleccionar unos cientos para conseguir el efecto deseado. Nadie volvió a sentirse impune, y arrancar kazaa pasaba a ser como jugar a la ruleta rusa.

En Europa la ley es distinta, pero naturalmente a grandes rasgos se mantiene la idea de la de EE.UU. Protección del copyright y de los derechos de autor.

Después de esta disgresión, volvamos a casa, a España, donde una lectura ingenua de la LPI parece sacar fuera de la ley a
los usuarios de los programas P2P.

Un extracto de los artículos que son en mi opinión relevantes:


Artículo 17. Derecho exclusivo de explotación y sus modalidades

Corresponde al autor el ejercicio exclusivo de los derechos de explotación de su obra en cualquier forma y, en especial, los derechos de reproducción, distribución, comunicación pública y transformación, que no podrán ser realizadas sin su autorización, salvo en los casos previstos en la presente Ley.

Artículo 18. Reproducción

Se entiende por reproducción la fijación de la obra en un medio que permita su comunicación y la obtención de copias de toda o parte de ella.

Artículo 19. Distribución

1. Se entiende por distribución la puesta a disposición del público del original o copias de la obra mediante su venta, alquiler, préstamo o de cualquier otra forma.
[...]

Artículo 20. Comunicación pública

1. Se entenderá por comunicación pública todo acto por el cual una pluralidad de personas pueda tener acceso a la obra sin previa distribución de ejemplares a cada una de ellas.

No se considerará pública la comunicación cuando se celebre dentro de un ámbito estrictamente doméstico que no esté integrado o conectado a una red de difusión de cualquier tipo.
[...]

Artículo 31. Reproducción sin autorización

1. Las obras ya divulgadas podrán reproducirse sin autorización del autor y sin perjuicio en lo pertinente, de lo dispuesto en el artículo 34 de esta Ley, en los siguientes casos:
[...]
2.º Para uso privado del copista, sin perjuicio de lo dispuesto en los artículos 25 y 99.a) de esta Ley, y siempre que la copia no sea objeto de utilización colectiva ni lucrativa.
[...]


Asi pues siguiendo con análisis “intuitivo”, la ley da derecho exclusivo al autor para la “reproducción, distribución, comunicación pública” de su obra, añadiendo una excepción para la reproducción de la obra, siempre que esta no sea “objeto de utilización colectiva ni lucrativa”. Una copia de un CD para llevar en el coche, por ejemplo. Primer detalle curioso, no necesitas hacer la copia a partir de un “original”, y en caso de que asi lo hagas, el “original” no tiene porque ser tuyo.

Hay sin embargo un abogado español, David Bravo Bueno que sostiene que compartir música en un programa P2P no cae dentro de:

  1. la utilización colectiva de la reproducción de una obra
  2. ni la distribución
  3. ni la comunicación pública

David expone su tesis en su Copia este libro (página 105 en adelante).

Las restricciones del artículo 31 son, tal y como David explica:

  1. Que la obra esté divulgada.
  2. Que la copia lo sea para uso privado del copista.
  3. Que la obra no sea objeto de utilización colectiva.
  4. Que no sea objeto de utilización lucrativa.

La única restricción potencialmente problemática para los que usan un programa P2P es la de la utilización colectiva, ya que al mismo tiempo que una canción o película se descarga en el ordenador se esta también enviando a los demás usuarios.

Sin embargo la ley entiende “utilización” en el sentido de escuchar una canción, o ver una pelicula, pero no se está “utilizando” una obra por el mero hecho de servir de soporte a una copia.

Asi pues, nada viola el derecho de reproducción sin autorización del autor cuando se usa un programa P2P.

Sin embargo, y aqui es donde mi postura se diferencia de la de David, no comparto su argumentación sobre la “Distribución” (página 111). él dice:


Sin embargo, es de manual (literalmente) que el acto de distribución no puede darse nunca en Internet porque se refiere, exclusivamente, a ejemplares físicos de las obras. El libro “Manual de Propiedad Intelectual” coordinado por Rodrigo Bercovitz nos recuerda que “la distribución implica necesariamente la incorporación de la obra a un soporte físico que permita su comercialización pública a través de los medios señalados en el art. 19. El carácter físico del soporte exige la posibilidad de aprehensión del mismo por parte del público (…). Todos aquellos modos de explotación que no permitan la incorporación física de la obra no pueden ser considerados como de distribución (…). El aspecto fundamental del derecho de distribución es que la obra se incorpore a un soporte tangible, general y similar para el conjunto de integrantes del público”.


(Un pie de nota añade que en este aspecto la doctrina especializada es unánime.)

Hay algo en lo que no estoy de acuerdo. Si leemos detalladamente los artículos 19 y 18 (en este orden), vemos lo siguiente:


Se entiende por distribución la puesta a disposición del público de[...] copias de la obra.
Se entiende por reproducción la fijación de la obra en un medio que permita [...] la obtención de copias[...]


Según cuenta David, son estos dos artículos juntos los que llevan a la conclusión de que para que haya distribución, la obra se tiene que incorporar a un soporte físico. Mi educación es la de un físico y no un abogado, pero volviendo a mis orígenes cartesianos, yo no veo en la descripción de la reproducción una claúsula que diga que solo se pueden obtener copias a través de la fijación de la obra en un medio.

Es decir, el enúnciado del artículo 18 no parece dar una forma exclusiva a la obtención de copias, y a partir de ahi, la deducción de que las copias a las que se refiere el artículo 19 tengan que tener un soporte material me parece errónea.

David comenta también que utilizar un programa P2P tampoco infringe el derecho exclusivo del autor a la “Comunicación pública” de la obra. Su argumento es el siguiente:


Las redes Peer to Peer establecen conexiones exclusivamente entre dos personas. Son redes “punto a punto” y resulta difícil calificar como comunicación pública un sistema que carece de público.


Esto es falso. Se puede traducir Peer to Peer como se quiera, pero
eso no hace que la comunicación sea exclusivamente entre dos personas. El término Peer to Peer tiene origen en la ausencia de un servidor en la red, asi pues todos los nodos de la red tienen el mismo estatus, y las comunicaciones se realizan entre “pares” (ningún comunicante tiene un estatus más elevado que el otro).

En general, un usuario conectado en una red P2P envia sus obras compartidas a varios usuarios simúltaneamente. Cuando un usuario A carga una de las obras de otro usuario B de la misma red, nada impide que A no sea el único “cliente” de B.

Para finalizar, y ya que David comenta un caso judicial francés para apoyar su tesis de que cargar obras por internet es legal[1], mencionar que ha habido numerosas condenas en Francia a usuarios de programas P2P, a pesar de que no tenían animo de lucro.

Nota: No soy abogado, ni lo parezco. Solo soy un ciudadano interesado por saber si puede usar los programas P2P con la conciencia tranquila, o no. Ha habido decisiones de justicia contradictorias en este sentido en Francia, pero la gran mayoría apuntan a que no es legal. La opinión de los abogados también es contradictoria, pero la inmensa mayoría también parecen estar de acuerdo en que no es legal.

La ley francesa y la española en este sentido se parecen como dos gotas de agua, pero el lenguaje utilizado es lo suficientemente sutil como para ignorar todo lo que se salga de la LPI.

En Francia no tengo muchas dudas de que no debo usar un programa P2P, y la única forma que me queda de sacar una conclusión al respecto es leer por mi mismo la ley, ya que la opinión de los expertos es contradictoria.

Si alguien quiere aportar alguna corrección a este artículo, que me la mande y la añadiré gustosamente.



1: Tesis con la que estoy de acuerdo. Con lo que no estoy de acuerdo es con que enviar obras por internet también sea legal.

Tuesday, August 16, 2005

Fade in and out

Playing with the effects in script.aculo.us I added a new little effect to switch from an element to another one smoothly.

Better see it in action, go to Panoramio, register as a new user, and see the little fade out of “Login     Register” and the fade in of “Hi Foo Bar”.

Sure enough, it was not working on IE. It's a documented problem, the Opacity effect didn't work on elements without layout. The “layout” is some kind of karma that IE throws on certain elements, and you can activate it manually if you use the right CSS incantation.

So here we go, I put height: 1px; to my CSS elements to no joy, it still didn't work. Let's try zoom: 1... on the money! My page didn't validate anyway (and no, I don't care) so I'm sell on zoom.

But now I have another problem. If you have cleartype activated the result is ugly beyond belief. Pssst. Well, nobody said that's going to be easy. Another google search, and we see this lovely blogger giving us a work-around. Just put an explicit background color to this element. Luckly enough I'm using an uniform background for this part of the site, so I just added a background-color: white; and now I have something that works like a charm at the very least on IE6 and on Firefox.

I can now take my breakfast.