Greasy Fork is available in English.

Download Video as MP4 [video.sibnet.ru+myvi.ru]

Скачать видео с myvi.ru, video.sibnet.ru одним кликом, правильные названия видео при скачивании

התקן את הסקריפט?
סקריפטים מומלצים של יוצר זה

אולי תאהב גם את Vk Media Downloader.

התקן את הסקריפט
// ==UserScript==
// @name           Download Video as MP4 [video.sibnet.ru+myvi.ru]
// @name:en        Download Video as MP4 [video.sibnet.ru+myvi.ru]
// @namespace      http://video.sibnet.ru
// @version        3.0.0
// @description    Скачать видео с myvi.ru, video.sibnet.ru одним кликом, правильные названия видео при скачивании
// @description:en Download video from myvi.ru and video.sibnet.ru by a simple click, true filenames of downloaded videos
// @author         EisenStein
// @include        https://video.sibnet.ru/*
// @include        https://cv*.sibnet.ru/*
// @include        https://dv*.sibnet.ru/*
// @include        https://myvi.ru/*
// @include        https://www.myvi.ru/*
// @include        https://fs*.myvi.ru*
// @include        https://myvi.tv/*
// @include        https://www.myvi.tv/*
// @include        https://fs*.myvi.tv*
// @include        https://fs.mikadox.com*
// @connect        sibnet.ru
// @connect        myvi.tv
// @connect        myvi.ru
// @connect        mikadox.com
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_deleteValue
// @grant          GM_addStyle
// @grant          GM_info
// @grant          GM_download
// @grant          GM_xmlhttpRequest
// @grant          GM.getValue
// @grant          GM.setValue
// @grant          GM.deleteValue
// @grant          GM.addStyle
// @grant          GM.info
// @grant          GM.download
// @grant          GM.xmlHttpRequest
// @grant          unsafeWindow
// @require        https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @run-at         document-start
// ==/UserScript==

