class Downloader(object):
    def __init__(self):
        self.googlePhotos = self.getPhotosService()
        self.logger = MessageLogger(logName="download.log")

    def getPhotosService(self):
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server(port=0)
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)

        service = build('photoslibrary', 'v1', credentials=creds)
        return service

    def listAllAlbums(self, pageSize=50):
        albumsList = []
        nextPageToken = 'Dummy'
        while nextPageToken != '':
            nextPageToken = '' if nextPageToken == 'Dummy' else nextPageToken
            results = self.googlePhotos.albums().list(
                pageSize=pageSize, pageToken=nextPageToken).execute()
            albums = results.get('albums', [])
            nextPageToken = results.get('nextPageToken', '')
            for album in albums:
                albumsList.append(album)
        return albumsList

    def getAlbum(self, albumId):
        return self.googlePhotos.albums().get(albumId=albumId).execute()

    def listAlbumPhotos(self, albumId, pageSize=50):
        photoList = []
        nextPageToken = 'Dummy'
        while nextPageToken != '':
            nextPageToken = '' if nextPageToken == 'Dummy' else nextPageToken
            results = self.googlePhotos.mediaItems().search(
                body={
                    "albumId": albumId,
                    "pageSize": pageSize,
                    "pageToken": nextPageToken
                }).execute()
            photos = results.get('mediaItems', [])
            nextPageToken = results.get('nextPageToken', '')
            for photo in photos:
                photoList.append(photo)
            return photoList

    def getPhoto(self, photoId):
        return self.googlePhotos.mediaItems().get(
            mediaItemId=photoId).execute()

    def getPhotoBinary(self, photo):
        return self.getPhotoBinaryFromBaseUrl(photo['baseUrl'])

    def getPhotoBinaryFromBaseUrl(self, baseUrl):
        baseUrl = baseUrl + "=d"
        res = request.urlopen(baseUrl)
        return res.read()

    def saveToFile(self, data, albumName, fileName, coverPhoto=False):
        if coverPhoto:
            filePath = topLevelDir + "\\" + albumName + "\\" + "Cover Photo\\" + fileName
        else:
            filePath = topLevelDir + "\\" + albumName + "\\" + fileName
        with open(filePath, "wb") as _file:
            _file.write(data)

    def makeAlbumDir(self, albumName):
        newDirName = topLevelDir + '\\' + albumName
        os.mkdir(newDirName)

    def makeCoverPhotoDir(self, albumName):
        newDirName = topLevelDir + '\\' + albumName + '\\' + "Cover Photo"
        os.mkdir(newDirName)

    def makeTagsDir(self, albumName):
        newDirName = topLevelDir + '\\' + albumName + '\\' + "Tags"
        os.mkdir(newDirName)

    def downloadAlbum(self, albumId):
        album = self.getAlbum(albumId)
        albumName = album['title']
        msg = "\tDownloading " + albumName + "..."
        self.logger.log(msg)

        coverPhotoBaseUrl = album['coverPhotoBaseUrl']
        coverPhotoMediaItemId = album['coverPhotoMediaItemId']

        self.makeAlbumDir(albumName)
        self.makeCoverPhotoDir(albumName)
        self.makeTagsDir(albumName)

        tags = self.getAlbumTags(albumName)
        self.writeTagsToFile(albumName, tags)

        coverPhoto = self.getPhoto(coverPhotoMediaItemId)
        data = self.getPhotoBinary(coverPhoto)
        self.saveToFile(data,
                        albumName,
                        coverPhoto['filename'],
                        coverPhoto=True)

        photos = self.listAlbumPhotos(albumId)

        for photo in photos:
            if photo['id'] != coverPhoto['id']:
                fileName = photo['filename']
                data = self.getPhotoBinary(photo)
                self.saveToFile(data, albumName, fileName)

        return albumName

    def getAlbumTags(self, albumName):
        tags = []
        with open("tags.pickle", "rb") as _file:
            tagCandidates = pickle.load(_file)
            for key in tagCandidates:
                if tagCandidates[key].lower() in albumName.lower():
                    if tagCandidates[key] not in tags:
                        tags.append(tagCandidates[key])
        return tags

    def writeTagsToFile(self, albumName, tags):
        tagsDir = topLevelDir + '\\' + albumName + '\\' + "Tags\\"
        tagsFile = tagsDir + "tags.txt"
        with open(tagsFile, "w") as _file:
            for tag in tags:
                _file.write(tag + "\n")

    def getAlbums(self):
        with open("albumList.txt") as _file:
            albums = _file.readlines()
            albums = [album.rstrip('\n') for album in albums]
            albums = [album.split(' :: ') for album in albums]
            return albums

    def downloadAlbums(self):

        numAlbums = len(self.getAlbums())
        failCount = 0
        failList = []

        msg = "Downloading " + str(numAlbums) + " albums..."
        self.logger.log(msg)

        for album in self.getAlbums():
            albumName = album[0]
            albumId = album[1]
            try:
                self.downloadAlbum(albumId)
                msg = "\tSuccess downloading " + albumName + "\n"
                self.logger.log(msg)
            except Exception as e:
                msg = "\t" + str(e) + "\n"
                self.logger.log(msg)
                failCount = failCount + 1
                failList.append(albumName)

        if failCount > 0:
            msg = "Failed to download " + str(failCount) + " albums"
            self.logger.log(msg)
            for name in failList:
                self.logger.log(name)
        else:
            msg = "All albums downloaded successfully"
            self.logger.log(msg)

        self.logger.endLog()
