<commit_before>/* global require, $, _, Backbone, gapi, API */

var FORMAT_YT = 1
var FORMAT_SC = 2

var OAUTH2_CLIENT_ID = '440141609143-o7ujhbv1eute4bc1u3sregmansq768ue.apps.googleusercontent.com'
var OAUTH2_SCOPES = [ 'https://www.googleapis.com/auth/youtube' ]

function toEta (s) {
  var m = Math.floor(s / 60)
  s %= 60
  return m + ':' + (s < 10 ? '0' + s : s)
}

function toJQueryDeferred (thenable) {
  return $.Deferred(function (def) {
    thenable.then(def.resolve, def.reject)
  })
}

function resolved () {
  return $.Deferred(function (def) {
    def.resolve()
  })
}

function wait (ms) {
  return $.Deferred(function (def) {
    setTimeout(def.resolve, ms)
  })
}

function fetchPlaylist (id) {
  return $.getJSON('/_/playlists/' + id + '/media')
    .then(function (result) { return result.data })
}

function getUsername () {
  return API.getUser().username
}

function getPlaylists () {
  return _.find(require.s.contexts._.defined, function (m) {
    return m && m instanceof Backbone.Collection && typeof m.jumpToMedia === 'function'
  })
}

function getPlaylistName (playlist) {
  return playlist.name + ' - ' + getUsername() + ' (Exported from plug.dj)'
}

var _authedWithYouTube = false
function authYouTube () {
  if (_authedWithYouTube) {
    return resolved()
  }
  gapi.client.setApiKey('<KEY>')

  return $.Deferred(function (def) {
    gapi.auth.init(function () {
      gapi.auth.authorize({
        client_id: OAUTH2_CLIENT_ID,
        scope: OAUTH2_SCOPES,
        immediate: true
      }, function onAuthComplete (res) {
        if (res && !res.error) {
          def.resolve()
        } else {
          API.chatLog('Please press the button below to log in to YouTube, so playlists can be exported to your account.')

          var button = $('<button />')
            .css({ padding: '5px' })
            .text('Log in to YouTube')
            .on('click', function () {
              gapi.auth.authorize({
                client_id: OAUTH2_CLIENT_ID,
                scope: OAUTH2_SCOPES,
                immediate: false
              }, function (res) {
                if (res && !res.error) {
                  def.resolve()
                } else {
                  def.reject(res ? res.error : null)
                }
              })
            })

          var message = $('<div />').addClass('cm log').append(
            $('<div />').addClass('msg').append(
              $('<div />').addClass('text cid-ytexport').append(
                button
              )
            )
          )

          $('#chat-messages')
            .append(message)
            .scrollTop(1e8)
        }
      })
    })
  }).then(function () {
    _authedWithYouTube = true
  })
}

function createYouTubePlaylist (name) {
  return toJQueryDeferred(gapi.client.youtube.playlists.insert({
    part: 'snippet,status',
    resource: {
      snippet: {
        title: name.replace(/[<>]/g, ''),
        description: 'Exported from Plug.dj'
      },
      status: {
        privacyStatus: 'unlisted'
      }
    }
  }))
}

function insertYouTubeItem (playlistId, media) {
  return toJQueryDeferred(gapi.client.youtube.playlistItems.insert({
    part: 'snippet,contentDetails',
    resource: {
      snippet: {
        playlistId: playlistId,
        resourceId: {
          kind: 'youtube#video',
          videoId: media.cid
        }
      },
      contentDetails: {
        note: media.author + ' - ' + media.title
      }
    }
  }))
}

function exportPlaylist (playlist) {
  var playlistId = null
  var promise = createYouTubePlaylist(getPlaylistName(playlist))
    .then(function (res) {
      console.log(res)
      playlistId = res.result.id
    })
  return function (media) {
    media.forEach(function (item) {
      if (item.format === FORMAT_YT) {
        promise = promise.then(function () {
          return $.Deferred(function (def) {
            insertYouTubeItem(playlistId, item)
              .then(def.resolve)
              .fail(function (res) {
                API.chatLog('Could not add "' + item.author + ' - ' + item.title + '". The video might have been deleted.')
                def.resolve()
              })
          })
        })
      } else if (item.format === FORMAT_SC) {
        API.chatLog('Ignoring SoundCloud media "' + item.author + ' - ' + item.title + '" ):')
      }
    })

    promise = promise.then(function () {
      return playlistId
    })

    return promise
  }
}

