<gh_stars>1-10
import requests, json, threading, logging
logger = logging.getLogger(__name__)

class Video:
    def __init__(self, track, videoJSON):
        if not videoJSON['id']['kind'] == 'youtube#video':
            raise Exception('JSON was not a Youtube Video')
        self.id = videoJSON['id']['videoId']
        self.channelTitle = videoJSON['snippet']['channelTitle']
        self.publishDate = videoJSON['snippet']['publishedAt']
        self.title = videoJSON['snippet']['title']
        self.description = videoJSON['snippet']['description']
        self.thumbnail = videoJSON['snippet']['thumbnails']['default']['url']
        self.rank = _might_be_official(self, track)

    def __str__(self):
        return self.channelTitle + ' - ' + self.title

    def todict(self):
        return self.__dict__

def _build_findquery(client, track):
    return client.search().list(
        q='"%s" "%s" %s Official' % (track.name, track.artist, track.year),
        type="video",
        part="id,snippet",
        maxResults=10,
      )

def _set_suggestions(d, track):
    def _inner(id, resp, ex):
        if ex is not None:
            logger.error(ex)
            return

        videos = [Video(item) for item in resp.get('items')]
        videos.sort(lambda v1, v2: v1.rank - v2.rank)
        d[track] = videos

    return _inner

def _might_be_official(video, track):
    rank = 0
    channel, title = video.channelTitle.lower(), video.title.lower()
    if 'vevo' in channel:
        rank += 10

    if 'official audio' in title:
        rank -= 20
    elif 'official' in title:
        rank += 5

    if 'Auto-generated by YouTube.' in video.description:
        rank -= 20

    if track.artist.lower() in channel and not channel.endswith('- topic'):
        rank += 10

    if 'lyric' in title and 'lyric' not in track.name.lower() and 'lyric' not in track.artist.lower():
        rank -= 10

    return rank

def find_videos(client, tracks):
    batch = client.new_batch_http_request()
    videos = {}
    for t in tracks:
        batch.add(_build_findquery(client, t), _set_suggestions(videos, t))

    batch.execute()
    return videos

def _build_addquery(client, playlistid, video):
    return client.playlistItems().insert(
        part='snippet',
        body={
            'snippet':{
                'playlistId': playlistid,
                'resourceId': {
                    'kind': 'youtube#video',
                    'videoId': video.id
                }
            }
        }
    )

def createplaylist(client, title, videos):
    # There is a bug in the youtube api, adding as a batch will cause videos to
    # override one another, the workaround is to kick the playlist off with one
    # track, then spawn a new thread to add the rest sequentially

    resp = client.playlists().insert(
            part='snippet,status',
            body={
                'snippet': {'title': title},
                'status': {'privacyStatus': 'private'}
            }
        ).execute()

    playlistid = resp.get('id')
    _build_addquery(client, playlistid, videos[0]).execute()

    t = threading.Thread(target=_addvideos, args=(client, playlistid, videos[1:]))
    t.start()

    return playlistid

import requests, json, threading, logging
logger = logging.getLogger(__name__)

class Video:
    def __init__(self, track, videoJSON):
        if not videoJSON['id']['kind'] == 'youtube#video':
            raise Exception('JSON was not a Youtube Video')
        self.id = videoJSON['id']['videoId']
        self.channelTitle = videoJSON['snippet']['channelTitle']
        self.publishDate = videoJSON['snippet']['publishedAt']
        self.title = videoJSON['snippet']['title']
        self.description = videoJSON['snippet']['description']
        self.thumbnail = videoJSON['snippet']['thumbnails']['default']['url']
        self.rank = _might_be_official(self, track)

    def __str__(self):
        return self.channelTitle + ' - ' + self.title

    def todict(self):
        return self.__dict__

def _build_findquery(client, track):
    return client.search().list(
        q='"%s" "%s" %s Official' % (track.name, track.artist, track.year),
        type="video",
        part="id,snippet",
        maxResults=10,
      )

def _set_suggestions(d, track):
    def _inner(id, resp, ex):
        if ex is not None:
            logger.error(ex)
            return

        videos = [Video(item) for item in resp.get('items')]
        videos.sort(lambda v1, v2: v1.rank - v2.rank)
        d[track] = videos

    return _inner

def _might_be_official(video, track):
    rank = 0
    channel, title = video.channelTitle.lower(), video.title.lower()
    if 'vevo' in channel:
        rank += 10

    if 'official audio' in title:
        rank -= 20
    elif 'official' in title:
        rank += 5

    if 'Auto-generated by YouTube.' in video.description:
        rank -= 20

    if track.artist.lower() in channel and not channel.endswith('- topic'):
        rank += 10

    if 'lyric' in title and 'lyric' not in track.name.lower() and 'lyric' not in track.artist.lower():
        rank -= 10

    return rank

def find_videos(client, tracks):
    batch = client.new_batch_http_request()
    videos = {}
    for t in tracks:
        batch.add(_build_findquery(client, t), _set_suggestions(videos, t))

    batch.execute()
    return videos

def _build_addquery(client, playlistid, video):
    return client.playlistItems().insert(
        part='snippet',
        body={
            'snippet':{
                'playlistId': playlistid,
                'resourceId': {
                    'kind': 'youtube#video',
                    'videoId': video.id
                }
            }
        }
    )

def createplaylist(client, title, videos):
    # There is a bug in the youtube api, adding as a batch will cause videos to
    # override one another, the workaround is to kick the playlist off with one
    # track, then spawn a new thread to add the rest sequentially

    resp = client.playlists().insert(
            part='snippet,status',
            body={
                'snippet': {'title': title},
                'status': {'privacyStatus': 'private'}
            }
        ).execute()

    playlistid = resp.get('id')
    _build_addquery(client, playlistid, videos[0]).execute()

    t = threading.Thread(target=_addvideos, args=(client, playlistid, videos[1:]))
    t.start()

    return playlistid
#video': #video', # There is a bug in the youtube api, adding as a batch will cause videos to # override one another, the workaround is to kick the playlist off with one # track, then spawn a new thread to add the rest sequentially