Example #1
0
    def sync(self):
        self.dao = LycheeDAO(self.conf)

        path = self.conf["srcdir"]
        event_handler = GalleryHandler(self)
        observer = Observer()
        observer.schedule(event_handler, path, recursive=True)
        observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            observer.stop()
        observer.join()
Example #2
0
    def sync(self):
        """
        Program main loop
        Scans files to add in the sourcedirectory and add them to Lychee
        according to the conf file and given parameters
        Returns nothing
        """

        # Connect db
        # and drop it if dropdb activated
        self.dao = LycheeDAO(self.conf)

        if self.conf['dropdb']:
            self.deleteAllFiles()

        # Load db

        createdalbums = 0
        discoveredphotos = 0
        importedphotos = 0
        album = {}
        albums = []

        ignored = [];

        album_name_max_width = self.dao.getAlbumNameDBWidth()

        # walkthroug each file / dir of the srcdir
        for root, dirs, files in os.walk(self.conf['srcdir']):

            # Init album data
            album['id'] = None
            album['name'] = None
            album['path'] = None
            album['relpath'] = None  # path relative to srcdir
            album['photos'] = []  # path relative to srcdir

            # if a there is at least one photo in the files
            if any([self.isAPhoto(f) for f in files]):
                album['path'] = root

                if "ignore" in self.conf and root.endswith(tuple(self.conf["ignore"])):
                    msg = ("WARN: directory is ignored"), root
                    print msg
                    ignored.append(root)
                    continue

                if ".lycheeignore" in files:
                    msg = ("WARN: directory ignored by .lycheeignore"), root
                    print msg
                    ignored.append(root)
                    continue

                if root.startswith(tuple(ignored)):
                    msg = ("WARN: directory is a child of an ignored directory"), root
                    print msg
                    continue

                # don't know what to do with theses photo
                # and don't wan't to create a default album
                if album['path'] == self.conf['srcdir']:
                    msg = ("WARN: file at srcdir root won't be added to lychee, " +
                           "please move them in a subfolder"), os.path.join(root, f)
                    print msg
                    continue

                # Fill in other album properties
                # albumnames start at srcdir (to avoid absolute path albumname)
                album['relpath'] = os.path.relpath(album['path'], self.conf['srcdir'])
                album['name'] = self.getAlbumNameFromPath(album)

                if len(album['name']) > album_name_max_width:
                    print "WARN: album name too long, will be truncated " + album['name']
                    album['name'] = album['name'][0:album_name_max_width]
                    if self.conf['verbose']:
                        print "WARN: album name is now " + album['name']

                album['id'] = self.dao.albumExists(album)

                if not(album['id']):
                    # create album
                    album['id'] = self.createAlbum(album)
                    # TODO go to next album if it fails
                    if not(album['id']):
                        print "ERROR didn't manage to create album for: " + album['relpath']
                        continue

                    createdalbums += 1
                elif self.conf['replace']:
                    # drop album photos
                    filelist = self.dao.eraseAlbum(album)
                    self.deleteFiles(filelist)

                # Albums are created or emptied, now take care of photos
                for f in files:
                    if self.isAPhoto(f):

                        try:
                            discoveredphotos += 1
                            photo = LycheePhoto(self.conf, f, album)

                            if not(self.dao.photoExists(photo)):
                                if self.conf['verbose']:
                                    print "INFO: adding to lychee", os.path.join(root, f)
                                self.makeThumbnail(photo)
                                res = self.addFileToAlbum(photo)
                                self.adjustRotation(photo)
                                # increment counter
                                if res:
                                    importedphotos += 1
                                # report
                                if self.conf['verbose']:
                                    if res:
                                        album['photos'].append(photo)
                                    else:
                                        print "ERROR: while adding to lychee", os.path.join(root, f)
                            else:
                                if self.conf['verbose']:
                                    print "WARN: photo already exists in lychee with same name or same checksum: ", photo.srcfullpath
                        except Exception:
                            print "ERROR could not add " + str(f) + " to album " + album['name']
                            traceback.print_exc()

                a = album.copy()
                albums.append(a)

        self.updateAlbumsDate(albums)
        if self.conf['sort']:
            self.reorderalbumids(albums)
            self.dao.reinitAlbumAutoIncrement()
        self.dao.close()

        # Final report
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        print "Directory scanned:", self.conf['srcdir']
        print "Created albums: ", str(createdalbums)
        if (importedphotos == discoveredphotos):
            print str(importedphotos), "photos imported on", str(discoveredphotos), "discovered"
        else:
            print 'ERROR: ' + str(importedphotos), "photos imported on", str(discoveredphotos), "discovered"
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
Example #3
0
class LycheeSyncer:

    """
    This class contains the logic behind this program
    It consist mainly in filesystem operations
    It relies on:
    - LycheeDAO for dtabases operations
    - LycheePhoto to store (and compute) photos propreties
    """

    conf = {}

    def __init__(self, conf):
        """
        Takes a dictionnary of conf as input
        """
        self.conf = conf

    def getAlbumNameFromPath(self, album):
        """
        build a lychee compatible albumname from an albumpath (relative to the srcdir main argument)
        Takes an album properties list  as input. At least the path sould be specified (relative albumpath)
        Returns a string, the lychee album name
        """
        # make a list with directory and sub dirs
        path = album['relpath'].split(os.sep)
        # join the rest: no subfolders in lychee yet
        album['name'] = "_".join(path)
        return album['name']

    def isAPhoto(self, file):
        """
        Determine if the filename passed is a photo or not based on the file extension
        Takes a string  as input (a file name)
        Returns a boolean
        """
        validimgext = ['.jpg', '.jpeg', '.gif', '.png']
        ext = os.path.splitext(file)[-1].lower()
        return (ext in validimgext)

    def albumExists(self, album):
        """
        Takes an album properties list  as input. At least the relpath sould be specified (relative albumpath)
        Returns an albumid or None if album does not exists
        """

    def createAlbum(self, album):
        """
        Creates an album
        Inputs:
        - album: an album properties list. at least path should be specified (relative albumpath)
        Returns an albumid or None if album does not exists
        """
        album['id'] = None
        if album['name'] != "":
            album['id'] = self.dao.createAlbum(album)
        return album['id']

    def thumbIt(self, res, photo, destinationpath, destfile):
        """
        Create the thumbnail of a given photo
        Parameters:
        - res: should be a set of h and v res (640, 480)
        - photo: a valid LycheePhoto object
        - destinationpath: a string the destination full path of the thumbnail (without filename)
        - destfile: the thumbnail filename
        Returns the fullpath of the thuumbnail
        """

        if photo.width > photo.height:
            delta = photo.width - photo.height
            left = int(delta / 2)
            upper = 0
            right = int(photo.height + left)
            lower = int(photo.height)
        else:
            delta = photo.height - photo.width
            left = 0
            upper = int(delta / 2)
            right = int(photo.width)
            lower = int(photo.width + upper)

        destimage = os.path.join(destinationpath, destfile)
        try:
            img = Image.open(photo.srcfullpath)
        except:
            print "ERROR ioerror (corrupted file?): " + photo.srcfullpath
            raise

        img = img.crop((left, upper, right, lower))
        img.thumbnail(res, Image.ANTIALIAS)
        img.save(destimage, quality=99)
        return destimage

    def makeThumbnail(self, photo):
        """
        Make the 2 thumbnails needed by Lychee for a given photo
        and store their path in the LycheePhoto object
        Parameters:
        - photo: a valid LycheePhoto object
        returns nothing
        """
        # set  thumbnail size
        sizes = [(200, 200), (400, 400)]
        # insert @2x in big thumbnail file name
        filesplit = os.path.splitext(photo.url)
        destfiles = [photo.url, ''.join([filesplit[0], "@2x", filesplit[1]]).lower()]
        # compute destination path
        destpath = os.path.join(self.conf["lycheepath"], "uploads", "thumb")
        # make thumbnails
        photo.thumbnailfullpath = self.thumbIt(sizes[0], photo, destpath, destfiles[0])
        photo.thumbnailx2fullpath = self.thumbIt(sizes[1], photo, destpath, destfiles[1])

    def addFileToAlbum(self, photo):
        """
        add a file to an album, the albumid must be previously stored in the LycheePhoto parameter
        Parameters:
        - photo: a valid LycheePhoto object
        Returns True if everything went ok
        """
        res = False

        try:
            # copy photo
            if self.conf['link']:
                os.symlink(photo.srcfullpath, photo.destfullpath)
            else:
                shutil.copy(photo.srcfullpath, photo.destfullpath)
            # adjust right (chmod/chown)
            try:
                os.lchown(photo.destfullpath, self.conf['uid'], self.conf['gid'])

                if not(self.conf['link']):
                    st = os.stat(photo.destfullpath)
                    os.chmod(photo.destfullpath, st.st_mode | stat.S_IRWXU | stat.S_IRWXG)
                else:
                    st = os.stat(photo.srcfullpath)
                    os.chmod(photo.srcfullpath, st.st_mode | stat.S_IROTH)

            except Exception as e:
                pass

            res = self.dao.addFileToAlbum(photo)

        except Exception:
            print "addFileToAlbum", Exception
            traceback.print_exc()
            res = False

        return res

    def deleteFiles(self, filelist):
        """
        Delete files in the Lychee file tree (uploads/big and uploads/thumbnails)
        Give it the file name and it will delete relatives files and thumbnails
        Parameters:
        - filelist: a list of filenames
        Returns nothing
        """

        for url in filelist:
            if self.isAPhoto(url):
                thumbpath = os.path.join(self.conf["lycheepath"], "uploads", "thumb", url)
                filesplit = os.path.splitext(url)
                thumb2path = ''.join([filesplit[0], "@2x", filesplit[1]]).lower()
                thumb2path = os.path.join(self.conf["lycheepath"], "uploads", "thumb", thumb2path)
                bigpath = os.path.join(self.conf["lycheepath"], "uploads", "big", url)
                remove_file(thumbpath)
                remove_file(thumb2path)
                remove_file(bigpath)

    def rotatephoto(self, photo, rotation):
        # rotate main photo
        img = Image.open(photo.destfullpath)
        img2 = img.rotate(rotation)
        img2.save(photo.destfullpath, quality=99)
        # rotate Thumbnails
        img = Image.open(photo.thumbnailx2fullpath)
        img2 = img.rotate(rotation)
        img2.save(photo.thumbnailx2fullpath, quality=99)
        img = Image.open(photo.thumbnailfullpath)
        img2.rotate(rotation)
        img2.save(photo.thumbnailfullpath, quality=99)

    def adjustRotation(self, photo):
        """
        Rotates photos according to the exif orienttaion tag
        Returns nothing
        """
        if photo.exif.orientation not in (0, 1):
            # There is somthing to do
            if photo.exif.orientation == 6:
                # rotate 90° clockwise
                # AND LOOSE EXIF DATA
                self.rotatephoto(photo, -90)
            if photo.exif.orientation == 8:
                # rotate 90° counterclockwise
                # AND LOOSE EXIF DATA
                self.rotatephoto(photo, 90)

    def reorderalbumids(self, albums):

        # sort albums by title
        def getName(album):
            return album['name']

        sortedalbums = sorted(albums, key=getName)

        # count albums
        nbalbum = len(albums)
        # get higher album id + 1 as a first new album id
        min, max = self.dao.getAlbumMinMaxIds()

        if nbalbum + 1 < min:
            newid = 1
        else:
            newid = max + 1

        for a in sortedalbums:
            self.dao.changeAlbumId(a['id'], newid)
            newid = newid + 1

    def updateAlbumsDate(self, albums):
        now = datetime.datetime.now()
        last2min = now - datetime.timedelta(minutes=2)
        last2min_epoch = (last2min - datetime.datetime(1970, 1, 1)).total_seconds()

        for a in albums:
            try:
                # get photos with a real date (not just now)
                datelist = None
                datelist = [photo.sysdate for photo in a['photos'] if photo.sysdate < last2min_epoch]

                if datelist is not None and len(datelist) > 0:
                    newdate = max(datelist)
                    self.dao.updateAlbumDate(a['id'], newdate)
                    if self.conf["verbose"]:
                        print "INFO album " + a['name'] + " sysstamp changed to: ", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(newdate))
            except Exception as e:
                print "ERROR: updating album date for album:" + a['name'], e

    def deleteAllFiles(self):
        """
        Deletes every photo file in Lychee
        Returns nothing
        """
        filelist = []
        photopath = os.path.join(self.conf["lycheepath"], "uploads", "big")
        filelist = [f for f in os.listdir(photopath)]
        self.deleteFiles(filelist)

    def sync(self):
        """
        Program main loop
        Scans files to add in the sourcedirectory and add them to Lychee
        according to the conf file and given parameters
        Returns nothing
        """

        # Connect db
        # and drop it if dropdb activated
        self.dao = LycheeDAO(self.conf)

        if self.conf['dropdb']:
            self.deleteAllFiles()

        # Load db

        createdalbums = 0
        discoveredphotos = 0
        importedphotos = 0
        album = {}
        albums = []

        ignored = [];

        album_name_max_width = self.dao.getAlbumNameDBWidth()

        # walkthroug each file / dir of the srcdir
        for root, dirs, files in os.walk(self.conf['srcdir']):

            # Init album data
            album['id'] = None
            album['name'] = None
            album['path'] = None
            album['relpath'] = None  # path relative to srcdir
            album['photos'] = []  # path relative to srcdir

            # if a there is at least one photo in the files
            if any([self.isAPhoto(f) for f in files]):
                album['path'] = root

                if "ignore" in self.conf and root.endswith(tuple(self.conf["ignore"])):
                    msg = ("WARN: directory is ignored"), root
                    print msg
                    ignored.append(root)
                    continue

                if ".lycheeignore" in files:
                    msg = ("WARN: directory ignored by .lycheeignore"), root
                    print msg
                    ignored.append(root)
                    continue

                if root.startswith(tuple(ignored)):
                    msg = ("WARN: directory is a child of an ignored directory"), root
                    print msg
                    continue

                # don't know what to do with theses photo
                # and don't wan't to create a default album
                if album['path'] == self.conf['srcdir']:
                    msg = ("WARN: file at srcdir root won't be added to lychee, " +
                           "please move them in a subfolder"), os.path.join(root, f)
                    print msg
                    continue

                # Fill in other album properties
                # albumnames start at srcdir (to avoid absolute path albumname)
                album['relpath'] = os.path.relpath(album['path'], self.conf['srcdir'])
                album['name'] = self.getAlbumNameFromPath(album)

                if len(album['name']) > album_name_max_width:
                    print "WARN: album name too long, will be truncated " + album['name']
                    album['name'] = album['name'][0:album_name_max_width]
                    if self.conf['verbose']:
                        print "WARN: album name is now " + album['name']

                album['id'] = self.dao.albumExists(album)

                if not(album['id']):
                    # create album
                    album['id'] = self.createAlbum(album)
                    # TODO go to next album if it fails
                    if not(album['id']):
                        print "ERROR didn't manage to create album for: " + album['relpath']
                        continue

                    createdalbums += 1
                elif self.conf['replace']:
                    # drop album photos
                    filelist = self.dao.eraseAlbum(album)
                    self.deleteFiles(filelist)

                # Albums are created or emptied, now take care of photos
                for f in files:
                    if self.isAPhoto(f):

                        try:
                            discoveredphotos += 1
                            photo = LycheePhoto(self.conf, f, album)

                            if not(self.dao.photoExists(photo)):
                                if self.conf['verbose']:
                                    print "INFO: adding to lychee", os.path.join(root, f)
                                self.makeThumbnail(photo)
                                res = self.addFileToAlbum(photo)
                                self.adjustRotation(photo)
                                # increment counter
                                if res:
                                    importedphotos += 1
                                # report
                                if self.conf['verbose']:
                                    if res:
                                        album['photos'].append(photo)
                                    else:
                                        print "ERROR: while adding to lychee", os.path.join(root, f)
                            else:
                                if self.conf['verbose']:
                                    print "WARN: photo already exists in lychee with same name or same checksum: ", photo.srcfullpath
                        except Exception:
                            print "ERROR could not add " + str(f) + " to album " + album['name']
                            traceback.print_exc()

                a = album.copy()
                albums.append(a)

        self.updateAlbumsDate(albums)
        if self.conf['sort']:
            self.reorderalbumids(albums)
            self.dao.reinitAlbumAutoIncrement()
        self.dao.close()

        # Final report
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        print "Directory scanned:", self.conf['srcdir']
        print "Created albums: ", str(createdalbums)
        if (importedphotos == discoveredphotos):
            print str(importedphotos), "photos imported on", str(discoveredphotos), "discovered"
        else:
            print 'ERROR: ' + str(importedphotos), "photos imported on", str(discoveredphotos), "discovered"
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
Example #4
0
    def sync(self):
        """
        Program main loop
        Scans files to add in the sourcedirectory and add them to Lychee
        according to the conf file and given parameters
        Returns nothing
        """

        #Connect db
        #and drop it if dropdb activated
        self.dao = LycheeDAO(self.conf)

        if self.conf['dropdb']:
            self.deleteAllFiles()

        #Load db

        createdalbums = 0
        discoveredphotos = 0
        importedphotos = 0
        album = {}
        #walkthroug each file / dir of the srcdir
        for root, dirs, files in os.walk(self.conf['srcdir']):

            # Init album data
            album['id'] = None
            album['name'] = None
            album['path'] = None
            album['relpath'] = None  # path relative to srcdir

            # if a there is at least one photo in the files
            if any([self.isAPhoto(f) for f in files]):
                    album['path'] = root

                    # don't know what to do with theses photo
                    # and don't wan't to create a default album
                    if album['path'] == self.conf['srcdir']:
                        msg = ("WARN: file at srcdir root won't be added to lychee, " +
                               "please move them in a subfolder"), os.path.join(root, f)
                        print msg
                        continue

                    # Fill in other album properties
                    # albumnames start at srcdir (to avoid absolute path albumname)
                    album['relpath'] = os.path.relpath(album['path'], self.conf['srcdir'])
                    album['name'] = self.getAlbumNameFromPath(album)
                    album['id'] = self.dao.albumExists(album)

                    if not(album['id']):
                        #create album
                        album['id'] = self.createAlbum(album)
                        createdalbums += 1
                    elif self.conf['replace']:
                        #drop album photos
                        filelist = self.dao.eraseAlbum(album)
                        self.deleteFiles(filelist)

            # Albums are created or emptied, now take care of photos
            for f in files:
                if self.isAPhoto(f):

                    discoveredphotos += 1
                    photo = LycheePhoto(self.conf, f, album)

                    if not(self.dao.photoExists(photo)):
                        if self.conf['verbose']:
                            print "INFO: adding to lychee", os.path.join(root, f)
                        self.makeThumbnail(photo)
                        res = self.addFileToAlbum(photo)
                        self.adjustRotation(photo)
                        #increment counter
                        if res:
                                importedphotos += 1
                        #report
                        if self.conf['verbose']:
                            if res:
                                print "INFO: successfully added to lychee", os.path.join(root, f)
                            else:
                                print "ERROR: while adding to lychee", os.path.join(root, f)
                    else:
                        if self.conf['verbose']:
                            print "WARN: photo already exists in lychee: ", photo.srcfullpath

        self.dao.close()

        # Final report
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        print "Directory scanned:", self.conf['srcdir']
        print "Created albums: ", str(createdalbums)
        print str(importedphotos), "photos imported on",  str(discoveredphotos), "discovered"
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
Example #5
0
class LycheeSyncer:
    """
    This class contains the logic behind this program
    It consist mainly in filesystem operations
    It relies on:
    - LycheeDAO for dtabases operations
    - LycheePhoto to store (and compute) photos propreties
    """

    conf = {}

    def __init__(self, conf):
        """
        Takes a dictionnary of conf as input
        """
        self.conf = conf

    def getAlbumNameFromPath(self, album):
        """
        build a lychee compatible albumname from an albumpath (relative to the srcdir main argument)
        Takes an album properties list  as input. At least the path sould be specified (relative albumpath)
        Returns a string, the lychee album name
        """
        #make a list with directory and sub dirs
        path = album['relpath'].split(os.sep)
        #join the rest: no subfolders in lychee yet
        album['name'] = "_".join(path).lower()
        return album['name']

    def isAPhoto(self, file):
        """
        Determine if the filename passed is a photo or not based on the file extension
        Takes a string  as input (a file name)
        Returns a boolean
        """
        validimgext = ['.jpg', '.jpeg', '.gif', '.png']
        ext = os.path.splitext(file)[-1].lower()
        return (ext in validimgext)

    def albumExists(self, album):
        """
        Takes an album properties list  as input. At least the relpath sould be specified (relative albumpath)
        Returns an albumid or None if album does not exists
        """

    def createAlbum(self, album):
        """
        Creates an album
        Inputs:
        - album: an album properties list. at least path should be specified (relative albumpath)
        Returns an albumid or None if album does not exists
        """
        album['id'] = None
        album['name'] = self.getAlbumNameFromPath(album)
        if album['name'] != "":
            album['id'] = self.dao.createAlbum(album)
        return album['id']

    def thumbIt(self, res, photo, destinationpath, destfile):
        """
        Create the thumbnail of a given photo
        Parameters:
        - res: should be a set of h and v res (640, 480)
        - photo: a valid LycheePhoto object
        - destinationpath: a string the destination full path of the thumbnail (without filename)
        - destfile: the thumbnail filename
        Returns the fullpath of the thuumbnail
        """

        if photo.width > photo.height:
            delta = photo.width - photo.height
            left = int(delta/2)
            upper = 0
            right = photo.height + left
            lower = photo.height
        else:
            delta = photo.height - photo.width
            left = 0
            upper = int(delta/2)
            right = photo.width
            lower = photo.width + upper

        destimage = os.path.join(destinationpath, destfile)
        img = Image.open(photo.srcfullpath)
        img = img.crop((left, upper, right, lower))
        img.thumbnail(res, Image.ANTIALIAS)
        img.save(destimage, quality=99)
        return destimage

    def makeThumbnail(self, photo):
        """
        Make the 2 thumbnails needed by Lychee for a given photo
        and store their path in the LycheePhoto object
        Parameters:
        - photo: a valid LycheePhoto object
        returns nothing
        """
        #set  thumbnail size
        sizes = [(200, 200), (400, 400)]
        #insert @2x in big thumbnail file name
        filesplit = os.path.splitext(photo.url)
        destfiles = [photo.url, ''.join([filesplit[0], "@2x", filesplit[1]]).lower()]
        #compute destination path
        destpath = os.path.join(self.conf["lycheepath"], "uploads", "thumb")
        #make thumbnails
        photo.thumbnailfullpath = self.thumbIt(sizes[0], photo, destpath, destfiles[0])
        photo.thumbnailx2fullpath = self.thumbIt(sizes[1], photo, destpath, destfiles[1])

    def addFileToAlbum(self, photo):
        """
        add a file to an album, the albumid must be previously stored in the LycheePhoto parameter
        Parameters:
        - photo: a valid LycheePhoto object
        Returns True if everything went ok
        """
        res = False

        try:
            #copy photo
            shutil.copy(photo.srcfullpath, photo.destfullpath)
            res = self.dao.addFileToAlbum(photo)

        except Exception:
            print "addFileToAlbum", Exception
            traceback.print_exc()
            res = False

        return res

    def deleteFiles(self, filelist):
        """
        Delete files in the Lychee file tree (uploads/big and uploads/thumbnails)
        Give it the file name and it will delete relatives files and thumbnails
        Parameters:
        - filelist: a list of filenames
        Returns nothing
        """

        for url in filelist:
            if self.isAPhoto(url):
                thumbpath = os.path.join(self.conf["lycheepath"], "uploads", "thumb", url)
                filesplit = os.path.splitext(url)
                thumb2path = ''.join([filesplit[0], "@2x", filesplit[1]]).lower()
                thumb2path = os.path.join(self.conf["lycheepath"], "uploads", "thumb", thumb2path)
                bigpath = os.path.join(self.conf["lycheepath"], "uploads", "big", url)

                os.remove(thumbpath)
                os.remove(thumb2path)
                os.remove(bigpath)

    def rotatephoto(self, photo, rotation):
        # rotate main photo
        img = Image.open(photo.destfullpath)
        img2 = img.rotate(rotation)
        img2.save(photo.destfullpath, quality=99)
        # rotate Thumbnails
        img = Image.open(photo.thumbnailx2fullpath)
        img2 = img.rotate(rotation)
        img2.save(photo.thumbnailx2fullpath, quality=99)
        img = Image.open(photo.thumbnailfullpath)
        img2.rotate(rotation)
        img2.save(photo.thumbnailfullpath, quality=99)

    def adjustRotation(self, photo):
        """
        Rotates photos according to the exif orienttaion tag
        Returns nothing
        """
        if photo.exif.orientation not in (0, 1):
            # There is somthing to do
            if photo.exif.orientation == 6:
                # rotate 90° clockwise
                # AND LOOSE EXIF DATA
                self.rotatephoto(photo, -90)
            if photo.exif.orientation == 8:
                # rotate 90° counterclockwise
                # AND LOOSE EXIF DATA
                self.rotatephoto(photo, 90)

    def deleteAllFiles(self):
        """
        Deletes every photo file in Lychee
        Returns nothing
        """
        filelist = []
        photopath = os.path.join(self.conf["lycheepath"], "uploads", "big")
        filelist = [f for f in os.listdir(photopath)]
        self.deleteFiles(filelist)

    def sync(self):
        """
        Program main loop
        Scans files to add in the sourcedirectory and add them to Lychee
        according to the conf file and given parameters
        Returns nothing
        """

        #Connect db
        #and drop it if dropdb activated
        self.dao = LycheeDAO(self.conf)

        if self.conf['dropdb']:
            self.deleteAllFiles()

        #Load db

        createdalbums = 0
        discoveredphotos = 0
        importedphotos = 0
        album = {}
        #walkthroug each file / dir of the srcdir
        for root, dirs, files in os.walk(self.conf['srcdir']):

            # Init album data
            album['id'] = None
            album['name'] = None
            album['path'] = None
            album['relpath'] = None  # path relative to srcdir

            # if a there is at least one photo in the files
            if any([self.isAPhoto(f) for f in files]):
                    album['path'] = root

                    # don't know what to do with theses photo
                    # and don't wan't to create a default album
                    if album['path'] == self.conf['srcdir']:
                        msg = ("WARN: file at srcdir root won't be added to lychee, " +
                               "please move them in a subfolder"), os.path.join(root, f)
                        print msg
                        continue

                    # Fill in other album properties
                    # albumnames start at srcdir (to avoid absolute path albumname)
                    album['relpath'] = os.path.relpath(album['path'], self.conf['srcdir'])
                    album['name'] = self.getAlbumNameFromPath(album)
                    album['id'] = self.dao.albumExists(album)

                    if not(album['id']):
                        #create album
                        album['id'] = self.createAlbum(album)
                        createdalbums += 1
                    elif self.conf['replace']:
                        #drop album photos
                        filelist = self.dao.eraseAlbum(album)
                        self.deleteFiles(filelist)

            # Albums are created or emptied, now take care of photos
            for f in files:
                if self.isAPhoto(f):

                    discoveredphotos += 1
                    photo = LycheePhoto(self.conf, f, album)

                    if not(self.dao.photoExists(photo)):
                        if self.conf['verbose']:
                            print "INFO: adding to lychee", os.path.join(root, f)
                        self.makeThumbnail(photo)
                        res = self.addFileToAlbum(photo)
                        self.adjustRotation(photo)
                        #increment counter
                        if res:
                                importedphotos += 1
                        #report
                        if self.conf['verbose']:
                            if res:
                                print "INFO: successfully added to lychee", os.path.join(root, f)
                            else:
                                print "ERROR: while adding to lychee", os.path.join(root, f)
                    else:
                        if self.conf['verbose']:
                            print "WARN: photo already exists in lychee: ", photo.srcfullpath

        self.dao.close()

        # Final report
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        print "Directory scanned:", self.conf['srcdir']
        print "Created albums: ", str(createdalbums)
        print str(importedphotos), "photos imported on",  str(discoveredphotos), "discovered"
        print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"