function exportPlaylists () {
  var playlists = getPlaylists().map(function (p) { return p.toJSON() })<commit_msg>check for unavailable videos first, and do not export them<commit_after>/* global require, $, _, Backbone, gapi, API */

var FORMAT_YT = 1
var FORMAT_SC = 2

var OAUTH2_CLIENT_ID = '440141609143-o7ujhbv1eute4bc1u3sregmansq768ue.apps.googleusercontent.com'
var OAUTH2_SCOPES = [ 'https://www.googleapis.com/auth/youtube' ]

function toEta (s) {
  var m = Math.floor(s / 60)
  s %= 60
  return m + ':' + (s < 10 ? '0' + s : s)
}

function toJQueryDeferred (thenable) {
  return $.Deferred(function (def) {
    thenable.then(def.resolve, def.reject)
  })
}

function resolved () {
  return $.Deferred(function (def) {
    def.resolve()
  })
}

function wait (ms) {
  return $.Deferred(function (def) {
    setTimeout(def.resolve, ms)
  })
}

function fetchPlaylist (id) {
  return $.getJSON('/_/playlists/' + id + '/media')
    .then(function (result) { return result.data })
}

function getUsername () {
  return API.getUser().username
}

function getPlaylists () {
  return _.find(require.s.contexts._.defined, function (m) {
    return m && m instanceof Backbone.Collection && typeof m.jumpToMedia === 'function'
  })
}

function getPlaylistName (playlist) {
  return playlist.name + ' - ' + getUsername() + ' (Exported from plug.dj)'
}

var _authedWithYouTube = false
function authYouTube () {
  if (_authedWithYouTube) {
    return resolved()
  }
  gapi.client.setApiKey('<KEY>')

  return $.Deferred(function (def) {
    gapi.auth.init(function () {
      gapi.auth.authorize({
        client_id: OAUTH2_CLIENT_ID,
        scope: OAUTH2_SCOPES,
        immediate: true
      }, function onAuthComplete (res) {
        if (res && !res.error) {
          def.resolve()
        } else {
          API.chatLog('Please press the button below to log in to YouTube, so playlists can be exported to your account.')

          var button = $('<button />')
            .css({ padding: '5px' })
            .text('Log in to YouTube')
            .on('click', function () {
              gapi.auth.authorize({
                client_id: OAUTH2_CLIENT_ID,
                scope: OAUTH2_SCOPES,
                immediate: false
              }, function (res) {
                if (res && !res.error) {
                  def.resolve()
                } else {
                  def.reject(res ? res.error : null)
                }
              })
            })

          var message = $('<div />').addClass('cm log').append(
            $('<div />').addClass('msg').append(
              $('<div />').addClass('text cid-ytexport').append(
                button
              )
            )
          )

          $('#chat-messages')
            .append(message)
            .scrollTop(1e8)
        }
      })
    })
  }).then(function () {
    _authedWithYouTube = true
  })
}

function getYouTubeVideoStatuses (media) {
  var current = []
  var chunks = [ current ]
  media = media.filter(function (m) { return m.format === FORMAT_YT })
  media.forEach(function (item) {
    if (current.length < 50) {
      current.push(item)
    }
    else {
      current = [ item ]
      chunks.push(current)
    }
  })

  var videos = []
  return chunks
    .reduce(function (promise, chunk) {
      return promise.then(function (res) {
        if (res) {
          videos = videos.concat(res.result.items)
        }
        return toJQueryDeferred(gapi.client.youtube.videos.list({
          part: 'status',
          id: chunk.map(function (m) { return m.cid }).join(',')
        }))
      })
    }, resolved())
    .then(function (res) {
      return videos.concat(res.result.items)
    })
}

function createYouTubePlaylist (name) {
  return toJQueryDeferred(gapi.client.youtube.playlists.insert({
    part: 'snippet,status',
    resource: {
      snippet: {
        title: name.replace(/[<>]/g, ''),
        description: 'Exported from Plug.dj'
      },
      status: {
        privacyStatus: 'unlisted'
      }
    }
  }))
}

function insertYouTubeItem (playlistId, media) {
  return toJQueryDeferred(gapi.client.youtube.playlistItems.insert({
    part: 'snippet,contentDetails',
    resource: {
      snippet: {
        playlistId: playlistId,
        resourceId: {
          kind: 'youtube#video',
          videoId: media.cid
        }
      },
      contentDetails: {
        note: media.author + ' - ' + media.title
      }
    }
  }))
}

function exportPlaylist (playlist) {
  var playlistId = null
  var promise = createYouTubePlaylist(getPlaylistName(playlist))
    .then(function (res) {
      console.log(res)
      playlistId = res.result.id
    })
  return function (media) {
    // find YT video status, so we can ignore deleted videos
    promise = promise.then(function () {
      return getYouTubeVideoStatuses(media)
    })

    promise = promise.then(function (videos) {
      // nested promise for an easy loop!
      var promise = resolved()
      media.forEach(function (item) {
        if (item.format === FORMAT_YT) {
          // ignore videos that have been terminated / are unavailable
          var video = _.findWhere(videos, { id: item.cid })
          if (!video || !video.status || video.status.uploadStatus !== 'processed') {
            API.chatLog('Could not add "' + item.author + ' - ' + item.title + '". The video might have been deleted.')
            return
          }

          promise = promise.then(function () {
            return $.Deferred(function (def) {
              insertYouTubeItem(playlistId, item)
                .then(def.resolve)
                .fail(function (res) {
                  API.chatLog('Could not add "' + item.author + ' - ' + item.title + '". The video might have been deleted.')
                  def.resolve()
                })
            })
          })
        } else if (item.format === FORMAT_SC) {
          API.chatLog('Ignoring SoundCloud media "' + item.author + ' - ' + item.title + '" ):')
        }
      })
      return promise
    })

    promise = promise.then(function () {
      return playlistId
    })

    return promise
  }
}

function exportPlaylists () {
  var playlists = getPlaylists().map(function (p) { return p.toJSON() })
{'fraction_non_alphanumeric': 0.13864168618266978, 'fraction_numerical': 0.007306791569086651, 'mean_word_length': 2.059025787965616, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6210681', 'n_tokens_mistral': 3392, 'n_tokens_neox': 3311, 'n_words': 706}