Canonical Voices

Posts tagged with 'ubuntuone'

Martin Albisetti

The Ubuntu One team are feeling the joys of Spring, because after several months working hard we’ve got some great news about updates to our Android Music app, which don’t forget works anywhere in the world!

The first thing you’ll notice in the new UI is album art so you will now see any saved album covers. Managing your playlists is now even easier as you can create, edit and delete playlists straight from your device. Those of you with lots of music will notice the overall speed improvement, meaning you can enjoy your huge music collection without any long waits. Plus, we have also added support for non-DRM iTunes songs so that you can stream songs you’ve purchased from iTunes just as easily as your MP3s, bringing all your music together.

Album view Playlist view Playing paylist

In addition many of you requested this next feature so we’re sure you’ll be pleased that we now support songs in Ogg Vorbis format so you can stream your collection of Ogg music natively, without the need to convert it to another format. If you’re a developer you may be interested in knowing that playlists are stored in your CouchDB database allowing you to write applications that read/write to them.

So that’s faster access to more of your music wherever you are in the world. The latest version 1.2 is now available in the Android market, happy listening and watch this space for upcoming updates to our iPhone app.

Enjoy!

- Martin and the web & mobile team

Read more
Cristian Parrino

Habemus Logo!

These are exciting times for Ubuntu One. Our sync services have become a reliable part of our users’ daily activities, people are raving over our new music streaming service, and we’re close to launching full-fledged clients for Windows and Android – effectively exposing the extended world of Ubuntu to millions of users for the first time.

We decided to dress ourselves up for the occasion… so our design team jumped at the opportunity and did a fantastic job in coming up with a vibrant new look that captures the choice, convenience and security benefits delivered by Ubuntu One.

Here’s a not-so-sneak preview (roll out is happening as I type):

Ubuntu One web site preview - click for larger image

We’re also thrilled about the “iconology” representing each Ubuntu One service:


File Sync
– the easiest way to keep your files safe and accessible anywhere, anytime – for free on all platforms supported (2GB of storage also included). Currently available on Ubuntu and Windows (in Beta) computers, on the Web and very soon on Android mobile devices.


Music Streaming
our most popular premium service allows you to take your entire music collection wherever you go, without the inconvenience of tethering mobile phones or worrying about local storage. Supports .mp3 and *very soon* iTunes (.mp4) and .ogg formats.


Contacts Sync
– We’re revamping our Contacts Sync service! Soon, you will be able to sync your Facebook friends, Gmail and mobile phone contacts – with brand new contacts management functionality available from our Web UI. Integration with Evolution Mail on the Ubuntu desktop is also supported.


Music Store
– Buy music from the most popular labels and artists and get it delivered directly to your personal cloud. Available only on Ubuntu computers, integrated with the Banshee and Rhythmbox music players.

Bookmarks sync icon


Bookmarks Sync
– a convenient way to keep your Firefox bookmarks in sync across Ubuntu computers.


Notes Sync
– If you’re an avid user of Tomboy Notes, sync your notes and access them via the Web UI or across your Ubuntu computers. Available only on Ubuntu computers, integrated with the Tomboy Notes application.

Now that we’re all dressed up, look out for a number of exciting changes to our Web UI, Ubuntu and Windows control panels as a result of an intensive week of user testing – but more to come on this soon…

Read more
Joshua Hoover

Hi, I’m Phil. I work in the Online Services group at Canonical in the Operations and Foundations group. We work on keeping Ubuntu One up and humming along and improving its core technologies.

I wanted to take a moment and apologize for the extension of our planned downtime on Tuesday morning. I was unable to anticipate the problem and since it happened roughly 3/4 of the way through the total process it wasn’t possible to roll back and restore the service in its previous state.

We had run through the upgrade process (database servers upgraded to 10.04, Postgres upgraded from 8.3 to 8.4, and a series of database patches rolled up and applied) across all our database servers and everything ran well within the time window that we defined in our initial outage announcement. During the production upgrade, the last storage shard spent a surprisingly long time re-adding a foreign key constraint. I spent longer than I would have liked hoping to wait the process out, thinking that we’d spend less total downtime vs. starting that import over from scratch. That didn’t end up being a smart decision.

I eventually reached the conclusion that this process wasn’t going to complete, and the import process was restarted. Two hours later the import was complete, all servers were restarted, and the service was restored to 100% functionality. Every developer and admin involved heaved a pretty serious sigh of relief.

So what did I do wrong, or what could we do better next time? First, I’m going to do a lot better job scheduling downtime going forward. This was scheduled for a low-traffic period where we had standard developer and admin coverage on a weekday; that gave us a small low-traffic window and with the unexpected increase in process time, we quickly ran into prime time. I should have scheduled it for a Sunday evening, giving us a much longer low-traffic window to work in where a minimum of users would be disrupted.

Second, we’ll do a better job of trusting our math. It was clear something was wrong much earlier than when we finally we pulled the trigger on restarting the process; I could have saved a couple of hours by trusting our initial analysis.

Finally, we’ll continue to work hard to extend our architecture to remove downtime and perform rolling upgrades. Perhaps zero downtime is an unrealistic expectation, but I’m going to make sure we get as close to that as we can.

Thanks so much for your patience and I hope you continue to enjoy the features we’ve added recently and have upcoming for Ubuntu One.

- Philip Fibiger

Read more
mandel

Before I introduce the code, let me say that this is not a 100% exact implementation of the interfaces that can be found in pyinotify but the implementation of a subset that matches my needs. The main idea of creating this post is to give an example of the implementation of such a library for Windows trying to reuse the code that can be found in pyinotify.

Once I have excused my self, let get into the code. First of all, there are a number of classes from pyinotify that we can use in our code. That subset of classes is the below code which I grabbed from pyinotify git:

#!/usr/bin/env python
 
# pyinotify.py - python interface to inotify
# Copyright (c) 2010 Sebastien Martini <seb@dbzteam.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Platform agnostic code grabed from pyinotify."""
import logging
import os
 
COMPATIBILITY_MODE = False
 
 
class RawOutputFormat:
    """
    Format string representations.
    """
    def __init__(self, format=None):
        self.format = format or {}
 
    def simple(self, s, attribute):
        if not isinstance(s, str):
            s = str(s)
        return (self.format.get(attribute, '') + s +
                self.format.get('normal', ''))
 
    def punctuation(self, s):
        """Punctuation color."""
        return self.simple(s, 'normal')
 
    def field_value(self, s):
        """Field value color."""
        return self.simple(s, 'purple')
 
    def field_name(self, s):
        """Field name color."""
        return self.simple(s, 'blue')
 
    def class_name(self, s):
        """Class name color."""
        return self.format.get('red', '') + self.simple(s, 'bold')
 