Beispiel #2
0
class Uploader(object):
    def __init__(self, api_key, api_secret):
        self.flickr = flickrapi.FlickrAPI(api_key, api_secret)
        self.authenticateFlickr()
        self.api_key = api_key
        self.logger = MessageLogger(logName="upload.log")

    def authenticateFlickr(self):
        if not self.flickr.token_valid(perms='write'):

            # Get a request token
            self.flickr.get_request_token(oauth_callback='oob')

            # Open a browser at the authentication URL. Do this however
            # you want, as long as the user visits that URL.
            authorize_url = self.flickr.auth_url(perms='write')
            webbrowser.open_new_tab(authorize_url)

            # Get the verifier code from the user. Do this however you
            # want, as long as the user gives the application the code.
            verifier = str(input('Verifier code: '))

            # Trade the request token for an access token
            self.flickr.get_access_token(verifier)

    def uploadPhoto(self, albumName, photoName, coverPhoto=False):
        if coverPhoto:
            filePath = topLevelDir + "\\" + albumName + "\\Cover Photo\\" + photoName
        else:
            filePath = topLevelDir + "\\" + albumName + "\\" + photoName

        return self.flickr.upload(filePath,
                                  FileWithCallback(filePath, callback))

    # Add a try block in this function to handle the exception
    # of trying to use an invalid filename
    def uploadAlbum(self, albumName):

        msg = "\tUploading " + albumName + "..."
        self.logger.log(msg)

        albumPath = topLevelDir + "\\" + albumName
        coverPath = albumPath + "\\Cover Photo"
        tagsPath = albumPath + "\\Tags\\tags.txt"

        with open(tagsPath, "r") as _file:
            tags = _file.readlines()
            tags = [tag.strip() for tag in tags]
            tags = " ".join(tags)

        _dir = os.listdir(coverPath)

        if len(_dir) == 0:
            raise Exception("The \"Cover Photo\" directory is empty")

        if len(_dir) > 1:
            raise Exception(
                "There can only be one photo in the \"Cover Photo\" directory")

        fileName = _dir[0]
        res = self.uploadPhoto(albumName, fileName, coverPhoto=True)
        if res is None:
            raise Exception("Failed to upload cover photo")

        coverPhotoId = self.getPhotoId(res)
        self.setPhotoTags(coverPhotoId, tags)
        res = self.createAlbum(albumName, coverPhotoId)
        if res is None:
            raise Exception("Failed to create album on Flickr")

        albumId = self.getPhotosetId(res)

        _dir = os.listdir(albumPath)
        for _file in _dir:
            if _file != "Cover Photo" and _file != "Tags":
                res = self.uploadPhoto(albumName, _file)
                photoId = self.getPhotoId(res)
                self.setPhotoTags(photoId, tags)
                self.addToAlbum(albumId, photoId)

    def createAlbum(self, albumName, primary_photo_id):
        return self.flickr._flickr_call(method='flickr.photosets.create',
                                        api_key=self.api_key,
                                        title=albumName,
                                        primary_photo_id=primary_photo_id)

    def addToAlbum(self, albumId, photoId):
        return self.flickr._flickr_call(method='flickr.photosets.addPhoto',
                                        api_key=self.api_key,
                                        photoset_id=albumId,
                                        photo_id=photoId)

    def setPhotoTags(self, photoId, tags):
        return self.flickr._flickr_call(method='flickr.photos.setTags',
                                        api_key=self.api_key,
                                        photo_id=photoId,
                                        tags=tags)

    def getPhotoId(self, res):
        return res[0].text

    def getPhotosetId(self, res):
        et = ET.fromstring(res.decode("utf-8"))
        return et[0].attrib['id']

    def getAlbums(self):
        with open("albumList.txt") as _file:
            albums = _file.readlines()
            albums = [album.rstrip('\n') for album in albums]
            albums = [album.split(' :: ')[0] for album in albums]
            return albums

    def uploadAlbums(self):

        numAlbums = len(self.getAlbums())
        failCount = 0
        failList = []

        msg = "Uploading " + str(numAlbums) + " albums..."
        self.logger.log(msg)

        for albumName in self.getAlbums():
            try:
                self.uploadAlbum(albumName)
                msg = "\tSuccess uploading " + albumName + "\n"
                self.logger.log(msg)
            except Exception as e:
                msg = "\t" + str(e) + "\n"
                self.logger.log(msg)
                failCount = failCount + 1
                failList.append(albumName)

        if failCount > 0:
            msg = "Failed to upload " + str(failCount) + " albums"
            self.logger.log(msg)
            for name in failList:
                self.logger.log(name)
        else:
            msg = "All albums uploaded successfully"
            self.logger.log(msg)

        self.logger.endLog()