(function(window, WINDOW) {
  (function(e, a) { for(var i in a) e[i] = a[i]; }(exports,  (function(modules) { // webpackBootstrap
 	// The module cache
 	var installedModules = {};

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Check if module is in cache
 		if(installedModules[moduleId]) {
 			return installedModules[moduleId].exports;
 		}
 		// Create a new module (and put it into the cache)
 		var module = installedModules[moduleId] = {
 			i: moduleId,
 			l: false,
 			exports: {}
 		};

 		// Execute the module function
 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

 		// Flag the module as loaded
 		module.l = true;

 		// Return the exports of the module
 		return module.exports;
 	}


 	// expose the modules object (__webpack_modules__)
 	__webpack_require__.m = modules;

 	// expose the module cache
 	__webpack_require__.c = installedModules;

 	// define getter function for harmony exports
 	__webpack_require__.d = function(exports, name, getter) {
 		if(!__webpack_require__.o(exports, name)) {
 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
 		}
 	};

 	// define __esModule on exports
 	__webpack_require__.r = function(exports) {
 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
 		}
 		Object.defineProperty(exports, '__esModule', { value: true });
 	};

 	// create a fake namespace object
 	// mode & 1: value is a module id, require it
 	// mode & 2: merge all properties of value into the ns
 	// mode & 4: return value when already ns object
 	// mode & 8|1: behave like require
 	__webpack_require__.t = function(value, mode) {
 		if(mode & 1) value = __webpack_require__(value);
 		if(mode & 8) return value;
 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
 		var ns = Object.create(null);
 		__webpack_require__.r(ns);
 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
 		return ns;
 	};

 	// getDefaultExport function for compatibility with non-harmony modules
 	__webpack_require__.n = function(module) {
 		var getter = module && module.__esModule ?
 			function getDefault() { return module['default']; } :
 			function getModuleExports() { return module; };
 		__webpack_require__.d(getter, 'a', getter);
 		return getter;
 	};

 	// Object.prototype.hasOwnProperty.call
 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

 	// __webpack_public_path__
 	__webpack_require__.p = "";


 	// Load entry module and return exports
 	return __webpack_require__(__webpack_require__.s = 14);
 })

 ([
/* 0 */
 (function(module, exports, __webpack_require__) {

var eventEmitter = __webpack_require__(8)

var LOGGER = {
  log: window.console.log.bind(window.console),
  info: window.console.info.bind(window.console),
  warn: window.console.warn.bind(window.console),
  error: window.console.error.bind(window.console),
  debug: window.console.debug.bind(window.console),
}

var noop = function () {}

function Logger(logger) {
  this.logger = Object.assign({}, LOGGER, logger, { debug: noop })
  var _this = this
  eventEmitter.on('logger', function (options) {
    try {
      _this.update(options)
    } catch (e) {
      console.error(e)
    }
  })
}

/**
 * @param {{ [x: string]: boolean | (...args: any[]) => void}} logger
 */
Logger.prototype.update = function (logger) {
  var keys = Object.keys(logger)
  for (var key of keys) {
    if (logger[key] === true) {
      this.logger[key] = LOGGER[key] || noop
    } else if (typeof logger[key] === 'function') {
      this.logger[key] = logger[key]
    }
  }
}

Logger.prototype.log = function () {
  return this.logger.log.apply(this.logger, arguments)
}
Logger.prototype.debug = function () {
  return this.logger.debug.apply(this.logger, arguments)
}
Logger.prototype.info = function () {
  return this.logger.info.apply(this.logger, arguments)
}
Logger.prototype.warn = function () {
  return this.logger.warn .apply(this.logger, arguments)
}
Logger.prototype.error = function () {
  return this.logger.error.apply(this.logger, arguments)
}

module.exports = Logger

 }),
/* 1 */
 (function(module, exports) {

const time = function() {
  return '[' + new Date().toISOString() + ']'
}

module.exports = time

 }),
/* 2 */
 (function(module, exports, __webpack_require__) {

var Logger = __webpack_require__(0)
var time = __webpack_require__(1)
var parseAJAXHeaders = __webpack_require__(17)
var parseAJAXResponse = __webpack_require__(18)

/**
 * @typedef {{
   method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'PATCH';
   url: string;
   headers?: { [x: string]: (string | number) };
   responseType?: string;
   data?: any;
   onprogress?: (loaded: number, total: number) => void;
 }} IRequestDetails
 * @typedef {{
   ok: boolean;
   status: number;
   headers: { [x: string]: string };
   problem?: string;
   data?: T;
 }} IResponse<T = any>
 */

/**
 * @param {IRequestDetails} details
 * @return {XMLHttpRequest | ActiveXObject} details
 */
function __XMLHttpRequest(details) {
  var xhr;
  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest()
  } else if (window.ActiveXObject) {
    xhr = new ActiveXObject('Msxml2.XMLHTTP.6.0')
  } else {
    return null
  }
  xhr.open(details.method, details.url, true)
  Object.keys(details.headers).forEach(function (key) {
    xhr.setRequestHeader(key, details.headers[key])
  })
  if (details.responseType) {
    xhr.responseType = details.responseType
  }
  if (details.timeout) {
    xhr.timeout = details.timeout
  }
  return xhr
}

/**
 * @param {string | IRequestDetails} details
 * @param {boolean} [useGM]
 * @return {Promise<IResponse>}
 */
function makeRequest(options, useGM = false) {
  var logger = new Logger()
  logger.debug(time(), 'makeRequest options', options, { useGM: useGM })
  var details = {
    method: 'GET',
    headers: {},
  }
  if (typeof details === 'string') {
    details = Object.assign(details, { url: options })
  } else {
    details = Object.assign(details, options)
  }
  // Response
  var response = {
    ok: false,
    problem: undefined,
    headers: {},
    status: 0,
    data: undefined,
    finalUrl: details.url,
  }
  var resolve
  var promise = new Promise(function (r) {
    resolve = r
  })
  var onLoad = function (e) {
    var isGM = !e.target
    var req = isGM ? e : e.target
    response.status = req.status
    response.headers = parseAJAXHeaders(isGM ? req.responseHeaders : req.getAllResponseHeaders())
    response.ok = req.status >= 200 && req.status < 300
    response.problem = response.ok ? undefined : response.problem
    var isText = !req.responseType || req.responseType.toLowerCase() === 'text'
    response.data = parseAJAXResponse(isText && !isGM ? req.responseText : req.response, response.headers, req.responseType)
    response.finalUrl = req.finalUrl || req.responseURL || response.finalUrl
    logger.debug(time(), 'makeRequest response: ', response, details)
    return response
  }
  var onTimeout = function (e) {
    var isGM = !e.target
    var req = isGM ? e : e.target
    logger.error(time(), 'makeRequest timeout', req.status, req.readyState)
    response.status = req.status
    response.ok = false
    response.problem = 'TIMEOUT'
    return response
  }
  var onError = function (e) {
    var isGM = !e.target
    var req = isGM ? e : e.target
    
    logger.error(time(), 'makeRequest error', req.status, req.readyState)
    response.status = req.status
    response.problem = req.status.toString()
    response.ok = false
    return response
  }
  var onProgress = function (e) {
    if (typeof details.onprogress === 'function') {
      details.onprogress(e.loaded, e.total)
    }
  }
  
  if (!useGM || typeof GM === 'undefined' || typeof GM.xmlHttpRequest === 'undefined') {
    var xhr = new __XMLHttpRequest(details)
    xhr.addEventListener('load', function (e) {
      onLoad(e)
    })
    xhr.addEventListener('timeout', function (e) {
      onTimeout(e)
    })
    xhr.addEventListener('error', function (e) {
      onError(e)
    })
    xhr.addEventListener('loadend', function (e) {
      resolve(response)
    })
    xhr.addEventListener('progress', function (e) {
      onProgress(e)
    })
    xhr.send(details.data)
  } else {
    GM.xmlHttpRequest(Object.assign({}, details, {
      onload: function (req) {
        var r = onLoad(req)
        resolve(r)
      },
      onerror: function (req) {
        var r = onError(req)
        resolve(r)
      },
      ontimeout: function (req) {
        var r = onTimeout(req)
        resolve(r)
      },
      onprogress: function (req) {
        onProgress(req)
      },
    }))
  }
  return promise
}

module.exports = makeRequest

 }),
/* 3 */
 (function(module, exports) {

/**
 * @param {number} timeout
 * @return {Promise<void>}
 */
function delay(timeout) {
  return new Promise(function (resolve) {
    setTimeout(resolve, timeout)
  })
}

module.exports = delay

 }),
/* 4 */
 (function(module, exports) {

const noop = function(){}

/**
 * @param {() => void} [callback]
 * @return {Promise<void>}
 */
function DOMReady(callback) {
  callback = typeof callback === 'function' ? callback : noop
  if (document.readyState === 'loading') {
    var resolve
    var promise = new Promise(function(r){
      resolve = r
    })
    document.addEventListener('DOMContentLoaded', function(){
      callback()
      resolve()
    })
    return promise
  }
  callback()
  return Promise.resolve()
}

module.exports = DOMReady

 }),
/* 5 */
 (function(module, exports) {

var link
/**
 * @param {string} url
 * @return {HTMLElement}
 */
function URLParse(url) {
  link = link || document.createElement('a')
  link.href = url
  return link.cloneNode()
}

module.exports = URLParse

 }),
/* 6 */
 (function(module, exports, __webpack_require__) {

var eventEmitter = __webpack_require__(8)
var Logger = __webpack_require__(0)
var time = __webpack_require__(1)

/**
 * @param {{
   videoId: string;
   url: string;
   filename: string;
   name: string;
   ext: string;
   event: string;
 }} IEventData
 */

var iframeChannel = {
  logger: new Logger(),
  init: function () {
    window.addEventListener('message', iframeChannel.onMessage)
  },
  /** @param {IEventData} data */
  getEventName: function (data) {
    return data && data.videoId && data.event ? (data.videoId + '-' + data.event) : ''
  },
  /** @param {IEventData} data */
  request: function (data) {
    var event = iframeChannel.getEventName(data)
    var logger = iframeChannel.logger;
    logger.debug(time(), 'iframeChannel event = ', event, data)
    if (!event) {
      logger.error(time(), 'iframeChannel request: invalid data', data)
      return Promise.reject(new Error('invalid data'))
    }
    var resolve
    var promise = new Promise(function (res) {
      resolve = res
    })
    eventEmitter.once(event, function (response) {
      logger.debug(time(), 'iframeChannel response ', event, response)
      resolve(response)
    })
    
    iframeChannel.send(data)
    return promise
  },
  /** @param {IEventData} data */
  send: function (data) {
    var videoId = data.videoId
    var id = 'video_iframe_' + videoId
    var iframe = document.querySelector('#' + id)
    var link = document.createElement('a')
    link.href = data.url
    if (!iframe) {
      iframe = document.createElement('iframe')
      iframe.id = id
      iframe.src = link.href + '#DM:' + encodeURIComponent(JSON.stringify(data))
      iframe.style.width = '1px'
      iframe.style.height = '1px'
      iframe.style.visibility = 'hidden'
      document.body.appendChild(iframe)
    } else {
      iframe.contentWindow.postMessage(data, '*')
    }
    return iframe
  },
  onMessage: function (e) {
    var event = iframeChannel.getEventName(e.data)
    if (event) {
      eventEmitter.emit(event, e.data)
    }
  },
}

module.exports = iframeChannel

 }),
/* 7 */
 (function(module, exports) {

var has_gm_info = function () {
  return typeof GM !== 'undefined' && typeof GM.info !== 'undefined'
}

function getScriptName() {
  if (has_gm_info()) {
    return GM.info.script.name
  }
  return 'Video Download Manager'
}

function getScriptVersion() {
  if (has_gm_info()) {
    return GM.info.script.version
  }
  return '1.0.0';
}

function getScriptAuthor() {
  if (has_gm_info()) {
    return GM.info.script.author
  }
  return 'eisen-stein'
}

function getScriptHandler() {
  if (has_gm_info()) {
    return GM.info.scriptHandler
  }
  return 'none'
}

module.exports = {
  script_name: getScriptName() || 'Video Download Manager',
  script_version: getScriptVersion() ? ('v' + getScriptVersion()) : 'v3.0.0',
  script_author: getScriptAuthor() || 'eisen-stein',
  script_handler: getScriptHandler() || 'none',
}


 }),
/* 8 */
 (function(module, exports, __webpack_require__) {

var EventEmitter = __webpack_require__(15)

module.exports = new EventEmitter()

 }),
/* 9 */
 (function(module, exports, __webpack_require__) {

var URLParse = __webpack_require__(5)
var time = __webpack_require__(1)
var Logger = __webpack_require__(0)
var downloadFile = __webpack_require__(16)
var makeRequest = __webpack_require__(2)
var DOMReady = __webpack_require__(4)
var delay = __webpack_require__(3)

var iframeController = {
  logger: new Logger(),
  start: function () {
    var logger = this.logger
    var _this = this
    
    var onData = function (data) {
      if (data && data.videoId && data.event === 'url') {
        logger.debug(time(), 'iframe getUrl', location.href)
        return _this.getUrl(data)
      }
      if (data && data.videoId && data.event === 'size') {
        logger.debug(time(), 'iframe getSize', location.href)
        return _this.getSize(data)
      }
      if (data && data.videoId && data.event === 'download') {
        logger.debug(time(), 'iframe download', location.href)
        return _this.download(data)
      }
      return Promise.resolve()
    }
    
    window.addEventListener('message', function (e) {
      logger.debug(time(), 'iframe onmessage: ', e.origin, e.data)
      onData(e.data)
    })
    
    return Promise.all([
      _this.removeVideo().catch(function() {}),
      onData(_this.getData()),
    ]).then(function () {
      logger.debug(time(), 'iframe started')
    }).catch(function (e) {
      logger.error(time(), 'iframe error: ', e)
    })
  },
  download: function (dt) {
    var data = dt || this.getData()
    var link = URLParse(location.href)
    var _this = this
    link.hash = ''
    return downloadFile(link.href, data.filename, function onProgress(loaded, total) {
      _this.onProgress(loaded, total, data)
    }).then(function () {
      _this.sendMessage(Object.assign({}, data, { event: 'download' }))
    })
  },
  getUrl: function (dt) {
    var data = dt || this.getData()
    
    var response = {
      ok: true,
      finalUrl: location.href,
      status: 200,
      readyState: 4,
      headers: {},
      
      event: 'url',
      videoId: data.videoId,
    }
    this.sendMessage(response)
  },
  getSize: function (dt) {
    var data = dt || this.getData()
    var _this = this
    
    return makeRequest({
      method: 'HEAD',
      url: location.href,
      headers: {
        range: 'bytes=0-10',
      },
    }).then(function (response) {
      _this.sendMessage(
        Object.assign(
          {},
          response,
          {
            event: 'size',
            videoId: data.videoId,
          },
        )
      )
    })
  },
  /** @param {number} [timeout] */
  removeVideo: function (timeout) {
    if (!location.hash || !location.hash.match(/^#DM\:/)) {
      return Promise.resolve()
    }
    return DOMReady().then(function () {
      var video = document.querySelector('video')
      video.removeAttribute('autoplay')
      video.setAttribute('preload', 'none')
      video.pause(0)
      video.src = ''
      video.parentNode.removeChild(video)
      return video
    })
  },
  sendMessage: function (message) {
    this.logger.debug(time(), 'iframe sending.. ', message)
    if (window.parent) {
      this.logger.debug(time(), 'iframe send window', window.parent)
      window.parent.postMessage(message, '*')
    }
  },
  onProgress: function (loaded, total, dt) {
    var data = dt || this.getData()
    this.sendMessage(Object.assign(data, {
      event: 'progress',
      loaded: loaded,
      total: total,
    }))
  },
  /**
   * @return {{
     url: string;
     name: string;
     filename: string;
     videoId: string;
     ext: string;
     event: string;
   }}
   */
  getData: function () {
    if (this._data) {
      return Object.assign({}, this._data)
    }
    if (location.hash && location.hash.match(/^#DM\:/)) {
      this._data = JSON.parse(decodeURIComponent(location.hash.slice(4)))
      location.hash = '';
      this._data.url = location.href
      return Object.assign({}, this._data)
    }
  },
}

module.exports = iframeController

 }),
/* 10 */
 (function(module, exports, __webpack_require__) {

var Logger = __webpack_require__(0)
var time = __webpack_require__(1)
var smartSize = __webpack_require__(21)
__webpack_require__(22)

/**
 * @typedef {{
   id?: string | number;
   visible?: boolean;
   title: string;
   onDownload?: (onProgress?: (loaded: number, total: number) => void) => void;
   getSize?: () => void;
   getUrl?: () => void;
   videoId?: number;
   filename?: string;
   filesize?: number | string;
   fileurl?: string;
   progress?: number;
   disabled?: boolean;
   icon?: { width?: number; height?: number; color?: string };
 }} IMainViewProps
 */

var baseView = {
  logger: new Logger(),
  /** @param {IMainViewProps} props */
  create: function (props) {
    /** @type {IMainViewProps} */
    this.props = props
    this._createDOM()
    this.onDownload = this.onDownload.bind(this)
    this.onClose = this.onClose.bind(this)
    this.getSize = this.getSize.bind(this)
    this.getUrl = this.getUrl.bind(this)
    this.getId = this.getId.bind(this)
    this.init = this.init.bind(this)
    document.body.insertBefore(this._root, document.body.firstElementChild)
    this.render()
    
    this.init()
  },
  init: function () {
    var _this = this
    this.getId()
    return this.getUrl().then(function () {
      return _this.getSize()
    })
  },
  _createDOM: function () {
    var div = document.createElement('div')
    this.id = this.props.id || ('vdm-' + Math.floor(Math.random() * 1e5))
    div.innerHTML = [
    '<div id="' + this.id + '" class="vdm-root">',
      '<div class="vdm-container">',
        '<div class="vdm-header">',
          '<span class="vdm-title">',
            (this.props.title || ''),
          '</span>',
          '<div class="vdm-close-btn">',
            '<div></div>',
          '</div>',
        '</div>',
        '<div class="vdm-content">',
          '<div class="vdm-item vdm-filename">',
            '<span>Название: <span>{filename}</span></span>',
          '</div>',
          '<div class="vdm-item vdm-filesize">',
            '<span>Размер: <span>{filesize}</span></span>',
          '</div>',
          '<div class="vdm-item vdm-fileurl">',
            '<span>',
              '<input type="checkbox" style="display:none" id="video_href" ></input>',
              '<span>',
                '<label class="show_link" for="video_href">Показать ссылку</label>',
                '<label class="video_link" for="video_href">Ссылка: </label>',
                '<a target="_blank">{fileurl}</a>',
              '</span>',
            '</span>',
          '</div>',
          '<div class="vdm-controllers">',
            '<div class="vdm-progress">',
            '</div>',
            '<div class="vdm-button vdm-download">',
              '<span>Скачать</span>',
            '</div>',
          '</div>',
        '</div>',
      '</div>',
    '</div>'].join('');
    
    this._root = div.firstElementChild
    return this._root
  },
  onDownload: function (e) {
    var logger = this.logger
    if (this.props.onDownload && !this.props.disabled && !this.props.isFetching) {
      this.update({ isFetching: true })
      var _this = this
      logger.debug(time(), 'downloading..')
      return this.props.onDownload(function onProgress(loaded, total) {
        _this.update({ progress: loaded / total })
      }).then(function () {
        _this.update({ isFetching: false, progress: 0 })
        logger.debug(time(), 'download complete')
        return true
      })
    }
    return Promise.resolve()
  },
  getSize: function () {
    var logger = this.logger
    if (this.props.getSize && !this.props.isFetching) {
      this.update({ isFetching: true })
      var _this = this
      return this.props.getSize().then(function (size) {
        logger.debug(time(), 'size: ', size)
        var filesize = smartSize(size)
        logger.info(time(), 'filesize: ', filesize)
        _this.update({ filesize: filesize, isFetching: false })
        return size
      })
    }
    return Promise.resolve()
  },
  getUrl: function () {
    var logger = this.logger;
    if (this.props.getUrl && !this.props.isFetching) {
      var _this = this
      this.update({ isFetching: true })
      return this.props.getUrl().then(function (fileurl) {
        logger.info(time(), 'fileurl: ', fileurl)
        _this.update({ fileurl: fileurl, isFetching: false })
        return fileurl
      })
    }
    return Promise.resolve()
  },
  getId: function () {
    var videoId = this.props.getId()
    this.update({ videoId: videoId, visible: Boolean(videoId) && this.props.visible })
  },
  onClose: function (e) {
    if (this.props.onClose) {
      this.props.onClose()
    }
    this.update({ visible: false })
  },
  loadingImage: function () {
    var icon = this.props.icon
    return `<svg
      xmlns="http://www.w3.org/2000/svg"
      version="1.1"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:svgjs="http://svgjs.com/svgjs"
      width="${icon && icon.width || '512'}"
      height="${icon && icon.height || '512'}"
      x="0"
      y="0"
      viewBox="0 0 16 16"
      style="enable-background:new 0 0 512 512"
      xml:space="preserve"
      class="loading"
    >
      <g>
        <path
          xmlns="http://www.w3.org/2000/svg"
          fill="${icon && icon.color || '#ffffff'}"
          d="M9.9 0.2l-0.2 1c3 0.8 5.3 3.5 5.3 6.8 0 3.9-3.1 7-7 7s-7-3.1-7-7c0-3.3 2.3-6 5.3-6.8l-0.2-1c-3.5 0.9-6.1 4.1-6.1 7.8 0 4.4 3.6 8 8 8s8-3.6 8-8c0-3.7-2.6-6.9-6.1-7.8z"
          style=""
          class=""
        >
        </path>
      </g>
    </svg>`;
  },
  render: function () {
    var visible = this.props.visible
    var root = this._root = this._root || this._createDOM()
    root.style.visibility = visible ? 'initial' : 'hidden'
    root.querySelector('.vdm-title').innerHTML = this.props.title
    if (this.props.filename) {
      root.querySelector('.vdm-filename span span').innerHTML = this.props.filename
    }
    if (this.props.filesize) {
      root.querySelector('.vdm-filesize span span').innerHTML = this.props.filesize
    }
    var link = root.querySelector('.vdm-fileurl span a')
    link.href = this.props.fileurl || '#'
    if (this.props.fileurl) {
      link.innerHTML = this.props.fileurl
    }
    root.querySelector('.vdm-close-btn').addEventListener('click', this.onClose)
    root.querySelector('.vdm-download').addEventListener('click', this.onDownload)
    if (!this.props.isFetching) {
      root.querySelector('.vdm-download').innerHTML = '<span>Скачать</span>'
    } else if (!root.querySelector('.vdm-download .loading')) {
      root.querySelector('.vdm-download').innerHTML = this.loadingImage()
    }
    if (!this.props.progress) {
      root.querySelector('.vdm-progress').style.display = 'none'
    } else {
      var progress = root.querySelector('.vdm-progress')
      progress.style.display = ''
      progress.style.width = Math.floor(this.props.progress * 100) + '%';
    }
    return root
  },
  /** @param {IMainViewProps} props */
  update: function (props) {
    this.props = Object.assign(this.props, props)
    this.render()
  },
}

module.exports = baseView

 }),
/* 11 */
 (function(module, exports) {

/**
 * @param {string} url
 * @return {string}
 */
function getExtension(url) {
  var link = document.createElement('a')
  link.href = url
  var match = link.pathname.match(/\.([^.]+)$/)
  return match ? match[1] : '';
}

module.exports = getExtension

 }),
/* 12 */
 (function(module, exports) {

module.exports = {}

 }),
/* 13 */
 (function(module, exports, __webpack_require__) {

var URLParse = __webpack_require__(5)
var makeRequest = __webpack_require__(2)
var delay = __webpack_require__(3)
var time = __webpack_require__(1)
var Logger = __webpack_require__(0)
var iframeChannel = __webpack_require__(6)
var eventEmitter = __webpack_require__(8)
var info = __webpack_require__(7)

/**
 * @typedef {{
    url: string;
    videoId: string;
    name?: string;
    filename?: string;
    size?: number;
    ext?: string;
    saveAs?: boolean;
    headers?: { [x: string]: string };
    onProgress?: (loaded: number, total: number) => void;
 * }} IDownloadDetails
 * @typedef {{
    url: string;
    name: string;
    filename: string;
    videoId: string;
    ext: string;
    event: string;
 * }} IVideoData
 */

var downloadManager = {
  __DEBUG__: false,
  logger: new Logger(),
  /**
   * @param {IDownloadDetails} details
   * @return {Promise<IVideoData>}
   */
  download: function (details) {
    var _this = this
    var logger = this.logger
    
    if (details.debug) {
      return this.DEBUG_download(details)
    }
    var promise = Promise.reject()
    if (
      (
        (typeof GM !== 'undefined' && typeof GM.download !== 'undefined')
        || typeof GM_download !== 'undefined'
      )
      && info.script_handler.toLowerCase() !== 'violentmonkey'
    ) {
      promise = this.GM_download(details)
    }
    return promise.catch(function (e) {
      if (e) {
        logger.error(time(), 'GM_download error: ', e)
      }
      var link = URLParse(details.url)
      if (location.origin === link.origin) {
        return _this.URL_download(details)
      }
      var p = Promise.reject()
      if (
        (
          (typeof GM !== 'undefined' && typeof GM.xmlHttpRequest !== 'undefined')
          || typeof GM_xmlhttpRequest !== 'undefined'
        ) && (typeof details.size === 'undefined' || details.size < (16 * 1024 * 1024))
      ) {
        p = _this.XHR_download(details)
      }
      return p.catch(function (e) {
        if (e) {
          logger.error(time(), 'XHR_download error: ', e)
        }
        return _this.IFrame_download(details)
      })
    }).then(function (response) {
      logger.info(time(), 'downloaded', details.url)
      return response
    }).catch(function (e) {
      logger.error(time(), 'failed to download', details.url, e)
    })
  },
  /**
   * @param {IDownloadDetails} details
   * @return {IVideoData}
   */
  getData: function (details) {
    var link = URLParse(details.url)
    return {
      url: link.href,
      name: details.name,
      filename: details.filename,
      videoId: details.videoId,
      ext: details.ext,
      event: 'download',
    }
  },
  /**
   * @param {IDownloadDetails} details
   * @return {Promise<IVideoData>}
   */
  GM_download: function (details) {
    var logger = this.logger
    logger.info(time(), 'GM_download', details.url)
    
    var data = this.getData(details)
    var resolve
    var reject
    var promise = new Promise(function (res, rej) {
      resolve = res
      reject = rej
    })
    GM_download({
      url: details.url,
      name: details.filename,
      saveAs: Boolean(details.saveAs),
      onerror: function (r) {
        reject(r)
      },
      onload: function () {
        resolve(data)
      },
      onprogress: function (e) {
        if (details.onProgress) {
          details.onProgress(e.loaded, e.total)
        }
      },
      ontimeout: function () {
        reject({ error: 'timeout' })
      },
    })
    return promise
  },
  /**
   * @param {IDownloadDetails} details
   * @return {Promise<IVideoData>}
   */
  XHR_download: function (details) {
    var logger = this.logger
    logger.info(time(), 'XHR_download', details.url)
    
    var data = this.getData(details)
    var _this = this
    return makeRequest({
      method: 'GET',
      url: data.url,
      headers: details.headers,
      responseType: 'blob',
      onprogress: details.onProgress,
    }, true).then(function (response) {
      if (!response.ok) {
        return Promise.reject(response)
      }
      var URL = window.URL || window.webkitURL
      var resource = URL.createObjectURL(response.data);
      return _this.URL_download(
        Object.assign({}, details, { url: resource })
      ).then(function () {
        URL.revokeObjectURL(resource)
      })
    }).then(function () {
      return data;
    })
  },
  /**
   * @param {IDownloadDetails} details
   * @return {Promise<IVideoData>}
   */
  IFrame_download: function (details) {
    var logger = this.logger
    logger.info(time(), 'IFrame_download', details.url)
    
    var data = this.getData(details)
    var onProgress = details.onProgress
    
    var progressEvent = iframeChannel.getEventName(
      Object.assign({}, data, { event: 'progress' })
    );
    eventEmitter.on(progressEvent, function (e) {
      onProgress && onProgress(e.loaded, e.total)
    })
    
    var promise = iframeChannel.request(
      Object.assign(
        {},
        data,
        {
          event: 'download',
        },
      )
    ).then(function (response) {
      eventEmitter.off(progressEvent)
      return response;
    });
    
    return promise
  },
  /**
   * @param {IDownloadDetails} details
   * @return {Promise<IVideoData>}
   */
  URL_download: function (details) {
    var logger = this.logger
    logger.info(time(), 'URL_download', details.url)
    
    var data = this.getData(details)
    var link = URLParse(data.url)
    link.download = data.filename || ('video' + details.videoId + '.mp4')
    link.innerHTML = data.filename
    document.body.appendChild(link)
    link.click()
    return delay(300).then(function () {
      document.body.removeChild(link)
      return data
    })
  },
  DEBUG_download: function (details) {
    var logger = this.logger
    logger.info(time(), 'DEBUG_download', details.url)
    
    var data = this.getData(details)
    return new Promise(function (resolve) {
      var _onProgress = details.onProgress || function (){}
      var total = 200 * 1024 * 1024
      var size = Math.floor(5000 / 300)
      var step = 1 / size
      var _progress = 0
      var interval = setInterval(function () {
        _progress += step
        _onProgress(_progress * total, total)
      }, 300)
      setTimeout(function () {
        _onProgress(total, total)
        clearInterval(interval)
        resolve(data)
      }, 5000)
    })
  },
}

module.exports = downloadManager

 }),
/* 14 */
 (function(module, exports, __webpack_require__) {

var DOMReady = __webpack_require__(4)
var Logger = __webpack_require__(0)

var iframeController = __webpack_require__(9)
var iframeChannel = __webpack_require__(6)
var sibnetView = __webpack_require__(20)
var myviView = __webpack_require__(24)
var time = __webpack_require__(1)
var info = __webpack_require__(7)

var logger = new Logger()

function app() {
	if(/^(cv|dv|fs)[a-z\d]+\.(sibnet|myvi)\.(ru|tv)/.test(location.hostname)) {
    logger.debug(time(), '(iframe)', location.href)
    return Promise.resolve().then(function () {
      return iframeController.start()
    })
  } else {
    logger.info(time(), info.script_name, info.script_version, '(c) ' + info.script_author)
    logger.debug(time(), '(top)', location.href)
    iframeChannel.init()
    return DOMReady(function () {
      if (location.origin.indexOf('sibnet') > -1) {
        sibnetView()
      } else {
        myviView()
      }
    })
  }
  return Promise.reject()
}

app().catch(function (e) {
  logger.error(time(), 'error', e)
})

module.exports = app


 }),
/* 15 */
 (function(module, exports) {

function EventEmitter() {
  this._listenerMap = {}
}
/**
 * @param {string} event
 * @param {(...args: any[]) => void} callback
 */
EventEmitter.prototype.addListener = function addListener(event, callback) {
  var listeners = this._listenerMap[event] 
  listeners = Array.isArray(listeners) ? listeners : []
  var index = listeners.indexOf(callback)
  if (index === -1) {
    listeners.push(callback)
  }
  this._listenerMap[event] = listeners
}

/**
 * @param {string} [event]
 * @param {(...args: any[]) => void} [callback]
 */
EventEmitter.prototype.removeListener = function removeListener(event, callback) {
  if (!event) {
    var events = Object.keys(this._listenerMap)
    for (var ev of events) {
      this._listenerMap[ev].length = 0
      delete this._listenerMap[ev]
    }
    return
  }
  if (!callback && Array.isArray(this._listenerMap[event])) {
    this._listenerMap[event].length = 0
    delete this._listenerMap[event]
    return
  }
  if (Array.isArray(this._listenerMap[event])) {
    var index = this._listenerMap[event].indexOf(callback)
    if (index !== -1) {
      this._listenerMap[event].splice(index, 1)
    }
    if (!this._listenerMap[event].length) {
      delete this._listenerMap[event]
    }
  }
}

/**
 * @param {string} event
 */
EventEmitter.prototype.emit = function emit(event) {
  var listeners = this._listenerMap[event]
  if (Array.isArray(listeners) && listeners.length) {
    var args = Array.prototype.slice.call(arguments, 1)
    for (var callback of listeners) {
      callback.apply(null, args)
    }
  }
}

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

/**
 * @param {string} event
 * @param {(...args: any[]) => void} callback
 */
EventEmitter.prototype.once = function once(event, callback) {
  var _this = this
  var listener = function () {
    callback.apply(null, arguments)
    _this.off(event, listener)
  }
  this.on(event, listener)
}

module.exports = EventEmitter

 }),
/* 16 */
 (function(module, exports, __webpack_require__) {

var makeRequest = __webpack_require__(2)
var delay = __webpack_require__(3)
var Logger = __webpack_require__(0)
var time = __webpack_require__(1)

var logger = new Logger()

/**
 * @param {any} resource
 * @param {string} [name]
 * @param {(loaded: number, total: number) => void} [onprogress]
 */
function downloadFile(source, name, onprogress) {
  var link = document.createElement('a')
  link.href = source
  if (link.origin === location.origin) {
    link.download = name || 'download'
    link.innerHTML = name || 'download'
    document.body.appendChild(link)
    link.click()
    logger.info(time(), 'downloadFile URL_download', link.href)
    return delay(300).then(function () {
      onprogress && onprogress(1, 1)
    })
  }
  logger.info(time(), 'downloadFile XHR_download', link.href)
  return makeRequest({
    method: 'GET',
    url: link.href,
    responseType: 'blob',
    onprogress: onprogress,
  }, true).then(function (response) {
    if (!response.ok) {
      return response
    }
    var URL = window.URL || window.webkitURL
    var resource = URL.createObjectURL(response.data);
    return downloadFile(resource, name).then(function () {
      URL.revokeObjectURL(resource)
      return response
    })
  })
}

module.exports = downloadFile

 }),
/* 17 */
 (function(module, exports) {

/**
 * @param {string} headersString
 * @return {{ [x: string]: string }}
 */
function parseAJAXHeaders(headersString) {
  if (typeof headersString !== 'string') {
    return headersString
  }
  return headersString.split(/\r?\n/g)
    .map(function (s) { return s.trim() })
    .filter(Boolean)
    .reduce(function (acc, cur) {
      var res = cur.split(':')
      var key, val
      if (res[0]) {
        key = res[0].trim().toLowerCase()
        val = res.slice(1).join('').trim()
        acc[key] = val
      }
      return acc
    }, {})
}

module.exports = parseAJAXHeaders

 }),
/* 18 */
 (function(module, exports, __webpack_require__) {

var createDocument = __webpack_require__(19)

/**
 * @param {string} responseText
 * @param {{ [x: string]: string }} headers
 * @param {string} [responseType]
 */
function parseAJAXResponse(responseText, headers, responseType) {
  var isText = !responseType || responseType.toLowerCase() === 'text'
  var contentType = headers['content-type']
  if (
    isText
    && contentType.indexOf('application/json') > -1
  ) {
    return JSON.parse(responseText)
  }
  if (
    isText &&
    (
      contentType.indexOf('text/html') > -1
      || contentType.indexOf('text/xml') > -1
    )
  ) {
    return createDocument(responseText)
  }
  return responseText
}

module.exports = parseAJAXResponse

 }),
/* 19 */
 (function(module, exports) {

/**
 * @param {string} text
 * @param {string} [title]
 * @return {Document}
 */
function createDocument(text, title) {
  title = title || ''
  var doc = document.implementation.createHTMLDocument(title);
	doc.documentElement.innerHTML = text
  return doc
}

module.exports = createDocument

 }),
/* 20 */
 (function(module, exports, __webpack_require__) {

var DOMReady = __webpack_require__(4)
var Logger = __webpack_require__(0)

var baseView = __webpack_require__(10)
var sibnetController = __webpack_require__(23)
var iframeController = __webpack_require__(9)
var time = __webpack_require__(1)
var info = __webpack_require__(7)

function sibnetView() {
  sibnetController.logger.info(time(), 'name:', sibnetController.getVideoName())
  baseView.create({
    title: info.script_name + ' ' + info.script_version,
    filename: sibnetController.getVideoName(),
    visible: true,
    icon: { width: 20, height: 20, color: '#ffffff' },
    onDownload: function (onProgress) {
      return sibnetController.downloadVideoFile(onProgress)
    },
    getId: function () {
      return sibnetController.getVideoId()
    },
    getUrl: function () {
      return sibnetController.getVideoUrl()
    },
    getSize: function () {
      return sibnetController.getVideoSize()
    },
  })
  return baseView
}

module.exports = sibnetView


 }),
/* 21 */
 (function(module, exports) {

/**
 * @param {number} size
 * @return {string}
 */
function smartSize(size) {
  var rest = size
  var mib = Math.floor(rest / (1024 * 1024))
  rest -= mib * 1024 * 1024
  var kib = Math.floor(rest / 1024)
  rest -= kib * 1024
  var bytes = rest
  var filesize;
  if (mib) {
    filesize = (size / (1024 * 1024)).toFixed(1) + ' MiB'
  } else if (kib) {
    filesize = (size / 1024).toFixed(1) + ' KiB'
  } else if (bytes) {
    filesize = bytes + ' bytes'
  } else {
    filesize = 'unknown'
  }
  return filesize
}

module.exports = smartSize

 }),
/* 22 */
 (function(module, exports, __webpack_require__) {

// extracted by mini-css-extract-plugin

 }),
/* 23 */
 (function(module, exports, __webpack_require__) {

var Logger = __webpack_require__(0)
var URLParse = __webpack_require__(5)
var makeRequest = __webpack_require__(2)
var getExtension = __webpack_require__(11)
var VIDEOS = __webpack_require__(12)
var delay = __webpack_require__(3)
var time = __webpack_require__(1)
var downloadManager = __webpack_require__(13)
var iframeChannel = __webpack_require__(6)

var sibnetVideoController = {
  logger: new Logger(),
  document: document,
  location: location,
  /**
   * @param {(size: number) => void} [onLoad]
   */
  getVideoSize: function (onLoad) {
    var videoId = this.getVideoId()
    var logger = this.logger
    
    logger.debug(time(), 'getVideoSize..', videoId)
    if (VIDEOS[videoId] && VIDEOS[videoId].size) {
      logger.debug(time(), 'getVideoSize', VIDEOS[videoId].size)
      return Promise.resolve(VIDEOS[videoId].size)
    }
    var self = this
    return this.getVideoUrl().then(function (url) {
      if (VIDEOS[videoId] && VIDEOS[videoId].size) {
        var size = VIDEOS[videoId].size
        logger.debug(time(), 'getVideoSize', size)
        onLoad && onLoad(size)
        return size;
      }
      return makeRequest({
        method: 'HEAD',
        url: url,
        headers: {
          referer: self.location.href,
          range: 'bytes=0-10',
        },
      }, true).then(function (response) {
        if (response.ok) {
          return response
        }
        return iframeChannel.request({
          event: 'size',
          videoId: videoId,
          url: url,
        })
      }).then(function (response) {
        var size = 0
        var contentRange = response.headers['content-range']
        if (contentRange) {
          size = parseInt(contentRange.split('/')[1], 10)
        }
        VIDEOS[videoId].size = size
        logger.debug(time(), 'getVideoSize', size)
        onLoad && onLoad(size)
        return size
      })
    }).catch(function (e) {
      logger.error(time(), 'getVideoSize error: ', e)
      return 0
    })
  },
  /**
   * @return {Promise<string>}
   */
  getVideoUrl: function () {
    var videoId = this.getVideoId()
    var logger = this.logger
    
    logger.debug(time(), 'getVideoUrl..', videoId)
    
    var finalUrl = VIDEOS[videoId] && VIDEOS[videoId].finalUrl
    if (finalUrl) {
      logger.debug(time(), 'getVideoUrl', finalUrl)
      return Promise.resolve(finalUrl)
    }
    var url = this.getVideoPath()
    return makeRequest({
      method: 'HEAD',
      url: url,
      headers: {
        range: 'bytes=0-',
        referer: this.location.href,
      },
    }, true).then(function (response) {
      if (response.ok) {
        return response
      }
      return iframeChannel.request({
        event: 'url',
        videoId: videoId,
        url: url,
      })
    }).then(function (response) {
      VIDEOS[videoId].finalUrl = response.finalUrl
      var contentRange = response.headers['content-range']
      var contentLength = response.headers['content-length']
      if (contentRange) {
        VIDEOS[videoId].size = parseInt(contentRange.split('/')[1], 10)
      } else if (contentLength) {
        VIDEOS[videoId].size = parseInt(contentLength, 10)
      }
      logger.debug(time(), 'getVideoUrl', response.finalUrl)
      return response.finalUrl
    }).catch(function (e) {
      logger.error(time(), 'getVideoUrl error: ', e)
      return url;
    })
  },
  /**
   * @param {(loaded: number, total: number) => void} [onProgress]
   */
  downloadVideoFile: function (onProgress) {
    var logger = this.logger
    var _this = this
    
    logger.debug(time(), 'downloadVideoFile...')
    return this.getVideoUrl().then(function (url) {
      if (!url) {
        logger.error(time(), 'downloadVideoFile', new Error('video url not found, url = ' + url))
        return
      }
      var ext = getExtension(url)
      if (!ext) {
        logger.warn(time(), 'downloadVideoFile: can not get extension from url = ', url)
        logger.warn(time(), 'downloadVideoFile: mp4 will be used as defualt extension')
      
        ext = 'mp4'
      }
      var name = _this.getVideoName()
      var filename = name + '.' + ext
    
      var videoId = _this.getVideoId()
      var data = {
        url: url,
        name: name,
        filename: filename,
        ext: ext,
        size: VIDEOS[videoId].size,
        videoId: videoId,
        event: 'download',
      }
      return downloadManager.download(
        Object.assign(
          {},
          data,
          {
            onProgress: onProgress,
            headers: {
              referer: _this.location.href,
              range: 'bytes=0-',
            },
            saveAs: false,
          },
          {
            debug: Boolean(downloadManager.__DEBUG__),
          },
        )
      );
    }).catch(function (e) {
      logger.error(time(), 'downloadVideoFile error: ', e)
    })
  },
  /** @return {string | undefined} */
  getVideoPath: function () {
    var document = this.document
    var logger = this.logger
    
    var videoId = this.getVideoId()
    VIDEOS[videoId] = VIDEOS[videoId] || {}
    if (VIDEOS[videoId].url) {
      return VIDEOS[videoId].url
    }
    var video = document.querySelector('video')
    if (video && video.src) {
      var link = URLParse(video.src)
      if (link.pathname.match(/\.mp4$/)) {
        VIDEOS[videoId].url = link.href
        return link.href
      }
    }
    // backward compatibility
    video = document.querySelector('#video')
    var html = video ? video.innerHTML : document.body.innerHTML
    html = html.split(/player\.src\(\s?\[\s?\{\s?src\s?\:\s?\"/)[1];
    var match = html ? html.match(/(\/v\/.*\.(mpd|mp4))/) : null
    if(html && match) {
      var link = URLParse(match[0])
      VIDEOS[videoId].url = link.href
      return link.href
    }
    logger.error(time(), 'url not found')
  },
  /** @return {string | undefined} */
  getVideoId: function () {
    var link = URLParse(this.location.href)
    var match = link.href.match(/video(id\s?\=\s?|)(\d+)/)
    var videoId = match ? match[2] : null
    if (videoId && !VIDEOS[videoId]) {
      VIDEOS[videoId] = { videoId: videoId }
    }
    return videoId
  },
  /** @return {string} */
  getVideoName: function () {
    var document = this.document
    var logger = this.logger
    
    var meta = document.querySelector('meta[property="og:title"]')
    var title = meta && meta.getAttribute('content')
    if (title) {
      return title
    }
    var h1 = document.querySelector('td.video_name > h1')
    var name = h1 && h1.innerHTML.replace(/\.mp4$/, '')
    if (name) {
      title = name + '_' + this.getVideoId()
      return title
    }
    title = 'sibnet_video_' + this.getVideoId()
    return title
  },
  updateVideoId: function (videoId, isForce) {
    this.logger.debug(time(), 'updateVideoId..', { videoId: videoId, isForce: isForce })
    if (videoId !== this.getVideoId()) {
      var pageUrl = 'https://video.sibnet.ru/shell.php?videoid=' + videoId
      var link = URLParse(pageUrl)
      var useGM = link.origin !== location.origin
      var _this = this
      return makeRequest({
        url: pageUrl,
        headers: {
          referer: pageUrl,
        },
      }, useGM).then(function (response) {
        if (response.ok) {
          _this.document = response.data
          _this.location = link
        }
        return response.ok
      })
    }
    return Promise.resolve()
  },
  clone: function () {
    return Object.assign({}, this)
  },
  /** @param {Logger} logger */
  setLogger: function (logger) {
    this.logger = logger
  },
  /** @param {HTMLDocument} document */
  setDocument: function(document) {
    this.document = document
  },
  /** @param {any} location */
  setLocation: function(location) {
    this.location = location;
  },
}

module.exports = sibnetVideoController

 }),
/* 24 */
 (function(module, exports, __webpack_require__) {

var DOMReady = __webpack_require__(4)
var Logger = __webpack_require__(0)

var baseView = __webpack_require__(10)
var myviController = __webpack_require__(25)
var iframeController = __webpack_require__(9)
var time = __webpack_require__(1)
var info = __webpack_require__(7)

function myviView() {
  myviController.logger.info(time(), 'name:', myviController.getVideoName())
  baseView.create({
    title: info.script_name + ' ' + info.script_version,
    filename: myviController.getVideoName(),
    visible: true,
    icon: { width: 20, height: 20, color: '#ffffff' },
    onDownload: function (onProgress) {
      return myviController.downloadVideoFile(onProgress)
    },
    getId: function () {
      return myviController.getVideoId()
    },
    getUrl: function () {
      return myviController.getVideoUrl()
    },
    getSize: function () {
      return myviController.getVideoSize()
    },
  })
  return baseView
}

module.exports = myviView


 }),
/* 25 */
 (function(module, exports, __webpack_require__) {

var Logger = __webpack_require__(0)
var URLParse = __webpack_require__(5)
var makeRequest = __webpack_require__(2)
var getExtension = __webpack_require__(11)
var VIDEOS = __webpack_require__(12)
var delay = __webpack_require__(3)
var time = __webpack_require__(1)
var downloadManager = __webpack_require__(13)
var iframeChannel = __webpack_require__(6)

var myviVideoController = {
  logger: new Logger(),
  document: document,
  location: location,
  /**
   * @param {(size: number) => void} [onLoad]
   */
  getVideoSize: function (onLoad) {
    var videoId = this.getVideoId()
    var logger = this.logger
    
    if (VIDEOS[videoId] && VIDEOS[videoId].size) {
      return Promise.resolve(VIDEOS[videoId].size)
    }
    var self = this
    logger.debug(time(), 'getVideoSize..', videoId)
    return this.getVideoUrl().then(function (url) {
      if (VIDEOS[videoId] && VIDEOS[videoId].size) {
        var size = VIDEOS[videoId].size
        onLoad && onLoad(size)
        return size;
      }
      return makeRequest({
        method: 'GET',
        url: url,
        headers: {
          referer: self.location.origin,
          range: 'bytes=0-10',
        },
      }, true).then(function (response) {
        if (response.ok) {
          return response
        }
        return iframeChannel.request({
          event: 'size',
          url: url,
          videoId: videoId,
        })
      }).then(function (response) {
        var contentRange = response.headers['content-range']
        var size = 0
        if (contentRange && response.ok) {
          size = parseInt(contentRange.split('/')[1], 10)
        }
        VIDEOS[videoId].size = size
        logger.debug(time(), 'getVideoSize', size)
        onLoad && onLoad(size)
        return size
      })
    }).catch(function (e) {
      logger.error(time(), 'getVideoSize error: ', e)
      return 0
    })
  },
  /**
   * @return {Promise<string>}
   */
  getVideoUrl: function () {
    var videoId = this.getVideoId()
    var logger = this.logger
    
    logger.debug(time(), 'getVideoUrl..', videoId)
    
    var finalUrl = VIDEOS[videoId] && VIDEOS[videoId].finalUrl
    if (finalUrl) {
      logger.debug(time(), 'getVideoUrl', finalUrl)
      return Promise.resolve(finalUrl)
    }
    var url = this.getVideoPath()
    var promise = Promise.resolve()
    var _this = this
    if (!url) {
      return _this.updateVideoId(videoId, true).then(function (ok) {
        if (ok) {
          return _this.getVideoUrl()
        }
        logger.error(time(), 'getVideoUrl failed')
      })
    }
    return makeRequest({
      method: 'GET',
      url: url,
      headers: {
        range: 'bytes=0-10',
        referer: location.origin,
      },
    }, true).then(function (response) {
      if (response.ok) {
        return response
      }
      return iframeChannel.request({
        event: 'url',
        videoId: videoId,
        url: url,
      })
    }).then(function (response) {
      VIDEOS[videoId].finalUrl = response.finalUrl
      var contentRange = response.headers['content-range']
      if (contentRange) {
        VIDEOS[videoId].size = parseInt(contentRange.split('/')[1], 10)
      }
      logger.debug(time(), 'getVideoUrl', response.finalUrl)
      return response.finalUrl
    }).catch(function (e) {
      logger.error(time(), 'getVideoUrl error: ', e)
      return url
    })
  },
  /**
   * @param {(loaded: number, total: number) => void} [onProgress]
   */
  downloadVideoFile: function (onProgress) {
    var logger = this.logger
    var _this = this
    
    logger.info(time(), 'downloadVideoFile...')
    return this.getVideoUrl().then(function (url) {
      if (!url) {
        logger.error(time(), 'downloadVideoFile', new Error('video url not found, url = ' + url))
        return
      }
      var ext = getExtension(url)
      if (!ext) {
        logger.warn(time(), 'downloadVideoFile: can not get extension from url = ', url)
        logger.warn(time(), 'downloadVideoFile: mp4 will be used as defualt extension')
      
        ext = 'mp4'
      }
      var name = _this.getVideoName()
      var filename = name + '.' + ext
    
      var videoId = _this.getVideoId()
      var data = {
        url: url,
        name: name,
        filename: filename,
        ext: ext,
        size: VIDEOS[videoId].size,
        videoId: videoId,
        event: 'download',
      }
      
      return downloadManager.download(
        Object.assign(
          {},
          data,
          {
            onProgress: onProgress,
            headers: {
              referer: _this.location.href,
              range: 'bytes=0-',
            },
            saveAs: false,
          },
          {
            debug: Boolean(downloadManager.__DEBUG__),
          },
        )
      );
    }).catch(function (e) {
      logger.error(time(), 'downloadVideoFile error: ', e)
    })
  },
  /** @return {string | undefined} */
  getVideoPath: function () {
    var document = this.document
    var logger = this.logger
    
    var videoId = this.getVideoId()
    VIDEOS[videoId] = VIDEOS[videoId] || {}
    if (VIDEOS[videoId].url) {
      return VIDEOS[videoId].url
    }
    var scripts = Array.prototype.slice.call(document.querySelectorAll('script'))
    var textarea = document.createElement('textarea')
    var html, match, urlencoded,
      regex = /PlayerLoader\.CreatePlayer\s?\(\s?(?:\"|\')([^"']+)/i
    for (var script of scripts) {
      html = script.innerHTML
      if (!html) {
        continue
      }
      textarea.innerHTML = html
      match = textarea.value.match(regex)
      if (match) {
        urlencoded = match[1]
        break
      }
    }
    if (urlencoded) {
      var obj = urlencoded.split(/\u0026/g).reduce(function (acc, cur){
        var arr = cur.split('=')
        var key = arr[0], val = arr[1]
        if (key) {
          acc[key] = val
        }
        return acc
      }, {})
      var v = decodeURIComponent(obj.v)
      v = v.split(/\\u0026/g)[0]
      var link = URLParse(v)
      VIDEOS[videoId].url = link.href
      return link.href
    }
    
    var video = document.querySelector('video')
    if (video && video.src) {
      var link = URLParse(video.src)
      if (link.pathname.match(/\.mp4$/)) {
        VIDEOS[videoId].url = link.href
        return link.href
      }
    }
    logger.error(time(), 'url not found')
  },
  /** @return {string | undefined} */
  getVideoId: function () {
    var link = URLParse(this.location.href)
    var match = link.pathname.match(/(?:embed\/(.+))/) || link.search.match(/v\=([^?&#=]+)/)
    var videoId = match ? match[1] : null;
    if (videoId && !VIDEOS[videoId]) {
      VIDEOS[videoId] = { videoId: videoId }
    }
    return videoId
  },
  /** @return {string} */
  getVideoName: function () {
    var document = this.document
    var logger = this.logger
    
    var title = document.querySelector('title')
    if (title) {
      title = title.innerHTML
      return title
    }
    title = 'myvi_video_' + this.getVideoId()
    return title
  },
  clone: function () {
    return Object.assign({}, this)
  },
  /** @param {Logger} logger */
  setLogger: function (logger) {
    this.logger = logger
  },
  /** @param {HTMLDocument} document */
  setDocument: function(document) {
    this.document = document
  },
  /** @param {any} location */
  setLocation: function(location) {
    this.location = location;
  },
  updateVideoId: function (videoId, isForce) {
    this.logger.debug(time(), 'updateVideoId..', { videoId: videoId, isForce: isForce })
    if (videoId !== this.getVideoId() || isForce) {
      var pageUrl = 'https://www.myvi.tv/embed/' + videoId
      var link = URLParse(pageUrl)
      var useGM = link.origin !== location.origin
      var _this = this
      return makeRequest({
        url: pageUrl,
        headers: {
          referer: pageUrl,
        },
      }, useGM).then(function (response) {
        if (response.ok) {
          _this.document = response.data
          _this.location = link
        }
        return response.ok
      })
    }
    return Promise.resolve()
  },
}

module.exports = myviVideoController

 })
 ])));
})(typeof unsafeWindow !== 'undefined' ? unsafeWindow : window, window);

(function () {
  const style = `
.vdm-root {
  margin: 0;
  padding: 0;
  background-color: #1e1a1a;
  z-index: 9999;
  bottom: 10px;
  right: 10px;
  border-radius: 5px;
  overflow: hidden;
  min-width: 400px;
  position: fixed;
  color: white;
  font-family: sans-serif;
  font-size: 14px;
  max-width: 600px;
}
.vdm-root span {
  word-break: break-all;
}
.vdm-container {
  display: flex;
  margin: 0;
  padding: 0;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  margin-left: 24px;
  margin-right: 24px;
}

.vdm-header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin: 10px;
}
.vdm-title {
  flex: 1;
  text-align: center;
}
.vdm-close-btn {
  z-index: 12;
  cursor: pointer;
}
.vdm-close-btn div {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
.vdm-close-btn,
.vdm-close-btn div,
.vdm-close-btn div:after,
.vdm-close-btn div:before {
  height: 16px;
}
.vdm-close-btn,
.vdm-close-btn div {
  width: 16px;
}
.vdm-close-btn div:after,
.vdm-close-btn div:before {
  content: "";
  position: absolute;
  background: #fff;
  width: 1.5px;
  display: block;
  transform: rotate(45deg);
}
.vdm-close-btn div:before {
  transform: rotate(-45deg);
}
.vdm-controllers {
  width: 100%;
  margin-top: 10px;
}
.vdm-button {
	cursor: pointer;
	background-color: green;
	border-radius: 5px;
  margin: 10px 0;
	text-align: center;
  font-weight: bold;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 20px;
}
.vdm-button:active {
  background-color: blue;
}
.vdm-button:hover {
  opacity: 0.95;
}
.vdm-content {
  display: flex;
  flex-direction: column;
  width: 100%;
  justify-content: space-between;
  align-items: center;
}
.vdm-filesize {
  margin-top: 5px;
  margin-bottom: 5px;
}
.vdm-item {
  align-self: flex-start;
}
.vdm-item a {
  color: #666;
}
.vdm-item a:hover {
  opacity: 0.8;
}
.vdm-item span label {
  cursor: pointer;
}
.vdm-item label:hover {
  opacity: 0.8;
}
.vdm-item label.show_link {
  color: #666;
}
.vdm-item input[type=checkbox] + span label.show_link,
.vdm-item input[type=checkbox]:checked + span label.video_link,
.vdm-item input[type=checkbox]:checked + span a {
  display: initial;
}

.vdm-item input[type=checkbox]:checked + span label.show_link,
.vdm-item input[type=checkbox] + span label.video_link,
.vdm-item input[type=checkbox] + span a {
  display: none;
}
.vdm-progress {
  height: 10px;
  background-color: #3838ea;
  border-radius: 2px;
}
.vdm-download .loading {
  -webkit-animation: spin 2s linear infinite;
  -moz-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
}
.vdm-button > * {
  padding: 10px;
  line-height: 20px;
}
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
`;
  if (typeof GM !== 'undefined' && typeof GM.addStyle !== 'undefined') {
    GM.addStyle(style)
  } else {
    var _addStyle = function (textCss) {
      var el = document.createElement('style')
      el.setAttribute('type', 'text/css')
      el.innerHTML = textCss
      return document.head.appendChild(el)
    }
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', function () {
        _addStyle(style)
      })
    } else {
      _addStyle(style)
    }
  }
})();