output_format = RawOutputFormat()
 
 
class EventsCodes:
    """
    Set of codes corresponding to each kind of events.
    Some of these flags are used to communicate with inotify, whereas
    the others are sent to userspace by inotify notifying some events.
 
    @cvar IN_ACCESS: File was accessed.
    @type IN_ACCESS: int
    @cvar IN_MODIFY: File was modified.
    @type IN_MODIFY: int
    @cvar IN_ATTRIB: Metadata changed.
    @type IN_ATTRIB: int
    @cvar IN_CLOSE_WRITE: Writtable file was closed.
    @type IN_CLOSE_WRITE: int
    @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
    @type IN_CLOSE_NOWRITE: int
    @cvar IN_OPEN: File was opened.
    @type IN_OPEN: int
    @cvar IN_MOVED_FROM: File was moved from X.
    @type IN_MOVED_FROM: int
    @cvar IN_MOVED_TO: File was moved to Y.
    @type IN_MOVED_TO: int
    @cvar IN_CREATE: Subfile was created.
    @type IN_CREATE: int
    @cvar IN_DELETE: Subfile was deleted.
    @type IN_DELETE: int
    @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
    @type IN_DELETE_SELF: int
    @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
    @type IN_MOVE_SELF: int
    @cvar IN_UNMOUNT: Backing fs was unmounted.
    @type IN_UNMOUNT: int
    @cvar IN_Q_OVERFLOW: Event queued overflowed.
    @type IN_Q_OVERFLOW: int
    @cvar IN_IGNORED: File was ignored.
    @type IN_IGNORED: int
    @cvar IN_ONLYDIR: only watch the path if it is a directory (new
                      in kernel 2.6.15).
    @type IN_ONLYDIR: int
    @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
                          IN_ONLYDIR we can make sure that we don't watch
                          the target of symlinks.
    @type IN_DONT_FOLLOW: int
    @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
                       in kernel 2.6.14).
    @type IN_MASK_ADD: int
    @cvar IN_ISDIR: Event occurred against dir.
    @type IN_ISDIR: int
    @cvar IN_ONESHOT: Only send event once.
    @type IN_ONESHOT: int
    @cvar ALL_EVENTS: Alias for considering all of the events.
    @type ALL_EVENTS: int
    """
 
    # The idea here is 'configuration-as-code' - this way, we get
    # our nice class constants, but we also get nice human-friendly text
    # mappings to do lookups against as well, for free:
    FLAG_COLLECTIONS = {'OP_FLAGS': {
        'IN_ACCESS'        : 0x00000001,  # File was accessed
        'IN_MODIFY'        : 0x00000002,  # File was modified
        'IN_ATTRIB'        : 0x00000004,  # Metadata changed
        'IN_CLOSE_WRITE'   : 0x00000008,  # Writable file was closed
        'IN_CLOSE_NOWRITE' : 0x00000010,  # Unwritable file closed
        'IN_OPEN'          : 0x00000020,  # File was opened
        'IN_MOVED_FROM'    : 0x00000040,  # File was moved from X
        'IN_MOVED_TO'      : 0x00000080,  # File was moved to Y
        'IN_CREATE'        : 0x00000100,  # Subfile was created
        'IN_DELETE'        : 0x00000200,  # Subfile was deleted
        'IN_DELETE_SELF'   : 0x00000400,  # Self (watched item itself)
                                          # was deleted
        'IN_MOVE_SELF'     : 0x00000800,  # Self(watched item itself) was moved
        },
                        'EVENT_FLAGS': {
        'IN_UNMOUNT'       : 0x00002000,  # Backing fs was unmounted
        'IN_Q_OVERFLOW'    : 0x00004000,  # Event queued overflowed
        'IN_IGNORED'       : 0x00008000,  # File was ignored
        },
                        'SPECIAL_FLAGS': {
        'IN_ONLYDIR'       : 0x01000000,  # only watch the path if it is a
                                          # directory
        'IN_DONT_FOLLOW'   : 0x02000000,  # don't follow a symlink
        'IN_MASK_ADD'      : 0x20000000,  # add to the mask of an already
                                          # existing watch
        'IN_ISDIR'         : 0x40000000,  # event occurred against dir
        'IN_ONESHOT'       : 0x80000000,  # only send event once
        },
                        }
 
    def maskname(mask):
        """
        Returns the event name associated to mask. IN_ISDIR is appended to
        the result when appropriate. Note: only one event is returned, because
        only one event can be raised at a given time.
 
        @param mask: mask.
        @type mask: int
        @return: event name.
        @rtype: str
        """
        ms = mask
        name = '%s'
        if mask & IN_ISDIR:
            ms = mask - IN_ISDIR
            name = '%s|IN_ISDIR'
        return name % EventsCodes.ALL_VALUES[ms]
 
    maskname = staticmethod(maskname)
 
 
# So let's now turn the configuration into code
EventsCodes.ALL_FLAGS = {}
EventsCodes.ALL_VALUES = {}
for flagc, valc in EventsCodes.FLAG_COLLECTIONS.items():
    # Make the collections' members directly accessible through the
    # class dictionary
    setattr(EventsCodes, flagc, valc)
 
    # Collect all the flags under a common umbrella
    EventsCodes.ALL_FLAGS.update(valc)
 
    # Make the individual masks accessible as 'constants' at globals() scope
    # and masknames accessible by values.
    for name, val in valc.items():
        globals()[name] = val
        EventsCodes.ALL_VALUES[val] = name
 
 
# all 'normal' events
ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.values())
EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
 
 
class _Event:
    """
    Event structure, represent events raised by the system. This
    is the base class and should be subclassed.
 
    """
    def __init__(self, dict_):
        """
        Attach attributes (contained in dict_) to self.
 
        @param dict_: Set of attributes.
        @type dict_: dictionary
        """
        for tpl in dict_.items():
            setattr(self, *tpl)
 
    def __repr__(self):
        """
        @return: Generic event string representation.
        @rtype: str
        """
        s = ''
        for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]):
            if attr.startswith('_'):
                continue
            if attr == 'mask':
                value = hex(getattr(self, attr))
            elif isinstance(value, basestring) and not value:
                value = "''"
            s += ' %s%s%s' % (output_format.field_name(attr),
                              output_format.punctuation('='),
                              output_format.field_value(value))
 
        s = '%s%s%s %s' % (output_format.punctuation('<'),
                           output_format.class_name(self.__class__.__name__),
                           s,
                           output_format.punctuation('>'))
        return s
 
    def __str__(self):
        return repr(self)
 
 
class _RawEvent(_Event):
    """
    Raw event, it contains only the informations provided by the system.
    It doesn't infer anything.
    """
    def __init__(self, wd, mask, cookie, name):
        """
        @param wd: Watch Descriptor.
        @type wd: int
        @param mask: Bitmask of events.
        @type mask: int
        @param cookie: Cookie.
        @type cookie: int
        @param name: Basename of the file or directory against which the
                     event was raised in case where the watched directory
                     is the parent directory. None if the event was raised
                     on the watched item itself.
        @type name: string or None
        """
        # Use this variable to cache the result of str(self), this object
        # is immutable.
        self._str = None
        # name: remove trailing '\0'
        d = {'wd': wd,
             'mask': mask,
             'cookie': cookie,
             'name': name.rstrip('\0')}
        _Event.__init__(self, d)
        logging.debug(str(self))
 
    def __str__(self):
        if self._str is None:
            self._str = _Event.__str__(self)
        return self._str
 
 
