Skip to content
Snippets Groups Projects
SaveMyPlaylist.py 5.45 KiB
Newer Older
  • Learn to ignore specific revisions
  • from __future__ import unicode_literals
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    import json
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    import logging
    
    
    import googleapiclient.discovery
    import googleapiclient.errors
    import youtube_dl
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    def prepare_logging() -> logging.Logger:
        LOG_FORMAT = '[%(levelname)-7s] - %(asctime)s - %(message)s'
        DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
    
        LOG_FORMATTER = logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT)
    
        logger = logging.getLogger('SaveMyPlaylist')
        logger.setLevel(logging.DEBUG)
    
        outHandler = logging.StreamHandler(sys.stdout)
        outHandler.setFormatter(LOG_FORMATTER)
        outHandler.setLevel(logging.DEBUG)
        outHandler.addFilter(lambda record: record.levelno <= logging.INFO)
        logger.addHandler(outHandler)
    
        errHandler = logging.StreamHandler(sys.stderr)
        errHandler.setFormatter(LOG_FORMATTER)
        errHandler.setLevel(logging.WARNING)
        logger.addHandler(errHandler)
        return logger
    
    
    logger = prepare_logging()
    
    
    
    class MyLogger(object):
        def debug(self, msg):
            pass
    
        def warning(self, msg):
            pass
    
        def error(self, msg):
    
            print(msg, file=sys.stderr)
    
    
    
    def my_hook(d):
        if d['status'] == 'finished':
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('Download finished!')
    
    
    
    class SaveMyPlaylist:
        SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
        API_NAME = 'youtube'
        API_VERSION = 'v3'
    
        CHANNEL = 0
        TITLE = 1
        VIDEO_ID = 2
    
        ILLEGAL_CHARS = ['NUL', '\',''//', ':', '*', '"', '<', '>', '|']
    
        def __init__(self, apiKey, playlistId):
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            with open('version.json', 'r', encoding='utf-8') as f:
                VERSION = json.load(f)['version']
    
            logger.info('### SaveMyPlaylist {} ###'.format(VERSION['name']))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('=============================')
    
            self._apiKey = apiKey
            self._playlistId = playlistId
            self._youtubeApi = googleapiclient.discovery.build(self.API_NAME, self.API_VERSION, developerKey=self._apiKey)
            self._items = self.__fetch_all_playlist_items()
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    
        def __fetch_all_playlist_items(self):
            items = []
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            nextPageToken = 0
            while nextPageToken is not None:
                pageItems, nextPageToken = self.__fetch_playlist_items(nextPageToken)
                items.extend(pageItems)
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('>>> Found {} items in playlist'.format(len(items)))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            return items
    
        def __fetch_playlist_items(self, nextPageToken=None):
            if nextPageToken is None or nextPageToken == 0:
    
                request = self._youtubeApi.playlistItems().list(
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                    part='snippet',
    
                    playlistId=self._playlistId,
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                    maxResults=50,
                )
            else:
    
                request = self._youtubeApi.playlistItems().list(
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                    part='snippet',
    
                    playlistId=self._playlistId,
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                    maxResults=50,
                    pageToken=nextPageToken
                )
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('Found {} videos in playlist'.format(len(response['items'])))
    
            for item in response['items']:
                snippet = item['snippet']
                title = snippet['title']
                channel = snippet['channelTitle']
                videoId = snippet['resourceId']['videoId']
                items.append((channel, title, videoId))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                logger.info('{} - {} (videoId: {})'.format(channel, title, videoId))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            nextPageToken = None
            if 'nextPageToken' in response:
                nextPageToken = response['nextPageToken']
    
            return items, nextPageToken
    
    
        def download_items(self, destinationFolder, debug=False):
    
            os.makedirs(destinationFolder, exist_ok=True)
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('>>> Scanning destination folder...')
    
            downloadedVideos = [f for f in os.listdir(destinationFolder) if
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                                os.path.isfile(os.path.join(destinationFolder, f)) and f.endswith('.mp4')]
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('>>> Found {} videos in destination folder'.format(len(downloadedVideos)))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('>>> Started Downloading...')
    
            for idx, item in enumerate(self._items):
    
                fileName = '{} - {}.mp4'.format(item[self.TITLE], item[self.CHANNEL])
                fileName = self.__escape_file_name(fileName)
                if fileName in downloadedVideos:
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                    logger.info('Skipping {}/{}: "{}" as it already exists'.format(idx + 1, len(self._items), fileName))
    
    Robert Goldmann's avatar
    Robert Goldmann committed
                logger.info('Downloading {}/{}: "{}"'.format(idx + 1, len(self._items), fileName))
    
                    'format': 'bestvideo+bestaudio',
    
                    'merge_output_format': 'mp4',
                    'outtmpl': os.path.join(destinationFolder, fileName),
                    'logger': MyLogger(),
    
                    'progress_hooks': [my_hook]
    
                }
    
                if debug:
                    continue
    
                with youtube_dl.YoutubeDL(ydl_opts) as ydl:
                    ydl.download(['https://www.youtube.com/watch?v={}'.format(item[self.VIDEO_ID])])
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            logger.info('>>> Finished Downloading')
            logger.info('Downloaded {} new videos'.format(len(newVideos)))
    
    
        def __escape_file_name(self, fileName):
            for char in self.ILLEGAL_CHARS:
                fileName = fileName.replace(char, '')
    
            return fileName
    
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    if __name__ == '__main__':
    
        with open("config/settings.json", "r") as f:
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            SETTINGS = json.load(f)
    
    Robert Goldmann's avatar
    Robert Goldmann committed
        saveMyPlaylist = SaveMyPlaylist(SETTINGS['apiKey'], SETTINGS['playlistId'])
        saveMyPlaylist.download_items(SETTINGS['destinationFolder'], SETTINGS['skipDownload'])