class Event(_Event):
    """
    This class contains all the useful informations about the observed
    event. However, the presence of each field is not guaranteed and
    depends on the type of event. In effect, some fields are irrelevant
    for some kind of event (for example 'cookie' is meaningless for
    IN_CREATE whereas it is mandatory for IN_MOVE_TO).
 
    The possible fields are:
      - wd (int): Watch Descriptor.
      - mask (int): Mask.
      - maskname (str): Readable event name.
      - path (str): path of the file or directory being watched.
      - name (str): Basename of the file or directory against which the
              event was raised in case where the watched directory
              is the parent directory. None if the event was raised
              on the watched item itself. This field is always provided
              even if the string is ''.
      - pathname (str): Concatenation of 'path' and 'name'.
      - src_pathname (str): Only present for IN_MOVED_TO events and only in
              the case where IN_MOVED_FROM events are watched too. Holds the
              source pathname from where pathname was moved from.
      - cookie (int): Cookie.
      - dir (bool): True if the event was raised against a directory.
 
    """
    def __init__(self, raw):
        """
        Concretely, this is the raw event plus inferred infos.
        """
        _Event.__init__(self, raw)
        self.maskname = EventsCodes.maskname(self.mask)
        if COMPATIBILITY_MODE:
            self.event_name = self.maskname
        try:
            if self.name:
                self.pathname = os.path.abspath(os.path.join(self.path,
                                                             self.name))
            else:
                self.pathname = os.path.abspath(self.path)
        except AttributeError, err:
            # Usually it is not an error some events are perfectly valids
            # despite the lack of these attributes.
            logging.debug(err)
 
 
class _ProcessEvent:
    """
    Abstract processing event class.
    """
    def __call__(self, event):
        """
        To behave like a functor the object must be callable.
        This method is a dispatch method. Its lookup order is:
          1. process_MASKNAME method
          2. process_FAMILY_NAME method
          3. otherwise calls process_default
 
        @param event: Event to be processed.
        @type event: Event object
        @return: By convention when used from the ProcessEvent class:
                 - Returning False or None (default value) means keep on
                 executing next chained functors (see chain.py example).
                 - Returning True instead means do not execute next
                   processing functions.
        @rtype: bool
        @raise ProcessEventError: Event object undispatchable,
                                  unknown event.
        """
        stripped_mask = event.mask - (event.mask & IN_ISDIR)
        maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
        if maskname is None:
            raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
 
        # 1- look for process_MASKNAME
        meth = getattr(self, 'process_' + maskname, None)
        if meth is not None:
            return meth(event)
        # 2- look for process_FAMILY_NAME
        meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
        if meth is not None:
            return meth(event)
        # 3- default call method process_default
        return self.process_default(event)
 
    def __repr__(self):
        return '<%s>' % self.__class__.__name__
 
 
class ProcessEvent(_ProcessEvent):
    """
    Process events objects, can be specialized via subclassing, thus its
    behavior can be overriden:
 
    Note: you should not override __init__ in your subclass instead define
    a my_init() method, this method will be called automatically from the
    constructor of this class with its optionals parameters.
 
      1. Provide specialized individual methods, e.g. process_IN_DELETE for
         processing a precise type of event (e.g. IN_DELETE in this case).
      2. Or/and provide methods for processing events by 'family', e.g.
         process_IN_CLOSE method will process both IN_CLOSE_WRITE and
         IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
         process_IN_CLOSE_NOWRITE aren't defined though).
      3. Or/and override process_default for catching and processing all
         the remaining types of events.
    """
    pevent = None
 
    def __init__(self, pevent=None, **kargs):
        """
        Enable chaining of ProcessEvent instances.
 
        @param pevent: Optional callable object, will be called on event
                       processing (before self).
        @type pevent: callable
        @param kargs: This constructor is implemented as a template method
                      delegating its optionals keyworded arguments to the
                      method my_init().
        @type kargs: dict
        """
        self.pevent = pevent
        self.my_init(**kargs)
 
    def my_init(self, **kargs):
        """
        This method is called from ProcessEvent.__init__(). This method is
        empty here and must be redefined to be useful. In effect, if you
        need to specifically initialize your subclass' instance then you
        just have to override this method in your subclass. Then all the
        keyworded arguments passed to ProcessEvent.__init__() will be
        transmitted as parameters to this method. Beware you MUST pass
        keyword arguments though.
 
        @param kargs: optional delegated arguments from __init__().
        @type kargs: dict
        """
        pass
 
    def __call__(self, event):
        stop_chaining = False
        if self.pevent is not None:
            # By default methods return None so we set as guideline
            # that methods asking for stop chaining must explicitely
            # return non None or non False values, otherwise the default
            # behavior will be to accept chain call to the corresponding
            # local method.
            stop_chaining = self.pevent(event)
        if not stop_chaining:
            return _ProcessEvent.__call__(self, event)
 
    def nested_pevent(self):
        return self.pevent
 
    def process_IN_Q_OVERFLOW(self, event):
        """
        By default this method only reports warning messages, you can
        overredide it by subclassing ProcessEvent and implement your own
        process_IN_Q_OVERFLOW method. The actions you can take on receiving
        this event is either to update the variable max_queued_events in order
        to handle more simultaneous events or to modify your code in order to
        accomplish a better filtering diminishing the number of raised events.
        Because this method is defined, IN_Q_OVERFLOW will never get
        transmitted as arguments to process_default calls.
 
        @param event: IN_Q_OVERFLOW event.
        @type event: dict
        """
        log.warning('Event queue overflowed.')
 
    def process_default(self, event):
        """
        Default processing event method. By default does nothing. Subclass
        ProcessEvent and redefine this method in order to modify its behavior.
 
        @param event: Event to be processed. Can be of any type of events but
                      IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
        @type event: Event instance
        """
        pass
 
 
class PrintAllEvents(ProcessEvent):
    """
    Dummy class used to print events strings representations. For instance this
    class is used from command line to print all received events to stdout.
    """
    def my_init(self, out=None):
        """
        @param out: Where events will be written.
        @type out: Object providing a valid file object interface.
        """
        if out is None:
            out = sys.stdout
        self._out = out
 
    def process_default(self, event):
        """
        Writes event string representation to file object provided to
        my_init().
 
        @param event: Event to be processed. Can be of any type of events but
                      IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
        @type event: Event instance
        """
        self._out.write(str(event))
        self._out.write('\n')
        self._out.flush()
 
 
class WatchManagerError(Exception):
    """
    WatchManager Exception. Raised on error encountered on watches
    operations.
 
    """
    def __init__(self, msg, wmd):
        """
        @param msg: Exception string's description.
        @type msg: string
        @param wmd: This dictionary contains the wd assigned to paths of the
                    same call for which watches were successfully added.
        @type wmd: dict
        """
        self.wmd = wmd
        Exception.__init__(self, msg)

Unfortunatly we need to implement the code that talks with the Win32 API to be able to retrieve the events in the file system. In my design this is done by the Watch class that looks like this:

# Author: Manuel de la Pena <manuel@canonical.com>
#
# Copyright 2011 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
"""File notifications on windows."""
 
import logging
import os
import re
 
import winerror
 
from Queue import Queue, Empty
from threading import Thread
from uuid import uuid4
from twisted.internet import task, reactor
from win32con import (
    FILE_SHARE_READ,
    FILE_SHARE_WRITE,
    FILE_FLAG_BACKUP_SEMANTICS,
    FILE_NOTIFY_CHANGE_FILE_NAME,
    FILE_NOTIFY_CHANGE_DIR_NAME,
    FILE_NOTIFY_CHANGE_ATTRIBUTES,
    FILE_NOTIFY_CHANGE_SIZE,
    FILE_NOTIFY_CHANGE_LAST_WRITE,
    FILE_NOTIFY_CHANGE_SECURITY,
    OPEN_EXISTING
)
from win32file import CreateFile, ReadDirectoryChangesW
from ubuntuone.platform.windows.pyinotify import (
    Event,
    WatchManagerError,
    ProcessEvent,
    PrintAllEvents,
    IN_OPEN,
    IN_CLOSE_NOWRITE,
    IN_CLOSE_WRITE,
    IN_CREATE,
    IN_ISDIR,
    IN_DELETE,
    IN_MOVED_FROM,
    IN_MOVED_TO,
    IN_MODIFY,
    IN_IGNORED
)
from ubuntuone.syncdaemon.filesystem_notifications import (
    GeneralINotifyProcessor
)
from ubuntuone.platform.windows.os_helper import (
    LONG_PATH_PREFIX,
    abspath,
    listdir
)
 
# constant found in the msdn documentation:
# http://msdn.microsoft.com/en-us/library/ff538834(v=vs.85).aspx
FILE_LIST_DIRECTORY = 0x0001
FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020
FILE_NOTIFY_CHANGE_CREATION = 0x00000040
 
# a map between the few events that we have on windows and those
# found in pyinotify
WINDOWS_ACTIONS = {
  1: IN_CREATE,
  2: IN_DELETE,
  3: IN_MODIFY,
  4: IN_MOVED_FROM,
  5: IN_MOVED_TO
}
 
# translates quickly the event and it's is_dir state to our standard events
NAME_TRANSLATIONS = {
    IN_OPEN: 'FS_FILE_OPEN',
    IN_CLOSE_NOWRITE: 'FS_FILE_CLOSE_NOWRITE',
    IN_CLOSE_WRITE: 'FS_FILE_CLOSE_WRITE',
    IN_CREATE: 'FS_FILE_CREATE',
    IN_CREATE | IN_ISDIR: 'FS_DIR_CREATE',
    IN_DELETE: 'FS_FILE_DELETE',
    IN_DELETE | IN_ISDIR: 'FS_DIR_DELETE',
    IN_MOVED_FROM: 'FS_FILE_DELETE',
    IN_MOVED_FROM | IN_ISDIR: 'FS_DIR_DELETE',
    IN_MOVED_TO: 'FS_FILE_CREATE',
    IN_MOVED_TO | IN_ISDIR: 'FS_DIR_CREATE',
}
 
# the default mask to be used in the watches added by the FilesystemMonitor
# class
FILESYSTEM_MONITOR_MASK = FILE_NOTIFY_CHANGE_FILE_NAME | \
    FILE_NOTIFY_CHANGE_DIR_NAME | \
    FILE_NOTIFY_CHANGE_ATTRIBUTES | \
    FILE_NOTIFY_CHANGE_SIZE | \
    FILE_NOTIFY_CHANGE_LAST_WRITE | \
    FILE_NOTIFY_CHANGE_SECURITY | \
    FILE_NOTIFY_CHANGE_LAST_ACCESS
 
 
# The implementation of the code that is provided as the pyinotify
# substitute
class Watch(object):
    """Implement the same functions as pyinotify.Watch."""
 
    def __init__(self, watch_descriptor, path, mask, auto_add,
        events_queue=None, exclude_filter=None, proc_fun=None):
        super(Watch, self).__init__()
        self.log = logging.getLogger('ubuntuone.platform.windows.' +
            'filesystem_notifications.Watch')
        self._watching = False
        self._descriptor = watch_descriptor
        self._auto_add = auto_add
        self.exclude_filter = None
        self._proc_fun = proc_fun
        self._cookie = None
        self._source_pathname = None
        # remember the subdirs we have so that when we have a delete we can
        # check if it was a remove
        self._subdirs = []
        # ensure that we work with an abspath and that we can deal with
        # long paths over 260 chars.
        self._path = os.path.abspath(path)
        if not self._path.startswith(LONG_PATH_PREFIX):
            self._path = LONG_PATH_PREFIX + self._path
        self._mask = mask
        # lets make the q as big as possible
        self._raw_events_queue = Queue()
        if not events_queue:
            events_queue = Queue()
        self.events_queue = events_queue
 
    def _path_is_dir(self, path):
        """"Check if the path is a dir and update the local subdir list."""
        self.log.debug('Testing if path "%s" is a dir', path)
        is_dir = False
        if os.path.exists(path):
            is_dir = os.path.isdir(path)
        else:
            self.log.debug('Path "%s" was deleted subdirs are %s.',
                path, self._subdirs)
            # we removed the path, we look in the internal list
            if path in self._subdirs:
                is_dir = True
                self._subdirs.remove(path)
        if is_dir:
            self.log.debug('Adding %s to subdirs %s', path, self._subdirs)
            self._subdirs.append(path)
        return is_dir
 
    def _process_events(self):
        """Process the events form the queue."""
        # we transform the events to be the same as the one in pyinotify
        # and then use the proc_fun
        while self._watching or not self._raw_events_queue.empty():
            file_name, action = self._raw_events_queue.get()
            # map the windows events to the pyinotify ones, tis is dirty but
            # makes the multiplatform better, linux was first :P
            is_dir = self._path_is_dir(file_name)
            if os.path.exists(file_name):
                is_dir = os.path.isdir(file_name)
            else:
                # we removed the path, we look in the internal list
                if file_name in self._subdirs:
                    is_dir = True
                    self._subdirs.remove(file_name)
            if is_dir:
                self._subdirs.append(file_name)
            mask = WINDOWS_ACTIONS[action]
            head, tail = os.path.split(file_name)
            if is_dir:
                mask |= IN_ISDIR
            event_raw_data = {
                'wd': self._descriptor,
                'dir': is_dir,
                'mask': mask,
                'name': tail,
                'path': head.replace(self.path, '.')
            }
            # by the way in which the win api fires the events we know for
            # sure that no move events will be added in the wrong order, this
            # is kind of hacky, I dont like it too much
            if WINDOWS_ACTIONS[action] == IN_MOVED_FROM:
                self._cookie = str(uuid4())
                self._source_pathname = tail
                event_raw_data['cookie'] = self._cookie
            if WINDOWS_ACTIONS[action] == IN_MOVED_TO:
                event_raw_data['src_pathname'] = self._source_pathname
                event_raw_data['cookie'] = self._cookie
            event = Event(event_raw_data)
            # FIXME: event deduces the pathname wrong and we need manually
            # set it
            event.pathname = file_name
            # add the event only if we do not have an exclude filter or
            # the exclude filter returns False, that is, the event will not
            # be excluded
            if not self.exclude_filter or not self.exclude_filter(event):
                self.log.debug('Addding event %s to queue.', event)
                self.events_queue.put(event)
 
    def _watch(self):
        """Watch a path that is a directory."""
        # we are going to be using the ReadDirectoryChangesW whihc requires
        # a direcotry handle and the mask to be used.
        handle = CreateFile(
            self._path,
            FILE_LIST_DIRECTORY,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            None,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS,
            None
        )
        self.log.debug('Watchng path %s.', self._path)
        while self._watching:
            # important information to know about the parameters:
            # param 1: the handle to the dir
            # param 2: the size to be used in the kernel to store events
            # that might be lost whilw the call is being performed. This
            # is complicates to fine tune since if you make lots of watcher
            # you migh used to much memory and make your OS to BSOD
            results = ReadDirectoryChangesW(
                handle,
                1024,
                self._auto_add,
                self._mask,
                None,
                None
            )
            # add the diff events to the q so that the can be processed no
            # matter the speed.
            for action, file in results:
                full_filename = os.path.join(self._path, file)
                self._raw_events_queue.put((full_filename, action))
                self.log.debug('Added %s to raw events queue.',
                    (full_filename, action))
 
    def start_watching(self):
        """Tell the watch to start processing events."""
        # get the diff dirs in the path
        for current_child in listdir(self._path):
            full_child_path = os.path.join(self._path, current_child)
            if os.path.isdir(full_child_path):
                self._subdirs.append(full_child_path)
        # start to diff threads, one to watch the path, the other to
        # process the events.
        self.log.debug('Sart watching path.')
        self._watching = True
        watch_thread = Thread(target=self._watch,
            name='Watch(%s)' % self._path)
        process_thread = Thread(target=self._process_events,
            name='Process(%s)' % self._path)
        process_thread.start()
        watch_thread.start()
 
    def stop_watching(self):
        """Tell the watch to stop processing events."""
        self._watching = False
        self._subdirs = []
 
    def update(self, mask, proc_fun=None, auto_add=False):
        """Update the info used by the watcher."""
        self.log.debug('update(%s, %s, %s)', mask, proc_fun, auto_add)
        self._mask = mask
        self._proc_fun = proc_fun
        self._auto_add = auto_add
 
    @property
    def path(self):
        """Return the patch watched."""
        return self._path
 
    @property
    def auto_add(self):
        return self._auto_add
 
    @property
    def proc_fun(self):
        return self._proc_fun
 
 
class WatchManager(object):
    """Implement the same functions as pyinotify.WatchManager."""
 
    def __init__(self, exclude_filter=lambda path: False):
        """Init the manager to keep trak of the different watches."""
        super(WatchManager, self).__init__()
        self.log = logging.getLogger('ubuntuone.platform.windows.'
            + 'filesystem_notifications.WatchManager')
        self._wdm = {}
        self._wd_count = 0
        self._exclude_filter = exclude_filter
        self._events_queue = Queue()
        self._ignored_paths = []
 
    def stop(self):
        """Close the manager and stop all watches."""
        self.log.debug('Stopping watches.')
        for current_wd in self._wdm:
            self._wdm[current_wd].stop_watching()
            self.log.debug('Watch for %s stopped.', self._wdm[current_wd].path)
 
    def get_watch(self, wd):
        """Return the watch with the given descriptor."""
        return self._wdm[wd]
 
    def del_watch(self, wd):
        """Delete the watch with the given descriptor."""
        try:
            watch = self._wdm[wd]
            watch.stop_watching()
            del self._wdm[wd]
            self.log.debug('Watch %s removed.', wd)
        except KeyError, e:
            logging.error(str(e))
 
    def _add_single_watch(self, path, mask, proc_fun=None, auto_add=False,
        quiet=True, exclude_filter=None):
        self.log.debug('add_single_watch(%s, %s, %s, %s, %s, %s)', path, mask,
            proc_fun, auto_add, quiet, exclude_filter)
        self._wdm[self._wd_count] = Watch(self._wd_count, path, mask,
            auto_add, events_queue=self._events_queue,
            exclude_filter=exclude_filter, proc_fun=proc_fun)
        self._wdm[self._wd_count].start_watching()
        self._wd_count += 1
        self.log.debug('Watch count increased to %s', self._wd_count)
 
    def add_watch(self, path, mask, proc_fun=None, auto_add=False,
        quiet=True, exclude_filter=None):
        if hasattr(path, '__iter__'):
            self.log.debug('Added collection of watches.')
            # we are dealing with a collection of paths
            for current_path in path:
                if not self.get_wd(current_path):
                    self._add_single_watch(current_path, mask, proc_fun,
                        auto_add, quiet, exclude_filter)
        elif not self.get_wd(path):
            self.log.debug('Adding single watch.')
            self._add_single_watch(path, mask, proc_fun, auto_add,
                quiet, exclude_filter)
 
    def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
                     auto_add=False, quiet=True):
        try:
            watch = self._wdm[wd]
            watch.stop_watching()
            self.log.debug('Stopped watch on %s for update.', watch.path)
            # update the data and restart watching
            auto_add = auto_add or rec
            watch.update(mask, proc_fun=proc_fun, auto_add=auto_add)
            # only start the watcher again if the mask was given, otherwhise
            # we are not watchng and therefore do not care
            if mask:
                watch.start_watching()
        except KeyError, e:
            self.log.error(str(e))
            if not quiet:
                raise WatchManagerError('Watch %s was not found' % wd, {})
 
    def get_wd(self, path):
        """Return the watcher that is used to watch the given path."""
        for current_wd in self._wdm:
            if self._wdm[current_wd].path in path and \
                self._wdm[current_wd].auto_add:
                return current_wd
 
    def get_path(self, wd):
        """Return the path watched by the wath with the given wd."""
        watch_ = self._wmd.get(wd)
        if watch:
            return watch.path
 
    def rm_watch(self, wd, rec=False, quiet=True):
        """Remove the the watch with the given wd."""
        try:
            watch = self._wdm[wd]
            watch.stop_watching()
            del self._wdm[wd]
        except KeyrError, err:
            self.log.error(str(err))
            if not quiet:
                raise WatchManagerError('Watch %s was not found' % wd, {})
 
    def rm_path(self, path):
        """Remove a watch to the given path."""
        # it would be very tricky to remove a subpath from a watcher that is
        # looking at changes in ther kids. To make it simpler and less error
        # prone (and even better performant since we use less threads) we will
        # add a filter to the events in the watcher so that the events from
        # that child are not received :)
        def ignore_path(event):
            """Ignore an event if it has a given path."""
            is_ignored = False
            for ignored_path in self._ignored_paths:
                if ignore_path in event.pathname:
                    return True
            return False
 
        wd = self.get_wd(path)
        if wd:
            if self._wdm[wd].path == path:
                self.log.debug('Removing watch for path "%s"', path)
                self.rm_watch(wd)
            else:
                self.log.debug('Adding exclude filter for "%s"', path)
                # we have a watch that cotains the path as a child path
                if not path in self._ignored_paths:
                    self._ignored_paths.append(path)
                # FIXME: This assumes that we do not have other function
                # which in our usecase is correct, but what is we move this
                # to other projects evet?!? Maybe using the manager
                # exclude_filter is better
                if not self._wdm[wd].exclude_filter:
                    self._wdm[wd].exclude_filter = ignore_path
 
    @property
    def watches(self):
        """Return a reference to the dictionary that contains the watches."""
        return self._wdm
 
    @property
    def events_queue(self):
        """Return the queue with the events that the manager contains."""
        return self._events_queue
 
 
class Notifier(object):
    """
    Read notifications, process events. Inspired by the pyinotify.Notifier
    """
 
    def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
                 threshold=10, timeout=-1):
        """Init to process event according to the given timeout & threshold."""
        super(Notifier, self).__init__()
        self.log = logging.getLogger('ubuntuone.platform.windows.'
            + 'filesystem_notifications.Notifier')
        # Watch Manager instance
        self._watch_manager = watch_manager
        # Default processing method
        self._default_proc_fun = default_proc_fun
        if default_proc_fun is None:
            self._default_proc_fun = PrintAllEvents()
        # Loop parameters
        self._read_freq = read_freq
        self._threshold = threshold
        self._timeout = timeout
 
    def proc_fun(self):
        return self._default_proc_fun
 
    def process_events(self):
        """
        Process the event given the threshold and the timeout.
        """
        self.log.debug('Processing events with threashold: %s and timeout: %s',
            self._threshold, self._timeout)
        # we will process an amount of events equal to the threshold of
        # the notifier and will block for the amount given by the timeout
        processed_events = 0
        while processed_events < self._threshold:
            try:
                raw_event = None
                if not self._timeout or self._timeout < 0:
                    raw_event = self._watch_manager.events_queue.get(
                        block=False)
                else:
                    raw_event = self._watch_manager.events_queue.get(
                        timeout=self._timeout)
                watch = self._watch_manager.get_watch(raw_event.wd)
                if watch is None:
                    # Not really sure how we ended up here, nor how we should
                    # handle these types of events and if it is appropriate to
                    # completly skip them (like we are doing here).
                    self.log.warning('Unable to retrieve Watch object '
                        + 'associated to %s', raw_event)
                    processed_events += 1
                    continue
                if watch and watch.proc_fun:
                    self.log.debug('Executing proc_fun from watch.')
                    watch.proc_fun(raw_event)  # user processings
                else:
                    self.log.debug('Executing default_proc_fun')
                    self._default_proc_fun(raw_event)
                processed_events += 1
            except Empty:
                # increase the number of processed events, and continue
                processed_events += 1
                continue
 
    def stop(self):
        """Stop processing events and the watch manager."""
        self._watch_manager.stop()

While one of the threads is retrieving the events from the file system, the second one process them so that the will be exposed as pyinotify events. I have done so because I did not want to deal with OVERLAP structures for asyn operations in Win32 and because I wanted to use pyinotify events so that if someone with experience in pyinotify looks at the output, he can easily understand it. I really like this approach because it allowed me to reuse a fair amount of logic hat we had in the Ubuntu client and to approach the port in a very TDD way since the tests I’ve used are the same ones as the ones found on Ubuntu :)

Read more
Matt Griffin

Are you the kind of person that wants to listen their extensive Barry White collection on their big living room speakers?

There are a variety of complex ways to do this but Ubuntu One offers an easy solution for iPhone users. All you’ll need is Ubuntu One Music (part of the Ubuntu One Mobile add-on) on an iPhone running iOS 4.2 and an audio/video component that supports AirPlay. Devices such as Apple TV and stereo electronics from DenonMarantz, or iHome (US links) are available now or coming soon to a store near you.

Setup is easy and does not require an update to the Ubuntu One Music app.

  1. Follow the instructions to connect your AirPlay device to your network and television.
  2. Launch Ubuntu One Music on your iPhone and start playing a song from your synced mp3 collection.
  3. The volume slider at the bottom of the screen has a new icon on the right. Clicking the icon enables you to choose where to output your audio. Select your Apple TV and after a few seconds you’ll start to hear “Can’t Get Enough of Your Love, Babe” from your speakers.

All of the app’s controls still function so you can pause, skip, or browse over to your Teddy Pendergrass favorites. You can be anywhere in the house and control what’s streaming from your Ubuntu One personal cloud.

Remember that Ubuntu One Mobile will stream all of your mp3s that are in your personal cloud whether they’re purchased from the Ubuntu One Music Store or synced from your personal collection.

Ubuntu One Music for iPhone with AirPlay

Read more
Joshua Hoover

The Ubuntu One Mobile plan gives you the ability to stream any music in your personal cloud to your iPhone or Android phone. Watch as Josh shows you how easy it can be to keep your music with you, everywhere you go, with Ubuntu One!

Read more
Joshua Hoover

Sometimes it’s the little things that can make a big difference. We think that’s the case with the following improvements we made to the Ubuntu One files management web interface:Stop syncing folder via web UI

Stop syncing folders. Do you have a folder you selected to sync with Ubuntu One but don’t want to anymore? Click the “More” link next to the folder and then click the new “Stop syncing this folder” link. Your folder will no longer sync with Ubuntu One while leaving the folder untouched on your computers.

Upload and publish files. Publishing files with Ubuntu One is a nice feature. Even nicer is being able to upload and publish a file at the same time. No more uploading, finding the uploaded file, clicking the “More” link, clicking the “Publish” link, then clicking the “More” link again, and…you get the idea. We made this much simpler. Now you click the “Upload file” link to select your file and tick to publish it all in one go.
Upload and publish a file via the Ubuntu One web UI

Along with adding the ability to upload and publish in one step, we made uploading files better. After you’ve successfully uploaded a file, you’ll be taken to that file in the list with the details expanded. That way you don’t have to hunt for the file you just uploaded. Upload file in Ubuntu One web UI

Stop receiving a share. If you no longer want to receive files from a folder someone shared with you, click on the “More” link and then click “Delete this share”. The folder and its files will still be on your computers but you won’t receive any new files. Delete a folder someone is sharing with you

These small improvements are the first of several more that are part of an overhaul of the entire web UI for files, notes, contacts, and bookmarks (yes, bookmarks). More on the overhaul later.

Read more
Cristian Parrino

So let there be Windows…

Ok, we’ve made the jump. An Ubuntu-branded experience on Windows. Can you believe it?

With the Maverick release, we’re finally delivering a service that gives you a flavor of our broader personal cloud strategy. As we strive to introduce security, control and convenience around the different facets of your digital world, we will be evolving in a number of areas:

    • > Platform ubiquity (Ubuntu, Web and now a Windows Beta) on basic synchronization services and breadth of content synced (Files, Contacts, Bookmarks and Notes are already available)
    • > Services that let you do more with that content (our cloud to mobile music streaming service for Android and iPhone is a first example)
    • > The integration of third party services (like the 7digital music store)

We’re ready to take the next step toward serving the needs of those ‘legacy’ OS users by accepting requests to join the public beta of Ubuntu One for Windows. Expanding to Windows is actually a fantastic thing for Ubuntu users. There are many of you out there that use Ubuntu at home and Windows, for example, at work. This platform expansion will make it easier to integrate Ubuntu into more aspects of your digital world.

For our first release of Ubuntu One for Windows, we decided to start small, with a target of delivering a commercial grade and a more comprehensive offering for Natty in April 2011. Our first Beta release synchronizes files between your Windows computer, your Ubuntu One personal cloud and your Ubuntu computer(s). Please review our wiki page for feature updates to our beta Windows service.

Update: The Windows beta installer is now available for everyone https://one.ubuntu.com/windows/beta

Read more
Joshua Hoover

Ask Ubuntu

The Ubuntu One team has been working hard to improve support of late. We initially focused on our status page, FAQs, and contacting Ubuntu One support directly. All of those areas have gone through several iterations and are continuing to evolve. One part of the support equation we’ve been missing is a definitive place to get help from other Ubuntu One users. Ask Ubuntu is the place to go for this type of community support.

Ask Ubuntu logoTo steal a line from the FAQ, Ask Ubuntu is a Q&A site designed to make it easy for users to get answers to Ubuntu-related questions. It’s easy to get started asking and answering questions. You can use an OpenID login like a Launchpad or Google account, or other options like Facebook, etc. to login to the site. For Ubuntu One related questions, use the ubuntu-one tag. Once you receive an answer you’re satisfied with, mark it as the accepted answer by clicking on the check box outline to the left of the answer.

Beyond asking and answering questions, you can also vote (up or down) questions and answers others have provided. Votes go towards a person’s reputation on the site. For example, if you answer a question and someone votes your answer up, you’ll gain +10. If someone votes up your question, you’ll gain +5. That’s right, good questions go towards building your reputation. For more info on reputation and Ask Ubuntu in general, check out the Ask Ubuntu FAQ.

Some Ubuntu One questions can’t be answered on Ask Ubuntu. These include questions specific to your account (billing, login, etc.) or those that require details about specific content not syncing properly. For example, my file “~/Pictures/Joshua on mongoose hunt.jpg” won’t sync. Questions like these require private information to be exchanged and should be asked via the Ubuntu One contact form.

In general, when looking for Ubuntu One support, you’ll want to consider the following options, in this order:

  1. Status page – There may be a service disruption causing the problem you’re having.
  2. FAQs – We continue to build and groom a list of frequently asked questions that may provide the answer to the question you have.
  3. Ask Ubuntu – If your question is not account related and doesn’t appear to need specifics on your content (file names, contacts, etc.), ask the community for help.
  4. Contact form – If your question is account related or you have specific issues with your content (particular files or contacts not syncing, etc) then contact us directly for support.

We’re excited about Ask Ubuntu. It only takes a minute or so to sign up and start using the site, so please do! See you there.

Read more
beuno

I am ZOMG very tired from the exciting release for Ubuntu One, but I wanted to highlight a very pleasant surprise.
We launched the Ubuntu One Music Streaming a week ago, and yesterday, while hadn't yet publicly released, we got a patch that adds support to tell last.fm the music you are listing from the Android app.

A big thank you to Scott Ferguson for being so awesome.

Read more
Matt Griffin

It’s finally here. Ubuntu One users can now add music streaming to the long list of things that we offer. This is a big step for us and our goal of creating useful services around your content.

We have to thank the Android beta testers who provided great feedback, identified important bugs and streamed enough songs to their phones to help us identify ways that we should improve our infrastructure.

Testing in the wild and sharing the code with everyone has yielded another benefit – feature contributions. We didn’t expect it, but the Ubuntu One Music app for Android will also include last.fm Scrobbling support. Special thanks to Scott Ferguson for that contribution… even before the app was released!

Find out more about Ubuntu One Mobile and music streaming at one.ubuntu.com/mobile.

Ubuntu One Music for Android and iPhone

Read more
beuno

Matt Griffin has written a great blog post, so I'm just going to echo it:

After over a year’s worth of feedback from users like you and a clear view of where we want to take Ubuntu One in the future, we’ve just made some changes to the Ubuntu One service offering and pricing plans.

For starters, we will no longer offer the 50 GB plan to new subscribers. Everyone will get the basic plan and then have the option to add various ‘add-ons’ of services and storage as needed. But here are the details:

Ubuntu One Basic – available now
This is the same as the current free 2 GB option but with a new name. Users can continue to sync files, contacts, bookmarks and notes for free as part of our basic service and access the integrated Ubuntu One Music Store. We are also extending our platform support to include a Windows client, which will be available in Beta very soon.

Ubuntu One Mobile – available October 7th
Ubuntu One Mobile is our first example of a service that helps you do more with the content stored in your personal cloud. With Ubuntu One Mobile’s main feature – mobile music streaming – users can listen to any MP3 songs in their personal cloud (any owned MP3s, not just those purchased from the Ubuntu One Music Store) using our custom developed apps for iPhone and Android (coming soon to their respective marketplaces). These will be open source and available from Launchpad. Ubuntu One Mobile will also include the mobile contacts sync feature that was launched in Beta for the 10.04 release.

Ubuntu One Mobile is available for $3.99 (USD) per month or $39.99 (USD) per year. Users interested in this add-on can try the service free for 30 days. Ubuntu One Mobile will be the perfect companion to your morning exercise, daily commute, and weekend at the beach – we’re really excited to bring you this service!

Ubuntu One 20-Packs – available now
A 20-Pack is 20 GB of storage for files, contacts, notes, and bookmarks. Users will be able to add multiple 20-Packs at $2.99 (USD) per month or $29.99 (USD) per year each. If you start with Ubuntu One Basic (2 GB) and add 1 20-Pack (20 GB), you will have 22 GB of storage.

All add-ons are available for purchase in multiple currencies – USD, EUR and, recently added, GBP.

Users currently paying for the old 50 GB plan (including mobile contacts sync) can either keep their existing service or switch to the new plans structure to get more value from Ubuntu One at a lower price.

We know that you will enjoy these new add-ons as well as the performance enhancements we’ve made to Ubuntu One in recent months. If you have questions, our recently updated support area is a great place to start. There you’ll find a link to the current status of Ubuntu One services, a link to our frequently updated list of frequently asked questions, and a way to send us a direct message. As always, you can also ping the team on IRC (#ubuntuone in freenode). We welcome your questions, comments and suggestions.

Read more
Matt Griffin

After over a year’s worth of feedback from users like you and a clear view of where we want to take Ubuntu One in the future, we’ve just made some changes to the Ubuntu One service offering and pricing plans.

For starters, we will no longer offer the 50 GB plan to new subscribers. Everyone will get the basic plan and then have the option to add various ‘add-ons’ of services and storage as needed. But here are the details:

Ubuntu One Basic – available now
This is the same as the current free 2 GB option but with a new name. Users can continue to sync files, contacts, bookmarks and notes for free as part of our basic service and access the integrated Ubuntu One Music Store. We are also extending our platform support to include a Windows client, which will be available in Beta very soon.

Ubuntu One Mobile – available October 7th
Ubuntu One Mobile is our first example of a service that helps you do more with the content stored in your personal cloud. With Ubuntu One Mobile’s main feature – mobile music streaming – users can listen to any MP3 songs in their personal cloud (any owned MP3s, not just those purchased from the Ubuntu One Music Store) using our custom developed apps for iPhone and Android (coming soon to their respective marketplaces). These will be open source and available from Launchpad. Ubuntu One Mobile will also include the mobile contacts sync feature that was launched in Beta for the 10.04 release.

Ubuntu One Mobile is available for $3.99 (USD) per month or $39.99 (USD) per year. Users interested in this add-on can try the service free for 30 days. Ubuntu One Mobile will be the perfect companion to your morning exercise, daily commute, and weekend at the beach – we’re really excited to bring you this service!

Ubuntu One 20-Packs – available now
A 20-Pack is 20 GB of storage for files, contacts, notes, and bookmarks. Users will be able to add multiple 20-Packs at $2.99 (USD) per month or $29.99 (USD) per year each. If you start with Ubuntu One Basic (2 GB) and add 1 20-Pack (20 GB), you will have 22 GB of storage.

All add-ons are available for purchase in multiple currencies – USD, EUR and, recently added, GBP.

Users currently paying for the old 50 GB plan (including mobile contacts sync) can either keep their existing service or switch to the new plans structure to get more value from Ubuntu One at a lower price.

We know that you will enjoy these new add-ons as well as the performance enhancements we’ve made to Ubuntu One in recent months. If you have questions, our recently updated support area is a great place to start. There you’ll find a link to the current status of Ubuntu One services, a link to our frequently updated list of frequently asked questions, and a way to send us a direct message. As always, you can also ping the team on IRC (#ubuntuone in freenode). We welcome your questions, comments and suggestions.

Read more
beuno

After a solid 6 months of work, music streaming is up for public testing!  \o/

Read the full announcement for all the details, and go see the wiki page on how to sign up.

Read more
Matt Griffin

Some of you may remember this blueprint about music steaming and the discussion at UDS-M earlier this year. Well we have some great news to share.

This new Ubuntu One service is almost complete and we’d like to invite you to help us test. The mobile music streaming service will enable you to enjoy your music, just like you do with Rhythmbox on your desktop, from your mobile phone.

  • Stream your entire library from your Ubuntu One personal cloud over a Wi-Fi or 3G connection.
  • Listen using the new Ubuntu One Music app for Android and iPhone. Apps coming soon.

Want to know more about this new feature? Take a look at the beta test information page which includes a link to join the beta test, a few screenshots, and answers to questions that you may have.

Ubuntu One Music on your Android phone

Get started with Ubuntu One Music

Listen to all of your artists and albums

Read more
beuno

One of the questions that took a little while for me to fully understand was a very simple one: why does Ubuntu One exist?

Depending on who you ask, you may get a different answer, but here's my take on it.

Above all, to extend the power of Ubuntu as an environment. Ubuntu One already allows you to many things beyond the basic file sync we started off with, you can keep your contacts from your phone and desktop  (and between other Ubuntu devices) in sync and backed up, notes, bookmarks, all your important files are backed up and synced, you can share them privately or publicly, you can buy music that gets delivered right to your music player, and soon you will be able to stream any of your music to your phone. And this is just today. As the project matures, we are working hard to make it easy for more and more third-party projects to use our platform and out-pace us in ideas and code.
All of this allows Ubuntu to extend its reach into mobile devices and even other operating systems. It feels like integrating into the real world today, not only the world we want to build.

Openness is the next thing on my mind. I know about all the criticisms about the server software not being open, I understand them and I've been through this same process with Launchpad. Right now, Canonical doesn't see a way to fund a 30+ developer team of rockstars, a huge infrastructure and bandwidth usage that is mostly used at no cost and still offer up the code to any competitor who could set up a competing project within minutes. I am sure someday, just like with Launchpad, we will figure it out and I will see all my commits push me up thousands of positions on ohloh. Until then, I'll have to continue working on Wikkid or any of the other 20 projects I use and am interested in, to keep me at a decent ranking.
All that said, the Ubuntu One team releases tons of source code all the time. A lot of the libraries we build are open sourced as soon as we get some time to clean them up and split it out of our source tree. All our desktop clients are open source from the start. On top of that, we work on pieces like desktopcouch, enabling couchdb for the desktop. We even got the chance to work with a closed-source iphone application, iSub, to open source his code so we could base our new streaming client on. We get to pay developers of open source projects on the Android platform as well, to work on improving it so we can deliver a better and more secure experience. We also get a chance to learn to package applications and upload new versions of the libraries we use to the Ubuntu repositories. And hundreds of other small things we do that feel so natural we forget to advertise and be proud of. All of this on Canonical's dime.

Finally, a goal that is dear to my heart. Make Canonical profitable. I have been overwhelmed over and over again by the passion with which Mark personally, and the company as a whole, contributes to making open source be the standard way of developing software in the world. I can understand why it's easy to feel uncomfortable with a privately owned company pursuing a profit while sponsoring an open source project which thousands of people contribute to, but after having sat down in dozens of meetings where everybody there cared about making sure we continue to grow as a community and that open source continues to win over tens of thousands of computers each month, I only worry about Canonical *not* being sustainable and constantly growing.

All these reasons for working on Ubuntu One have been close to my heart for many years now, a long time before I took the final step of investing not only my free time, but my work time, leisure time, and not too seldom, my sleep time,  and started working for Canonical in a very strict sense of the term "full time".

I've spent time working in a few different teams, all of them are interesting, exceptionally skilled and open source is a core part of their lives. Ubuntu One is where I feel I can do the most impact today, and I'm beyond lucky to have given the opportunity to act on it.

Read more
Matt Griffin

Ubuntu One Maverick beta update

Special thanks go out to the Ubuntu Maverick alpha and beta testers and those that are informing us of Ubuntu One bugs in the release. Our team has been hard at work resolving them so I thought I’d provide a brief summary of a few of the most recent fixes.

  • Ubuntu One is now using the new Ubuntu SSO authentication service. There have been many bug fixes along with this change that will make creating an Ubuntu One account more convenient and signing-in to add a computer more reliable
  • Fixed the application name “Ubuntu One” that we display at https://login.ubuntu.com/+applications
  • Added the Nautilus Ubuntu One ribbon and implemented fixes for when it is visible, how it functions, and some related Nautilus crashing issues
  • Fixes to Nautilus displaying published files
  • Many server side improvements to improve server resource efficiency and enhance desktop sync speed
  • Usability enhancements to the contact picker which will make privately sharing folders using your Evolution address book an easier process
  • The Ubuntu One Music Store is now available to Maverick testers using Rhythmbox. A fix is coming soon for Banshee

These are only a few areas that the team has worked on to make it a better service for all customers. Thanks again for the Maverick beta testers for providing important feedback on Ubuntu One. If you notice problems with Ubuntu One on Maverick or any other release, please file bugs at our Launchpad project page.

UPDATE:

Just a reminder that if you are filing a bug, it’s best to at least include this basic information (courtesy of Dave Winer via Joshua Hoover).

  1. What you were doing.
  2. What you expected to happen.
  3. What actually happened.

Thanks!

Read more
Matt Griffin

We have some great news to share about Ubuntu One support for Android devices.

Ubuntu One Contacts for Android

We recently released an Android version of the Ubuntu One Contacts sync application. This app will sync your phone address book with your Ubuntu One personal cloud to help keep your digital life together. An Ubuntu One account is required.

Ubuntu One Contacts is based on the existing Funambol application but we’ve made a few enhancements.

  • You no longer need a custom username and password. Sign in with your standard Ubuntu One username and password (same as your Ubuntu SSO account)
  • Many interface updates to improve the application’s overall usability

Ubuntu One Contacts is now available for free from the Android Market. Just search for “Ubuntu One” and install. Remember that we’ve temporarily removed the time limit on the mobile contacts sync trial so more people can experience this feature. After the Ubuntu 10.10 release on October 10, we will re-enable the 30-day time limit.

The hackers among you are also welcome to download the source code of our Android application and learn how it works.

More Android devices

Ubuntu One Mobile Contacts Sync now supports more Android devices. In fact, these are some of the most popular Android devices available. They include the following:

  • Google Nexus One
  • HTC Dragon
  • Motorola A855 Tao
  • Motorola Driod
  • Motorola Milestone
  • Motorola Moto XT701
  • Motorola Motorio XT720
  • Motorola Moto XT800
  • Motorola Sholes
  • Motorola Sholes Tablet
  • Motorola Zeppelin

If you have one of these devices, sign up for Ubuntu One and try out Mobile Contacts Sync today.

Read more
Matt Griffin

The Ubuntu One team is working hard to finish our many enhancements for Ubuntu 10.10. Read a preview of some of the features we’re building. Highlights include:

  • Mobile music streaming
  • Mobile contacts sync enhancements
  • Significantly improved sync performance
  • File sync for Windows

More information coming soon about the other ways Ubuntu One will help bring your digital life together this October.

Read more
Matt Griffin

Ubuntu One dashboard update

Ubuntu One August 2010 dashboard update

Some of you may have noticed that we recently updated the Ubuntu One dashboard. A few of the changes include:

  • Clearer view of how you’re using Ubuntu One with your files, contacts, and notes
  • Links to important Ubuntu One account management resources
  • Highlight our YouTube channel (more info on that coming soon)

We also added a link to the current Ubuntu One status at the bottom of all pages. This is an area that we update frequently with news about the current status of all Ubuntu One services. It’s a great first place to visit if you notice a problem and are curious if it’s been reported.

As we get closer to Ubuntu 10.10, we’ll release many more updates to the website so stay tuned.

Read more