Iwara 향상

Iwara 향상 — 사용자 정의 파일명 다운로드, 좋아요 비율 표시, 와이드 스크린, 더블클릭 전체화면, 비디오 플레이어 수정 등. Tampermonkey 및 Violentmonkey 지원.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Iwara Enhancement
// @name:en      Iwara Enhancement
// @name:zh-CN   Iwara增强
// @name:ja      Iwara 強化
// @name:ko      Iwara 향상
// @description  Iwara Enhancement — one-click download with customizable filenames, like ratio display, wide-screen layout, double-click fullscreen, and video player fixes. Supports Tampermonkey and Violentmonkey.
// @description:en Iwara Enhancement — one-click download with customizable filenames, like ratio display, wide-screen layout, double-click fullscreen, and video player fixes. Supports Tampermonkey and Violentmonkey.
// @description:zh-CN Iwara增强 — 一键下载与自定义文件名、显示喜爱率、宽屏布局、双击全屏、修复视频播放器等。支持 Tampermonkey 和 Violentmonkey。
// @description:ja Iwara 強化 — カスタムファイル名でのダウンロード、高評価率表示、ワイド画面、ダブルクリック全画面、動画プレイヤー修正など。Tampermonkey・Violentmonkey 対応。
// @description:ko Iwara 향상 — 사용자 정의 파일명 다운로드, 좋아요 비율 표시, 와이드 스크린, 더블클릭 전체화면, 비디오 플레이어 수정 등. Tampermonkey 및 Violentmonkey 지원.
// @noframes
// @grant        unsafeWindow
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_download
// @grant        GM_info
// @require      https://unpkg.com/jquery
// @require      https://unpkg.com/[email protected]
// @require      https://unpkg.com/vue-i18n
// @match        *://*.iwara.tv/*
// @connect      *.iwara.tv
// @namespace    https://github.com/a1156883061/userscripts-iwara-enhancement
// @version      1.5
// @author       a1156883061
// @source       https://github.com/a1156883061/userscripts-iwara-enhancement
// @supportURL   https://github.com/a1156883061/userscripts-iwara-enhancement/issues
// @license MIT
// ==/UserScript==

;(() => {
  "use strict"

  $

  const external_Vue_namespaceObject = Vue

  function onClickOutside(el, callback) {
    document.addEventListener("click", handler)

    function handler(e) {
      if (!el.contains(e.target)) {
        document.removeEventListener("click", handler)
        callback(e)
      }
    }
  }

  /**
   * MutationObserver that calls callback with just a single mutation.
   */
  class SimpleMutationObserver extends MutationObserver {
    // since calling `new NodeList()` is illegal, this is the only way to create an empty NodeList
    static emptyNodeList = document.querySelectorAll("#__absolutely_nonexisting")

    constructor(callback) {
      super((mutations) => {
        for (const mutation of mutations) if (this.callback(mutation)) break
      })
      this.callback = callback
    }

    /**
     * @param options.immediate - When observing "childList", immediately trigger a mutation with existing nodes.
     */
    observe(target, options) {
      super.observe(target, options)

      options &&
        options.immediate &&
        options.childList &&
        target.childNodes.length &&
        this.callback({
          target,
          type: "childList",
          addedNodes: target.childNodes,
          removedNodes: SimpleMutationObserver.emptyNodeList,
        })
    }
  }

  function hasClass(node, className) {
    return !!node.classList?.contains(className)
  }

  let log

  setLogger(console.log)

  function setLogger(logger) {
    log = logger.bind(console, `[${GM_info.script.name}]`)
  }

  function clamp(val, min, max) {
    return val < min ? min : val > max ? max : val
  }

  function parseAbbreviatedNumber(str) {
    const units = { k: 1e3, m: 1e6, b: 1e9 }

    let number = parseFloat(str)

    if (!isNaN(number)) {
      const unit = str.trim().slice(-1).toLowerCase()

      return number * (units[unit] || 1)
    }

    return NaN
  }

  function formatDate(date) {
    let delimiter = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "/"
    return [
      date.getFullYear(),
      date.getMonth() + 1,
      date.getDate(),
      date.getHours(),
      date.getMinutes(),
      date.getSeconds(),
    ]
      .map((num) => String(num).padStart(2, "0"))
      .join(delimiter)
  }

  /** 可读的日期时间格式,文件名安全(避免 : / \ 等非法字符) */
  function formatDateReadable(date) {
    let includeTime = !(arguments.length > 1 && arguments[1] !== void 0) || arguments[1]
    const d = [date.getFullYear(), date.getMonth() + 1, date.getDate()]
      .map((num) => String(num).padStart(2, "0"))
      .join("-")

    if (!includeTime) return d

    const t = [date.getHours(), date.getMinutes(), date.getSeconds()]
      .map((num) => String(num).padStart(2, "0"))
      .join("")

    return `${d}T${t}`
  }

  function formatError(e) {
    let fallback = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "Unknown error"
    if (typeof e === "string") return e || fallback

    if (e !== null && typeof e === "object") {
      if (e instanceof Event && e.type === "error") return "Failed to load resource"

      if (e.message) return e.message

      const str = String(e)
      return str === "[object Object]" ? fallback : str
    }

    return fallback
  }

  function adjustHexColor(color, amount) {
    return color.replace(/\w\w/g, (color) =>
      clamp(parseInt(color, 16) + amount, 0, 255)
        .toString(16)
        .padStart(2, "0")
    )
  }

  // written by ChatGPT
  function adjustAlpha(color, alpha) {
    if (alpha < 0 || alpha > 1) throw new Error("Alpha value must be between 0 and 1")

    let r, g, b

    if (color.startsWith("#")) {
      if (color.length !== 7) throw new Error("Invalid color format")

      r = parseInt(color.slice(1, 3), 16)
      g = parseInt(color.slice(3, 5), 16)
      b = parseInt(color.slice(5, 7), 16)
    } else {
      if (!color.startsWith("rgb")) throw new Error("Invalid color format")
      {
        const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/)

        if (!match) throw new Error("Invalid color format")

        r = parseInt(match[1], 10)
        g = parseInt(match[2], 10)
        b = parseInt(match[3], 10)
      }
    }

    return `rgba(${r}, ${g}, ${b}, ${alpha})`
  }

  /**
   * Replaces characters that are forbidden in file systems.
   */
  function sanitizePath(path, illegalCharReplacement) {
    let keepDelimiters = !(arguments.length > 2 && arguments[2] !== void 0) || arguments[2]
    path = path.replace(/[*:<>?|]/g, illegalCharReplacement)

    keepDelimiters || (path = path.replace(/[\\/]/g, illegalCharReplacement))

    return path
  }

  const external_VueI18n_namespaceObject = VueI18n

  const en_namespaceObject = JSON.parse(
    '{"language":"English","name":"Iwara Enhancement","description":"Multiple UI enhancements for better experience.","ui":{},"s":{"enabled":"Enabled","extra":"Extra settings","download":{"label":"Download","auto":{"label":"One-click Download","desc":"Automatically starts download when clicking the download button."},"resolution":{"label":"Preferred Resolution for Download"},"filename":{"label":"Filename","desc":"Filename template to use when downloading a video.<br>Each keyword should be surrounded by <b>{\'{ }\'}</b>.","preview":"Preview","key":{"id":"video\'s ID","title":"video\'s title","res":"video\'s resolution","author":"author\'s name","date":"date time when the download starts","up_date":"date time when the video was uploaded","date_ts":"DATE in timestamp format","up_date_ts":"UP_DATE in timestamp format","date_fmt":"current date & time (e.g. 2026-06-10T143000)","up_date_fmt":"upload date & time (e.g. 2026-06-10T143000)"},"replace_illegal_char":"Replace characters that are disallowed in filename with:","tips":["Tips","You can use \\"/\\" in the filename to create subfolders, for example {AUTHOR}/{DATE}-{TITLE}.","If the filename doesn\'t work, check if you have any browser extensions that may interfere with the download, such as the Aria2 extension."]}},"ui":{"label":"UI","like_rate":{"label":"Like rate","desc":"Displays like rates in video/image lists."},"highlight_threshold":{"label":"Highlight threshold","desc":"Highlights video/image items over certain like rate."},"highlight_bg":{"label":"Highlight opacity"},"widen_content":{"label":"Widen content","desc":"Widen the content area in video/image pages.","scale":"Additional scale (%)"},"widen_list":{"label":"Widen list","desc":"Widen image/video lists.","scale":"Scale (%)"}},"script":{"label":"Script","language":{"label":"Language"}}}}'
  )

  const zh_namespaceObject = JSON.parse(
    '{"language":"中文","name":"Iwara增强","description":"多种增强体验的界面优化","s":{"enabled":"启用","extra":"更多选项","download":{"label":"下载","auto":{"label":"一键下载","desc":"点击下载按钮时自动开始下载"},"resolution":{"label":"优先下载的分辨率"},"filename":{"label":"文件名","desc":"下载视频时使用的文件名模板<br>每个关键词必须使用 <b>{\'{ }\'}</b> 来包围","preview":"预览","key":{"id":"视频 ID","title":"视频标题","author":"作者名","res":"视频分辨率","date":"下载开始时的日期和时间","up_date":"视频发布时的日期和时间","date_ts":"DATE 的时间戳格式","up_date_ts":"UP_DATE 的时间戳格式","date_fmt":"当前日期和时间(如 2026-06-10T143000)","up_date_fmt":"视频发布日期和时间(如 2026-06-10T143000)"},"replace_illegal_char":"将文件名中的非法字符替换为:","tips":["提示","可以在文件名里使用\\"/\\"来创建文件夹,例如{AUTHOR}/{DATE}-{TITLE}","如果文件名不起作用,检查一下是否安装了与下载相关的浏览器插件,比如 Aria2 插件"]}},"ui":{"label":"界面","like_rate":{"label":"喜爱率","desc":"在视频和图片列表里显示喜爱率"},"highlight_threshold":{"label":"高亮分界点","desc":"喜爱率高于此值的视频和图片将会被高亮显示"},"highlight_bg":{"label":"高亮透明度"},"widen_content":{"label":"加宽内容区域","desc":"加宽视频页和图片页的内容区域","scale":"额外缩放(%)"},"widen_list":{"label":"加宽列表","desc":"加宽视频列表和图片列表","scale":"缩放(%)"}},"script":{"label":"脚本","language":{"label":"语言"}}}}'
  )

  /* harmony default export */ const i18n = { zh: zh_namespaceObject, en: en_namespaceObject }

  function createStorage(prefix, schema) {
    prefix && (prefix += ".")

    return {
      get(key) {
        return GM_getValue(prefix + key, schema[key])
      },
      set(key, val) {
        typeof val === "function" && (val = val(this.get(key)))

        GM_setValue(prefix + key, val)
      },
    }
  }

  const storage = createStorage("", {
    v: GM_info.script.version,
    locale: navigator.language,
    volume: 0.5,
    auto_down_enabled: true,
    preferred_res: "Source",
    filename: "{DATE} {TITLE} - {AUTHOR} ({ID})",
    illegal_char_replacement: "_",
    dark: false,
    like_rates: true,
    like_rate_highlight: 4,
    like_rate_highlight_opacity: 0.2,
    widen_list: true,
    widen_list_scale:
      window.innerWidth <
      // container-fluid's default max-width is 1200px, leave 100px for tolerance
      1200 + 100
        ? 100
        : ~~(100 * ((window.innerWidth - /* sidebar width */ 250 - /* spacing */ 100) / 1200)),
    widen_content: true,
    widen_content_scale: 100,
  })

  const i18n_i18n = (0, external_VueI18n_namespaceObject.createI18n)({
    locale: storage.get("locale"),
    fallbackLocale: "en",
    messages: i18n,

    // disable warnings - I know what I'm doing!!
    silentFallbackWarn: true,
    silentTranslationWarn: true,
    warnHtmlInMessage: "off",
  })

  function matchLocale(locale) {
    return i18n_i18n.global.availableLocales.includes(locale)
      ? locale
      : i18n_i18n.global.availableLocales.find((loc) => locale.startsWith(loc)) || "en"
  }

  const locale = (0, external_Vue_namespaceObject.ref)(storage.get("locale"))

  ;(0, external_Vue_namespaceObject.watchEffect)(() => {
    i18n_i18n.global.locale = locale.value

    storage.set("locale", locale.value)
  })

  function useConfigSettings() {
    // locale that will actually be used, with fallback applied
    const activeLocale = (0, external_Vue_namespaceObject.computed)(() => matchLocale(locale.value))

    return { locale, activeLocale }
  }

  /* harmony default export */ function mitt(n) {
    return {
      all: (n = n || new Map()),
      on: function (t, e) {
        var i = n.get(t)
        i ? i.push(e) : n.set(t, [e])
      },
      off: function (t, e) {
        var i = n.get(t)
        i && (e ? i.splice(i.indexOf(e) >>> 0, 1) : n.set(t, []))
      },
      emit: function (t, e) {
        var i = n.get(t)
        i &&
          i.slice().map(function (n) {
            n(e)
          }),
          (i = n.get("*")) &&
            i.slice().map(function (n) {
              n(t, e)
            })
      },
    }
  }

  const ready = new Promise((resolve) => {
    document.readyState === "loading"
      ? document.addEventListener("DOMContentLoaded", () => resolve())
      : resolve()
  })

  function once(emitter, event, listener) {
    const fn = (data) => {
      emitter.off(event, fn)
      listener(data)
    }

    emitter.on(event, fn)

    return fn
  }

  function setupPaging() {
    ready.then(() => {
      const appDiv = document.getElementById("app")

      if (!appDiv) {
        log("Missing app div.")
        return
      }

      log("Start observing pages.")

      const appObserver = new SimpleMutationObserver((mutation) => {
        detectPageChange(appDiv, mutation.addedNodes, "pageEnter")
        detectPageChange(appDiv, mutation.removedNodes, "pageLeave")
      })
      appObserver.observe(appDiv, { childList: true, immediate: true })
    })
  }

  const emitter = mitt()

  let currentClassName = ""

  emitter.on("pageEnter", (className) => (currentClassName = className))

  const ALL = "*"

  // page listener for iwara
  function page(id, enter) {
    const match = (() => {
      if (id === ALL) return () => id

      const ids = typeof id === "string" ? [id] : id
      const classes = ids.map((id) => `page-${id}`)

      return (className) => {
        const split = className.split(" ")
        const index = classes.findIndex((cls) => split.includes(cls))

        return ids[index]
      }
    })()

    function callIfMatch(listener) {
      return (className) => {
        const matchedID = match(className)

        if (matchedID !== void 0)
          try {
            listener(matchedID)
          } catch (e) {
            log("Error executing page listener", e)
          }
      }
    }

    const onPageEnter = callIfMatch((matchedID) => {
      enter(matchedID, (onLeave) => {
        once(emitter, "pageLeave", callIfMatch(onLeave))
      })
    })

    emitter.on("pageEnter", onPageEnter)
  }

  function detectPageChange(appDiv, nodes, event) {
    if (nodes.length)
      // a valid class name will be like "page page-videoList", where "videoList" is the ID
      for (const node of nodes)
        if (hasClass(node, "page")) {
          // sometimes there are two (maybe more) "page" elements, and one of them contains only the "page" class,
          // we ignore it in this case
          const hasOtherPageElements =
            $(appDiv)
              .children(".page")
              .filter((_, e) => e !== node && !node.className.includes("page-")).length > 0

          hasOtherPageElements || emitter.emit(event, node.className)
          break
        }
  }

  function cancelOnLeave(onLeave, promise) {
    onLeave(() => promise.cancel())
    return promise
  }

  // cached keys, since there will most likely be only one React instance in the page
  let reactInstanceKey = ""
  let reactEventHandlersKey = ""

  function getReactInstance(element) {
    const reactElement = element

    if (reactInstanceKey) return reactElement[reactInstanceKey]

    for (const key of Object.keys(element))
      if (key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")) {
        reactInstanceKey = key
        return reactElement[key]
      }
  }

  function getReactEventHandlers(element) {
    const reactElement = element

    if (reactEventHandlersKey) return reactElement[reactEventHandlersKey]

    // 先尝试 React 16/17 的 __reactEventHandlers$
    for (const key of Object.keys(element))
      if (key.startsWith("__reactEventHandlers$")) {
        reactEventHandlersKey = key
        return reactElement[key]
      }
    // 再尝试 React 18 的 __reactProps$
    for (const key of Object.keys(element))
      if (key.startsWith("__reactProps$")) {
        reactEventHandlersKey = key
        return reactElement[key]
      }
    return
  }

  // sometimes I just don't want the script to depend on Lodash...
  function throttle(fn, timeout) {
    let timer = 0

    return function () {
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++)
        args[_key] = arguments[_key]
      if (timer) return

      timer = setTimeout(() => {
        fn.apply(null, args)

        timer = 0
      }, timeout)
    }
  }

  /**
   * Periodically calls given function until the return value is truthy.
   * @returns A CancelablePromise that resolves with the function's return value when truthy.
   */
  function until(fn) {
    let interval = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0
    !(arguments.length > 2 && arguments[2] !== void 0) || arguments[2]
    let cancelled = false

    const STOP = Symbol()

    const promise = new Promise((resolve, reject) => {
      const run = () => {
        if (cancelled) return STOP

        const result = fn()

        if (result) {
          resolve(result)
          return STOP
        }
      }

      const timerId = setInterval(() => {
        try {
          run() === STOP && clearInterval(timerId)
        } catch (e) {
          reject(e)
          clearInterval(timerId)
        }
      }, interval)
    })
    promise.cancel = () => (cancelled = true)

    return promise
  }

  /**
   * Periodically calls given function until the returned jQuery object is not empty.
   * @returns A CancelablePromise that resolves with the jQuery object.
   */
  function until$(fn) {
    let interval = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0
    let cancelOnReload = !(arguments.length > 2 && arguments[2] !== void 0) || arguments[2]
    return until(
      () => {
        const result = fn()

        if (result.length) return result
      },
      interval,
      cancelOnReload
    )
  }

  // a partial structure of the video data defined in iwara's video page,
  // including only the properties we need

  const FILENAME_KEYWORDS = [
    "ID",
    "TITLE",
    "RES",
    "AUTHOR",
    "DATE",
    "UP_DATE",
    "DATE_TS",
    "UP_DATE_TS",
    "DATE_FMT",
    "UP_DATE_FMT",
  ]
  const RESOLUTIONS = ["Source", "540p", "360p"]

  const autoEnabled = (0, external_Vue_namespaceObject.ref)(storage.get("auto_down_enabled"))
  const filenameTemplate = (0, external_Vue_namespaceObject.ref)(storage.get("filename"))
  const illegalCharReplacement = (0, external_Vue_namespaceObject.ref)(
    storage.get("illegal_char_replacement")
  )
  const resolution = (0, external_Vue_namespaceObject.ref)(storage.get("preferred_res"))

  const videoInfo = (0, external_Vue_namespaceObject.reactive)({
    id: "",
    title: "",
    author: "",
    created: 0,
    size: 0,
    error: "",
  })
  const sources = (0, external_Vue_namespaceObject.reactive)([])
  const source = (0, external_Vue_namespaceObject.computed)(
    () =>
      sources.find((_ref) => {
        let { label } = _ref
        return label === resolution.value
      }) || sources[0]
  )

  // indicates whether the sources belong to current page
  const hasFreshSources = (0, external_Vue_namespaceObject.ref)(false)

  const filename = (0, external_Vue_namespaceObject.computed)(() => {
    try {
      if (!source.value) throw "Please open a video"

      return resolveFilename(filenameTemplate.value, source.value)
    } catch (e) {
      return `Unable to resolve filename (${formatError(e)})`
    }
  })

  ;(0, external_Vue_namespaceObject.watchEffect)(() =>
    storage.set("preferred_res", resolution.value)
  )
  ;(0, external_Vue_namespaceObject.watchEffect)(() =>
    storage.set("filename", filenameTemplate.value)
  )
  ;(0, external_Vue_namespaceObject.watchEffect)(() =>
    storage.set("auto_down_enabled", autoEnabled.value)
  )
  ;(0, external_Vue_namespaceObject.watchEffect)(() =>
    convertDownloadDropdown(void 0, autoEnabled.value)
  )

  function useDownloaderSettings() {
    return {
      FILENAME_KEYWORDS,
      RESOLUTIONS,
      autoDownEnabled: autoEnabled,
      resolution,
      filenameTemplate,
      filenamePreview: filename,
      illegalCharReplacement,
    }
  }

  page("video", async (pageID, onLeave) => {
    const videoActions = $(".page-video__actions").get(0)

    if (!videoActions) {
      log("Could not find video actions.")
      return
    }

    onLeave(() => {
      // prevent unexpected downloads
      hasFreshSources.value = false
    })

    const $downloadButton = await cancelOnLeave(
      onLeave,
      until$(() => $(".page-video__actions .downloadButton"))
    )

    updateVideoInfo(videoActions)
    updateSources($downloadButton.closest(".dropdown").get(0))

    autoEnabled.value && convertDownloadDropdown($downloadButton.get(0), true)
  })

  function updateVideoInfo(videoActions) {
    try {
      // FIXME: reading the prop by a path is quite unreliable, any improvement?
      const video =
        findVideoInReactProps(getReactEventHandlers(videoActions)) || findVideoInFiber(videoActions)

      if (!video)
        throw new Error("Cannot extract video info from page (React internal API changed)")

      videoInfo.id = video.id
      videoInfo.title = video.title
      videoInfo.created = new Date(video.createdAt).getTime()
      videoInfo.author = video.user.name
      videoInfo.size = video.file.size
    } catch (e) {
      log(e)
      videoInfo.error = e + ""
    }
  }

  function findVideoInReactProps(props) {
    if (!props) return

    if (props.video) return props.video

    const children = Array.isArray(props.children) ? props.children : [props.children]

    for (const child of children) {
      const video = findVideoInReactProps(child?.props)

      if (video) return video
    }

    return
  }

  function findVideoInFiber(element) {
    let fiber = getReactInstance(element)

    while (fiber) {
      const video = findVideoInReactProps(fiber.memoizedProps)

      if (video) return video

      fiber = fiber.return
    }

    return
  }

  function updateSources(downloadDropdown) {
    const newSources = $(downloadDropdown)
      .find(".dropdown__content a")
      .map(function () {
        const url = this.href
        const label = this.innerText

        return { url, label }
      })
      .get()

    if (!newSources.length) return

    sources.splice(0, sources.length, ...newSources)

    hasFreshSources.value = true
  }

  function convertDownloadDropdown(downloadButton, enabled) {
    const $button = downloadButton ? $(downloadButton) : $(".downloadButton")
    const $dropdown = $button.closest(".dropdown")

    if (!$dropdown.length) return

    const rawButtonText = $button.text().replace(/\s*\(.*\)/, "")

    if (enabled) {
      $dropdown.data("converted") ||
        $dropdown
          .data("converted", true)
          .on("click", function () {
            download(this)
          })
          .children(".dropdown__content")
          .css("display", "none")

      const resolution = source.value?.label

      $button.text(rawButtonText + (resolution ? ` (${resolution})` : ""))
    } else {
      $dropdown
        .data("converted", false)
        .off("click")
        .children(".dropdown__content")
        .css("display", "")
      $button.text(rawButtonText)
    }
  }

  function download(downloadDropdown) {
    try {
      if (!hasFreshSources.value) throw new Error("No sources found in current page.")
      if (!source.value) throw new Error("Missing source.")

      const $downloadButton = $(downloadDropdown).find(".downloadButton")

      const filename = resolveFilename(filenameTemplate.value, source.value)

      log("Downloading:", filename, source.value.url, GM_info.downloadMode)

      if (GM_info.downloadMode === "browser") {
        setDownloadButtonEnabled(false)

        const downloadUrl = resolveDownloadUrl(source.value.url, filename)

        GM_download({
          url: downloadUrl,
          name: filename,
          onload: () => downloadEnded("onload"),
          onerror: (e) => {
            downloadEnded("onerror", e)
            openNativeDownload(downloadUrl, filename)
          },
          ontimeout: () => downloadEnded("ontimeout"),
        })
      } else openNativeDownload(resolveDownloadUrl(source.value.url, filename), filename)

      function setDownloadButtonEnabled(enabled) {
        if (enabled) {
          // TODO: properly disable the button
          $(downloadDropdown).css("pointer-events", "")
          $downloadButton.css("background-color", "")
        } else {
          $(downloadDropdown).css("pointer-events", "none")
          $downloadButton.css("background-color", "var(--primary-dark)")
        }
      }

      function downloadEnded(type, e) {
        setDownloadButtonEnabled(true)

        type === "ontimeout" && (e = { error: "timed_out" })

        if (e && e.error) {
          log(e)
          printDownloadMessage(
            `Download Error (${e.error}): ${(e.details && e.details.current) || "No info"}`
          )
        }
      }
    } catch (e) {
      log(e)
      printDownloadMessage(e + "")
    }
  }

  function resolveFilename(template, source) {
    if (videoInfo.error) throw new Error("Broken video info: " + videoInfo.error)

    const replacements = {
      ID: videoInfo.id,
      TITLE: videoInfo.title,
      RES: source.label,
      AUTHOR: videoInfo.author,
      DATE: formatDate(new Date(), ""),
      DATE_TS: new Date().getTime() + "",
      UP_DATE: formatDate(new Date(videoInfo.created), ""),
      UP_DATE_TS: videoInfo.created + "",
      DATE_FMT: formatDateReadable(new Date()),
      UP_DATE_FMT: formatDateReadable(new Date(videoInfo.created)),
    }

    const wrappedKeywords = FILENAME_KEYWORDS.map((k) => `{${k}}`)
    const regex = new RegExp(`(${wrappedKeywords.join("|")})`, "g")

    const basename = template.replace(regex, (match) => {
      const keyword = match.slice(1, -1)
      const value = replacements[keyword]

      // remove path delimiters in keyword values
      return value.replace(/[/\\]/g, illegalCharReplacement.value)
    })

    const ext = getFilenameExtension(source.url)

    const filename = basename + ext

    return sanitizePath(filename, illegalCharReplacement.value)
  }

  function getFilenameExtension(sourceUrl) {
    try {
      const url = new URL(sourceUrl)
      const queryFilename = url.searchParams.get("filename")
      const path = queryFilename || url.pathname
      const match = path.match(/\.([A-Za-z0-9]+)$/)

      return match ? `.${match[1]}` : ""
    } catch {
      const path = sourceUrl.split(/[?#]/)[0] || sourceUrl
      const match = path.match(/\.([A-Za-z0-9]+)$/)

      return match ? `.${match[1]}` : ""
    }
  }

  function openNativeDownload(url, filename) {
    const a = document.createElement("a")
    a.href = url
    a.download = filename
    a.click()
  }

  function resolveDownloadUrl(sourceUrl, filename) {
    try {
      const url = new URL(sourceUrl)

      url.hostname.endsWith("iwara.tv") &&
        url.searchParams.has("download") &&
        url.searchParams.set("download", getDownloadFilenameLeaf(filename))

      return url.href
    } catch {
      return sourceUrl
    }
  }

  function getDownloadFilenameLeaf(filename) {
    return filename.split(/[\\/]/).pop() || filename
  }

  function printDownloadMessage(msg) {
    $(".page-video__bottom")
      .css("flex-wrap", "wrap")
      .append(`<div style="flex: 100% 0 0">${msg}</div>`)
  }

  const likeRateEnabled = (0, external_Vue_namespaceObject.ref)(storage.get("like_rates"))
  const highlightThreshold = (0, external_Vue_namespaceObject.ref)(
    storage.get("like_rate_highlight")
  )
  const highlightOpacity = (0, external_Vue_namespaceObject.ref)(
    storage.get("like_rate_highlight_opacity")
  )

  const likeRateClass = "enh-like-rate"
  const highlightClass = "enh-highlight"

  ready.then(updateHighlightOpacity)

  ;(0, external_Vue_namespaceObject.watchEffect)(() => {
    storage.set("like_rates", likeRateEnabled.value)

    if (likeRateEnabled.value) {
      document.body.classList.add("enh-show-like-rates")
      $(".videoTeaser, .imageTeaser").each((i, teaser) => processTeaser(teaser))
    } else {
      document.body.classList.remove("enh-show-like-rates")
      $("." + highlightClass).removeClass(highlightClass)
    }
  })

  ;(0, external_Vue_namespaceObject.watchEffect)(() => {
    storage.set("like_rate_highlight", highlightThreshold.value)

    $(".videoTeaser, .imageTeaser").each((i, teaser) => processTeaser(teaser))
  })

  ;(0, external_Vue_namespaceObject.watchEffect)(() => {
    storage.set("like_rate_highlight_opacity", highlightOpacity.value)

    updateHighlightOpacity()
  })

  function useTeaserSettings() {
    return { likeRateEnabled, highlightThreshold, highlightOpacity }
  }

  page(
    ["home", "videoList", "imageList", "subscriptions", "profile", "video", "image"],
    async (pageID, onLeave) => {
      const teaserObserver = new SimpleMutationObserver((mutation) =>
        mutation.addedNodes.forEach(detectColumn)
      )

      onLeave(() => {
        teaserObserver.disconnect()
      })

      const teaserBatcher = new TeaserBatcher()

      if (["home", "profile", "image"].includes(pageID))
        [".videoTeaser", ".imageTeaser"].forEach(async (selector) => {
          const $teasers = await until$(() => $(selector), 200)

          requestProcessTeasers($teasers.toArray())
        })
      else if (pageID === "video") {
        const selectors = [".moreFromUser", ".moreLikeThis"].flatMap((parentCls) =>
          [".videoTeaser", ".imageTeaser"].map((cls) => `${parentCls} ${cls}`)
        )

        selectors.forEach(async (selector) => {
          const $teasers = await until$(() => $(selector), 200)

          requestProcessTeasers($teasers.toArray())
        })
      } else {
        const $teasers = await until$(
          () => $(".videoTeaser:first-of-type, .imageTeaser:first-of-type"),
          200
        )

        detectRow($teasers.closest(".row")[0])
      }

      function detectRow(row) {
        teaserObserver.observe(row, { childList: true, immediate: true })
      }

      function detectColumn(column) {
        const { firstChild } = column

        !firstChild ||
          (!hasClass(firstChild, "videoTeaser") && !hasClass(firstChild, "imageTeaser")) ||
          requestProcessTeasers([firstChild])
      }

      function requestProcessTeasers(teasers) {
        teasers.forEach((teaser) => teaserBatcher.add(teaser))
        teaserBatcher.run(processTeaser)
      }
    }
  )

  class TeaserBatcher {
    teasers = []

    add(teaser) {
      this.teasers.push(teaser)
    }

    run = throttle((callback) => {
      let lastError

      try {
        this.teasers.forEach(callback)
      } catch (e) {
        // only record the last error so the console won't blow up
        lastError = e
      }

      lastError && log("Failed to process teasers", lastError)

      this.teasers.length = 0
    }, 0)
  }

  function processTeaser(teaser) {
    const viewsLabel = $(teaser).find(".views")
    const likesLabel = $(teaser).find(".likes")

    let likePercentage

    const likeRateLabel = viewsLabel.children("." + likeRateClass)

    if (likeRateLabel.length) likePercentage = +likeRateLabel.text().trim().replace("%", "")
    else {
      const views = parseAbbreviatedNumber(viewsLabel.text().trim())
      const likes = parseAbbreviatedNumber(likesLabel.text().trim())

      likePercentage = Math.round((100 * likes) / views)

      const display = Number.isFinite(likePercentage) ? likePercentage + "%" : "/"

      // prettier-ignore
      viewsLabel.children().eq(0).clone().addClass(likeRateClass).text(display).prependTo(viewsLabel)
    }

    likePercentage >= highlightThreshold.value && likeRateEnabled.value
      ? teaser.classList.add(highlightClass)
      : teaser.classList.remove(highlightClass)
  }

  function updateHighlightOpacity() {
    const color = getComputedStyle(document.body).getPropertyValue("--primary").trim()

    // color will be empty before the page is fully loaded
    color &&
      document.body.style.setProperty("--ehg-hl-bg", adjustAlpha(color, highlightOpacity.value))
  }

  const widenListEnabled = (0, external_Vue_namespaceObject.ref)(storage.get("widen_list"))
  const widenListScale = (0, external_Vue_namespaceObject.ref)(storage.get("widen_list_scale"))
  const widenContentEnabled = (0, external_Vue_namespaceObject.ref)(storage.get("widen_content"))
  const widenContentScale = (0, external_Vue_namespaceObject.ref)(
    storage.get("widen_content_scale")
  )

  ;(0, external_Vue_namespaceObject.watch)(widenListEnabled, (enabled) =>
    storage.set("widen_list", enabled)
  )
  ;(0, external_Vue_namespaceObject.watch)(widenListScale, (scale) =>
    storage.set("widen_list_scale", scale)
  )
  ;(0, external_Vue_namespaceObject.watch)(widenContentEnabled, (enabled) =>
    storage.set("widen_content", enabled)
  )
  ;(0, external_Vue_namespaceObject.watch)(widenContentScale, (scale) =>
    storage.set("widen_content_scale", scale)
  )
  ;(0, external_Vue_namespaceObject.watch)(
    [widenListEnabled, widenListScale],
    throttle(updateListScale, 100)
  )

  function useWidenContentSettings() {
    return { widenListEnabled, widenListScale, widenContentEnabled, widenContentScale }
  }

  let widenListStyleEl

  function updateListScale() {
    widenListStyleEl?.remove()
    widenListStyleEl = void 0

    if (widenListEnabled.value) {
      const pageIds = [
        "home",
        "videoList",
        "imageList",
        "subscriptions",
        "profile",
        "video",
        "image",
      ]
      const classes = pageIds.map((pageId) => `.page-${pageId} .container-fluid`)

      widenListStyleEl = GM_addStyle(`${classes.join(",")} {
          max-width: ${1200 * (widenListScale.value / 100)}px;
        }`)
    }
  }

  updateListScale()

  page(["video", "image"], (pageID, onLeave) => {
    const mediaArea = $(".page-video__player, .page-video__slideshow").get(0)

    if (!mediaArea) {
      log(`${pageID === "video" ? "video" : "slideshow"} area not found.`)
      return
    }

    const sidebar = $(".page-video__sidebar").get(0)

    if (!sidebar) {
      log("sidebar not found.")
      return
    }

    const col = $(mediaArea).closest(".col-12").get(0)
    const row = $(mediaArea).closest(".row").get(0)
    const container = $(row).closest(".content").get(0)

    onLeave((0, external_Vue_namespaceObject.watchEffect)(() => updateResize()))

    function updateResize(entries) {
      if (widenContentEnabled.value) {
        let containerWidth = 0
        let rowWidth = 0
        let colWidth = 0
        let mediaHeight = 0

        if (entries)
          for (const entry of entries)
            entry.target === mediaArea
              ? (mediaHeight = entry.contentRect.height)
              : entry.target === col
              ? (colWidth = entry.contentRect.width)
              : entry.target === row
              ? (rowWidth = entry.contentRect.width)
              : entry.target === container && (containerWidth = entry.contentRect.width)
        else {
          containerWidth = container.offsetWidth
          rowWidth = row.offsetWidth
          colWidth = col.offsetWidth
          mediaHeight = mediaArea.offsetHeight
        }

        // iwara uses a polyfilled ResizeObserver, which reports an error when resizing DOMs in the callback immediately,
        // this is so stupid that I have to use setTimeout to avoid the error
        // see: https://github.com/juggle/resize-observer/issues/103
        setTimeout(() => {
          if (containerWidth > 0 && rowWidth > 0 && colWidth > 0) {
            const scale = widenContentScale.value / 100
            const mediaWidth = Math.min(rowWidth * scale, containerWidth)

            mediaArea.style.marginLeft = `${~~((rowWidth - mediaWidth) / 2)}px`
            mediaArea.style.marginRight = `${~~(
              (rowWidth - mediaWidth) / 2 -
              (rowWidth - colWidth)
            )}px`
          }

          mediaHeight > 0 &&
            (sidebar.style.marginTop = `${~~(mediaArea.offsetTop + mediaHeight)}px`)
        }, 0)
      } else {
        mediaArea.style.marginLeft = ""
        mediaArea.style.marginRight = ""
        sidebar.style.marginTop = ""
      }
    }

    const observer = new ResizeObserver(updateResize)

    observer.observe(mediaArea)
    observer.observe(row)
    observer.observe(container)

    onLeave(() => {
      observer.disconnect()
    })
  })

  // extracted by mini-css-extract-plugin
  /* harmony default export */ const Settings_module = {
    switch: "Settings-module__switch--qcsG",
    settings: "Settings-module__settings--alpJ",
    active: "Settings-module__active--iMRv",
    disabled: "Settings-module__disabled--vvjv",
    header: "Settings-module__header--s2Rw",
    title: "Settings-module__title--aDU_",
    view: "Settings-module__view--dY2E",
    sectionHeader: "Settings-module__section-header--Xy_I",
    fieldLabel: "Settings-module__field-label--O5EA",
    labelBlock: "Settings-module__label-block--EYVa",
    labelInline: "Settings-module__label-inline--v3DK",
    panel: "Settings-module__panel--PuCY",
  }

  // recommended vscode plugin for syntax highlighting: https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html
  // language=HTML
  // 由于Iwara的CSP限制,不能运行时渲染Vue模板,需要构建时预编译渲染函数(原模板请参见 git 中的源码)
  function render(_ctx, _cache) {
    return (
      (0, external_Vue_namespaceObject.openBlock)(),
      (0, external_Vue_namespaceObject.createElementBlock)(
        external_Vue_namespaceObject.Fragment,
        null,
        [
          (0, external_Vue_namespaceObject.createElementVNode)(
            "div",
            { class: "text text--text text--bold" },
            "E"
          ),
          _ctx.visible
            ? ((0, external_Vue_namespaceObject.openBlock)(),
              (0, external_Vue_namespaceObject.createElementBlock)(
                "div",
                {
                  key: 0,
                  class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.settings),
                  onClick: (0, external_Vue_namespaceObject.withModifiers)(() => {}, ["stop"]),
                },
                [
                  (0, external_Vue_namespaceObject.createElementVNode)(
                    "header",
                    { class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.header) },
                    [
                      (0, external_Vue_namespaceObject.createElementVNode)(
                        "h2",
                        { class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.title) },
                        (0, external_Vue_namespaceObject.toDisplayString)(_ctx.$t("name")) +
                          " v${GM_info.script.version}",
                        3
                      ),
                    ],
                    2
                  ),
                  (0, external_Vue_namespaceObject.createElementVNode)("nav", null, [
                    (0, external_Vue_namespaceObject.createElementVNode)("ul", null, [
                      ((0, external_Vue_namespaceObject.openBlock)(true),
                      (0, external_Vue_namespaceObject.createElementBlock)(
                        external_Vue_namespaceObject.Fragment,
                        null,
                        (0, external_Vue_namespaceObject.renderList)(
                          _ctx.tabs,
                          (tab, i) => (
                            (0, external_Vue_namespaceObject.openBlock)(),
                            (0, external_Vue_namespaceObject.createElementBlock)(
                              "li",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)({
                                  [_ctx.css.active]: i === _ctx.tabIndex,
                                }),
                                onClick: ($event) => (_ctx.tabIndex = i),
                              },
                              (0, external_Vue_namespaceObject.toDisplayString)(_ctx.$t(tab.name)),
                              11,
                              ["onClick"]
                            )
                          )
                        ),
                        256
                      )),
                    ]),
                  ]),
                  _ctx.tabVal === "ui"
                    ? ((0, external_Vue_namespaceObject.openBlock)(),
                      (0, external_Vue_namespaceObject.createElementBlock)(
                        "div",
                        {
                          key: 0,
                          class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.view),
                        },
                        [
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h2",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.sectionHeader
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.like_rate.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.ui.like_rate.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.enabled")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.withDirectives)(
                                  (0, external_Vue_namespaceObject.createElementVNode)(
                                    "input",
                                    {
                                      type: "checkbox",
                                      "onUpdate:modelValue": ($event) =>
                                        (_ctx.likeRateEnabled = $event),
                                    },
                                    null,
                                    8,
                                    ["onUpdate:modelValue"]
                                  ),
                                  [
                                    [
                                      external_Vue_namespaceObject.vModelCheckbox,
                                      _ctx.likeRateEnabled,
                                    ],
                                  ]
                                ),
                              ],
                              2
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.highlight_threshold.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.ui.highlight_threshold.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.withDirectives)(
                              (0, external_Vue_namespaceObject.createElementVNode)(
                                "input",
                                {
                                  type: "number",
                                  step: "0.1",
                                  min: "0",
                                  max: "100",
                                  "onUpdate:modelValue": ($event) =>
                                    (_ctx.highlightThreshold = $event),
                                },
                                null,
                                8,
                                ["onUpdate:modelValue"]
                              ),
                              [[external_Vue_namespaceObject.vModelText, _ctx.highlightThreshold]]
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.highlight_bg.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.withDirectives)(
                              (0, external_Vue_namespaceObject.createElementVNode)(
                                "input",
                                {
                                  type: "range",
                                  min: "0",
                                  max: "1",
                                  step: "0.01",
                                  "onUpdate:modelValue": ($event) =>
                                    (_ctx.highlightOpacity = $event),
                                },
                                null,
                                8,
                                ["onUpdate:modelValue"]
                              ),
                              [[external_Vue_namespaceObject.vModelText, _ctx.highlightOpacity]]
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.widen_content.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.ui.widen_content.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.enabled")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.withDirectives)(
                                  (0, external_Vue_namespaceObject.createElementVNode)(
                                    "input",
                                    {
                                      type: "checkbox",
                                      "onUpdate:modelValue": ($event) =>
                                        (_ctx.widenContentEnabled = $event),
                                    },
                                    null,
                                    8,
                                    ["onUpdate:modelValue"]
                                  ),
                                  [
                                    [
                                      external_Vue_namespaceObject.vModelCheckbox,
                                      _ctx.widenContentEnabled,
                                    ],
                                  ]
                                ),
                              ],
                              2
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.ui.widen_content.scale")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.createElementVNode)(
                                  "input",
                                  {
                                    type: "number",
                                    step: "1",
                                    min: "10",
                                    max: "500",
                                    value: _ctx.widenContentScale,
                                    onChange: ($event) =>
                                      (_ctx.widenContentScale = Math.round($event.target.value)),
                                  },
                                  null,
                                  40,
                                  ["value", "onChange"]
                                ),
                              ],
                              2
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.ui.widen_list.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.ui.widen_list.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.enabled")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.withDirectives)(
                                  (0, external_Vue_namespaceObject.createElementVNode)(
                                    "input",
                                    {
                                      type: "checkbox",
                                      "onUpdate:modelValue": ($event) =>
                                        (_ctx.widenListEnabled = $event),
                                    },
                                    null,
                                    8,
                                    ["onUpdate:modelValue"]
                                  ),
                                  [
                                    [
                                      external_Vue_namespaceObject.vModelCheckbox,
                                      _ctx.widenListEnabled,
                                    ],
                                  ]
                                ),
                              ],
                              2
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.ui.widen_list.scale")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.createElementVNode)(
                                  "input",
                                  {
                                    type: "number",
                                    step: "1",
                                    min: "10",
                                    max: "500",
                                    value: _ctx.widenListScale,
                                    onChange: ($event) =>
                                      (_ctx.widenListScale = Math.round($event.target.value)),
                                  },
                                  null,
                                  40,
                                  ["value", "onChange"]
                                ),
                              ],
                              2
                            ),
                          ]),
                        ],
                        2
                      ))
                    : _ctx.tabVal === "download"
                    ? ((0, external_Vue_namespaceObject.openBlock)(),
                      (0, external_Vue_namespaceObject.createElementBlock)(
                        "div",
                        {
                          key: 1,
                          class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.view),
                        },
                        [
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h2",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.sectionHeader
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.download.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.download.auto.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.download.auto.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "label",
                              {
                                class: (0, external_Vue_namespaceObject.normalizeClass)(
                                  _ctx.css.labelBlock
                                ),
                              },
                              [
                                (0, external_Vue_namespaceObject.createTextVNode)(
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$t("s.enabled")
                                  ) + " ",
                                  1
                                ),
                                (0, external_Vue_namespaceObject.withDirectives)(
                                  (0, external_Vue_namespaceObject.createElementVNode)(
                                    "input",
                                    {
                                      type: "checkbox",
                                      "onUpdate:modelValue": ($event) =>
                                        (_ctx.autoDownEnabled = $event),
                                    },
                                    null,
                                    8,
                                    ["onUpdate:modelValue"]
                                  ),
                                  [
                                    [
                                      external_Vue_namespaceObject.vModelCheckbox,
                                      _ctx.autoDownEnabled,
                                    ],
                                  ]
                                ),
                              ],
                              2
                            ),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.download.resolution.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            ((0, external_Vue_namespaceObject.openBlock)(true),
                            (0, external_Vue_namespaceObject.createElementBlock)(
                              external_Vue_namespaceObject.Fragment,
                              null,
                              (0, external_Vue_namespaceObject.renderList)(
                                _ctx.RESOLUTIONS,
                                (res) => (
                                  (0, external_Vue_namespaceObject.openBlock)(),
                                  (0, external_Vue_namespaceObject.createElementBlock)(
                                    "label",
                                    {
                                      class: (0, external_Vue_namespaceObject.normalizeClass)(
                                        _ctx.css.labelInline
                                      ),
                                    },
                                    [
                                      (0, external_Vue_namespaceObject.withDirectives)(
                                        (0, external_Vue_namespaceObject.createElementVNode)(
                                          "input",
                                          {
                                            type: "radio",
                                            name: "res",
                                            value: res,
                                            "onUpdate:modelValue": ($event) =>
                                              (_ctx.resolution = $event),
                                          },
                                          null,
                                          8,
                                          ["value", "onUpdate:modelValue"]
                                        ),
                                        [
                                          [
                                            external_Vue_namespaceObject.vModelRadio,
                                            _ctx.resolution,
                                          ],
                                        ]
                                      ),
                                      (0, external_Vue_namespaceObject.createTextVNode)(
                                        " " +
                                          (0, external_Vue_namespaceObject.toDisplayString)(res),
                                        1
                                      ),
                                    ],
                                    2
                                  )
                                )
                              ),
                              256
                            )),
                          ]),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.download.filename.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            { innerHTML: _ctx.$t("s.download.filename.desc") },
                            null,
                            8,
                            ["innerHTML"]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "div",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.keywords
                              ),
                            },
                            [
                              (0, external_Vue_namespaceObject.createElementVNode)(
                                "table",
                                {
                                  class: (0, external_Vue_namespaceObject.normalizeClass)(
                                    _ctx.css.keywordTable
                                  ),
                                },
                                [
                                  ((0, external_Vue_namespaceObject.openBlock)(true),
                                  (0, external_Vue_namespaceObject.createElementBlock)(
                                    external_Vue_namespaceObject.Fragment,
                                    null,
                                    (0, external_Vue_namespaceObject.renderList)(
                                      _ctx.FILENAME_KEYWORDS,
                                      (kw) => (
                                        (0, external_Vue_namespaceObject.openBlock)(),
                                        (0, external_Vue_namespaceObject.createElementBlock)(
                                          "tr",
                                          null,
                                          [
                                            (0, external_Vue_namespaceObject.createElementVNode)(
                                              "th",
                                              null,
                                              (0, external_Vue_namespaceObject.toDisplayString)(kw),
                                              1
                                            ),
                                            (0, external_Vue_namespaceObject.createElementVNode)(
                                              "td",
                                              null,
                                              (0, external_Vue_namespaceObject.toDisplayString)(
                                                _ctx.$t(
                                                  "s.download.filename.key." + kw.toLowerCase()
                                                )
                                              ),
                                              1
                                            ),
                                          ]
                                        )
                                      )
                                    ),
                                    256
                                  )),
                                ],
                                2
                              ),
                            ],
                            2
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("details", null, [
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "summary",
                              null,
                              (0, external_Vue_namespaceObject.toDisplayString)(_ctx.$t("s.extra")),
                              1
                            ),
                            (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                              (0, external_Vue_namespaceObject.createTextVNode)(
                                (0, external_Vue_namespaceObject.toDisplayString)(
                                  _ctx.$t("s.download.filename.replace_illegal_char")
                                ) + " ",
                                1
                              ),
                              (0, external_Vue_namespaceObject.withDirectives)(
                                (0, external_Vue_namespaceObject.createElementVNode)(
                                  "input",
                                  {
                                    type: "text",
                                    "onUpdate:modelValue": ($event) =>
                                      (_ctx.illegalCharReplacement = $event),
                                  },
                                  null,
                                  8,
                                  ["onUpdate:modelValue"]
                                ),
                                [
                                  [
                                    external_Vue_namespaceObject.vModelText,
                                    _ctx.illegalCharReplacement,
                                  ],
                                ]
                              ),
                              (0, external_Vue_namespaceObject.createTextVNode)(
                                " " +
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    "*miku*miku:dance??.mp4 -> "
                                  ) +
                                  " " +
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.sanitizePath(
                                      "*miku*miku:dance??.mp4",
                                      _ctx.illegalCharReplacement
                                    )
                                  ),
                                1
                              ),
                            ]),
                          ]),
                          (0, external_Vue_namespaceObject.withDirectives)(
                            (0, external_Vue_namespaceObject.createElementVNode)(
                              "input",
                              {
                                type: "text",
                                "onUpdate:modelValue": ($event) => (_ctx.filenameTemplate = $event),
                              },
                              null,
                              8,
                              ["onUpdate:modelValue"]
                            ),
                            [[external_Vue_namespaceObject.vModelText, _ctx.filenameTemplate]]
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "p",
                            null,
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.download.filename.preview") + ": " + _ctx.filenamePreview
                            ),
                            1
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "div",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.panel
                              ),
                            },
                            [
                              (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                                (0, external_Vue_namespaceObject.createElementVNode)(
                                  "b",
                                  null,
                                  (0, external_Vue_namespaceObject.toDisplayString)(
                                    _ctx.$tm("s.download.filename.tips")[0]
                                  ),
                                  1
                                ),
                              ]),
                              (0, external_Vue_namespaceObject.createElementVNode)("ul", null, [
                                ((0, external_Vue_namespaceObject.openBlock)(true),
                                (0, external_Vue_namespaceObject.createElementBlock)(
                                  external_Vue_namespaceObject.Fragment,
                                  null,
                                  (0, external_Vue_namespaceObject.renderList)(
                                    _ctx.$tm("s.download.filename.tips").slice(1),
                                    (tip) => (
                                      (0, external_Vue_namespaceObject.openBlock)(),
                                      (0, external_Vue_namespaceObject.createElementBlock)(
                                        "li",
                                        null,
                                        [
                                          (0, external_Vue_namespaceObject.createElementVNode)(
                                            "p",
                                            { innerHTML: tip },
                                            null,
                                            8,
                                            ["innerHTML"]
                                          ),
                                        ]
                                      )
                                    )
                                  ),
                                  256
                                )),
                              ]),
                            ],
                            2
                          ),
                        ],
                        2
                      ))
                    : (0, external_Vue_namespaceObject.createCommentVNode)("v-if", true),
                  _ctx.tabVal === "script"
                    ? ((0, external_Vue_namespaceObject.openBlock)(),
                      (0, external_Vue_namespaceObject.createElementBlock)(
                        "div",
                        {
                          key: 2,
                          class: (0, external_Vue_namespaceObject.normalizeClass)(_ctx.css.view),
                        },
                        [
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h2",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.sectionHeader
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.script.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)(
                            "h3",
                            {
                              class: (0, external_Vue_namespaceObject.normalizeClass)(
                                _ctx.css.fieldLabel
                              ),
                            },
                            (0, external_Vue_namespaceObject.toDisplayString)(
                              _ctx.$t("s.script.language.label")
                            ),
                            3
                          ),
                          (0, external_Vue_namespaceObject.createElementVNode)("p", null, [
                            ((0, external_Vue_namespaceObject.openBlock)(true),
                            (0, external_Vue_namespaceObject.createElementBlock)(
                              external_Vue_namespaceObject.Fragment,
                              null,
                              (0, external_Vue_namespaceObject.renderList)(
                                _ctx.$i18n.availableLocales,
                                (loc) => (
                                  (0, external_Vue_namespaceObject.openBlock)(),
                                  (0, external_Vue_namespaceObject.createElementBlock)(
                                    "label",
                                    {
                                      class: (0, external_Vue_namespaceObject.normalizeClass)(
                                        _ctx.css.labelInline
                                      ),
                                    },
                                    [
                                      (0, external_Vue_namespaceObject.createElementVNode)(
                                        "input",
                                        {
                                          type: "radio",
                                          name: "loc",
                                          value: loc,
                                          checked: _ctx.activeLocale === loc,
                                          onChange: ($event) => (_ctx.locale = loc),
                                        },
                                        null,
                                        40,
                                        ["value", "checked", "onChange"]
                                      ),
                                      (0, external_Vue_namespaceObject.createTextVNode)(
                                        " " +
                                          (0, external_Vue_namespaceObject.toDisplayString)(
                                            _ctx.localeName(loc)
                                          ),
                                        1
                                      ),
                                    ],
                                    2
                                  )
                                )
                              ),
                              256
                            )),
                          ]),
                        ],
                        2
                      ))
                    : (0, external_Vue_namespaceObject.createCommentVNode)("v-if", true),
                ],
                10,
                ["onClick"]
              ))
            : (0, external_Vue_namespaceObject.createCommentVNode)("v-if", true),
        ],
        64
      )
    )
  }

  function setup() {
    const tabs = [
      { name: "s.ui.label", val: "ui" },
      { name: "s.download.label", val: "download" },
      { name: "s.script.label", val: "script" },
    ]
    const tabIndex = (0, external_Vue_namespaceObject.ref)(0)
    const tabVal = (0, external_Vue_namespaceObject.computed)(
      () => tabs[tabIndex.value] && tabs[tabIndex.value].val
    )
    const visible = (0, external_Vue_namespaceObject.ref)(false)

    const onClickContainer = () => {
      visible.value = !visible.value

      visible.value && onClickOutside(settingsContainer, () => (visible.value = false))
    }

    settingsContainer.addEventListener("click", onClickContainer)

    ;(0, external_Vue_namespaceObject.onBeforeUnmount)(() => {
      settingsContainer.removeEventListener("click", onClickContainer)
    })

    function localeName(loc) {
      const msg = i18n_i18n.global.getLocaleMessage(loc)
      return msg?.language || loc
    }

    return {
      css: Settings_module,
      tabs,
      tabIndex,
      tabVal,
      visible,
      downloadMode: GM_info.downloadMode,
      sanitizePath,
      localeName,
      ...useDownloaderSettings(),
      ...useConfigSettings(),
      ...useTeaserSettings(),
      ...useWidenContentSettings(),
    }
  }

  const SETTINGS_ID = "enh-settings"

  const settingsContainer = $(
    `<div id="${SETTINGS_ID}" class='header__link ${Settings_module.switch}'></div>`
  )[0]

  let app

  page(ALL, (pageID, onLeave) => {
    const destination = $(".page .dropdown--bottomLeft, a[href='/register']")[0]

    if (destination) {
      // destination element will be destroyed everytime the page changes,
      // so we need to insert the container after every page change
      destination.before(settingsContainer)

      // lazy-init the app
      if (!app)
        try {
          app = (0, external_Vue_namespaceObject.createApp)({ render, setup })

          app.use(i18n_i18n)

          true &&
            // pending fix https://github.com/vuejs/core/pull/5197
            // @ts-ignore
            (unsafeWindow.Vue = Vue)

          app.mount(settingsContainer)

          log("Settings view initialized")
        } catch (e) {
          log("Settings view init failed: " + (e?.message || e))
          app = void 0
          settingsContainer.textContent = "E"
        }
    } else log("Could not insert settings view: container not found.")
  })

  // prevent Sentry from tracking the logging
  setLogger(console.log.__sentry_original__ || console.log)

  const patchedFlag = "__enhPatched"

  page(["video"], async (pageID, onLeave) => {
    const timerPromise = until(() => {
      const player = getPlayer()

      if (player) {
        fixResolution(player)

        if (!(patchedFlag in player)) {
          player[patchedFlag] = true
          preventVolumeScrolling(player)
        }
      }
    }, 500)

    onLeave(() => timerPromise.cancel())

    function getPlayer() {
      return $(".page-video__player .video-js").get(0)?.player
    }

    function preventVolumeScrolling(player) {
      const originalGet = WeakMap.prototype.get

      // hook WeakMap.get() to get the event data
      // https://github.com/videojs/video.js/blob/2b0df25df332dceaab375327887f0721ca8d21d0/src/js/utils/events.js#L271
      WeakMap.prototype.get = function (key) {
        const value = originalGet.call(this, key)

        try {
          const data = value

          if (data?.handlers?.mousewheel) {
            log(`removing ${data.handlers.mousewheel.length} mousewheel handler(s) from Player`)

            // the listeners are bound functions and cannot be checked with toString(),
            // so we have to remove all mousewheel handlers
            delete data.handlers.mousewheel
          }
        } catch (e) {
          log("error:", e)
        } finally {
          return value
        }
      }

      // trigger the hook by adding an arbitrary event listener
      player.on("__dummy", () => {})
      player.off("__dummy")

      WeakMap.prototype.get = originalGet

      const originalOn = player.on

      // prevent adding new mousewheel listeners
      player.on = function (targetOrType) {
        if (targetOrType === "mousewheel") {
          log("prevented adding mousewheel listener")
          return
        }

        for (
          var _len = arguments.length, rest = new Array(_len > 1 ? _len - 1 : 0), _key = 1;
          _key < _len;
          _key++
        )
          rest[_key - 1] = arguments[_key]
        return originalOn.call(this, targetOrType, ...rest)
      }
    }

    function fixResolution(player) {
      const targetSource = getTargetSource(player)

      if (targetSource && targetSource.src !== player.src()) {
        log(`setting resolution to ${targetSource.name}: ${targetSource.src}`)
        player.src(targetSource)
      }
    }

    function getTargetSource(player) {
      const sources = player.currentSources()

      if (!sources.length) return

      const selectedResName = localStorage.getItem("player-resolution")
      const source = sources.find((s) => s.name === selectedResName)

      if (source) return source

      log(`error: source not found for ${selectedResName}`)

      return
    }
  })

  // this "bg" is covering the video player and preventing player from entering fullscreen mode by double-clicks
  GM_addStyle(`
.videoPlayer__bg {
  pointer-events: none;
}
`)

  const state = (0, external_Vue_namespaceObject.reactive)({ theme: "light" })

  setInterval(() => {
    state.theme = localStorage.theme
  }, 1e3)

  ;(0, external_Vue_namespaceObject.watchEffect)(updateTheme)

  function updateTheme() {
    const theme = state.theme
    const adjustmentSign = theme === "light" ? -1 : 1
    const bodyColor = getComputedStyle(document.body).getPropertyValue("--body")

    document.body.style.setProperty(
      "--enh-body-focus",
      adjustHexColor(bodyColor, adjustmentSign * 15)
    )
    document.body.style.setProperty(
      "--enh-body-highlight",
      adjustHexColor(bodyColor, adjustmentSign * 30)
    )

    const darkClass = "enh-dark"

    theme === "dark"
      ? document.body.classList.add(darkClass)
      : document.body.classList.remove(darkClass)
  }

  async function main() {
    document.body.classList.add("enh-body")

    setupPaging()
  }

  main()
})()

GM_addStyle(`
.Settings-module__switch--qcsG {
  cursor: pointer;
}

.Settings-module__settings--alpJ {
  position: absolute;
  z-index: 1000;
  top: 100%;
  right: 0;
  width: 400px;
  max-height: calc(100vh - 65px);
  overflow: auto;
  background: var(--body);
  font-size: 14px;
  border: 2px solid var(--primary);
  border-top: none;
  cursor: default;
}

.Settings-module__settings--alpJ nav {
    padding: 0 16px;
    border-bottom: 1px solid var(--enh-body-highlight);
  }

.Settings-module__settings--alpJ nav ul {
      margin: 0;
      padding: 0;
      display: flex;
      flex-wrap: wrap;
    }

.Settings-module__settings--alpJ nav li {
      padding: 8px 16px;
      list-style-type: none;
      cursor: pointer;
    }

.Settings-module__settings--alpJ nav li:hover {
        background: var(--enh-body-focus);
      }

.Settings-module__settings--alpJ nav li.Settings-module__active--iMRv {
        background: var(--enh-body-highlight);
      }

.Settings-module__settings--alpJ details {
    border: 1px solid var(--enh-body-highlight);
  }

.Settings-module__settings--alpJ details > * {
      padding: 0 8px;
    }

.Settings-module__settings--alpJ p,
  .Settings-module__settings--alpJ summary {
    color: var(--muted);
  }

.Settings-module__settings--alpJ p {
    margin-top: 0;
    margin-bottom: 8px;
  }

.Settings-module__settings--alpJ a {
    font-weight: bold;
    cursor: pointer;
  }

.Settings-module__settings--alpJ ol,
  .Settings-module__settings--alpJ ul {
    padding-left: 20px;
  }

.Settings-module__settings--alpJ table {
    margin: 8px 0;
    width: 100%;
    background: var(--enh-body-focus);
    border: 1px solid var(--enh-body-highlight);
    border-collapse: collapse;
  }

.Settings-module__settings--alpJ th {
    text-align: right;
  }

.Settings-module__settings--alpJ th,
  .Settings-module__settings--alpJ td {
    padding: 4px 8px;
    border: 1px solid var(--enh-body-highlight);
  }

.Settings-module__settings--alpJ label,
  .Settings-module__settings--alpJ summary {
    cursor: pointer;
  }

.Settings-module__settings--alpJ label:hover, .Settings-module__settings--alpJ summary:hover {
      background: var(--enh-body-focus);
    }

.Settings-module__settings--alpJ label input, .Settings-module__settings--alpJ summary input {
      cursor: pointer;
    }

.Settings-module__settings--alpJ label.Settings-module__disabled--vvjv, .Settings-module__settings--alpJ summary.Settings-module__disabled--vvjv {
      cursor: not-allowed;
    }

.Settings-module__settings--alpJ label.Settings-module__disabled--vvjv input, .Settings-module__settings--alpJ summary.Settings-module__disabled--vvjv input {
        cursor: not-allowed;
      }

.Settings-module__settings--alpJ input[type="text"] {
    outline: none !important;
  }

.Settings-module__settings--alpJ input[type="text"] {
    margin: 8px 0;
    width: 100%;
    padding: 8px;
    background: var(--enh-body-focus);
    color: var(--text);
    border: 2px solid var(--enh-body-highlight);
    border-radius: 3px;
  }

.Settings-module__settings--alpJ input[type="text"]:hover,
    .Settings-module__settings--alpJ input[type="text"]:focus {
      background: var(--enh-body-highlight);
    }

.Settings-module__header--s2Rw {
  padding: 0 16px;
}

.Settings-module__title--aDU_ {
  margin-top: 4px;
}

.Settings-module__view--dY2E {
  padding: 16px;
}

.Settings-module__section-header--Xy_I {
  margin-bottom: 16px;
}

.Settings-module__field-label--O5EA {
  position: relative;
  margin: 16px 0;
  padding-top: 16px;
}

.Settings-module__field-label--O5EA:not(:first-of-type) {
    border-top: 1px solid var(--enh-body-highlight);
  }

.Settings-module__label-block--EYVa {
  display: flex;
  padding: 8px 8px 8px 0;
}

.Settings-module__label-block--EYVa input {
    margin-left: auto;
  }

.Settings-module__label-inline--v3DK {
  display: inline-flex;
  padding: 8px 8px 8px 0;
}

.Settings-module__label-inline--v3DK:not(:first-child) {
    padding-left: 8px;
  }

.Settings-module__label-inline--v3DK:not(:last-child) {
    margin-right: 8px;
  }

.Settings-module__panel--PuCY {
  margin-bottom: 8px;
  padding: 8px;
  background: var(--enh-body-focus);
}


.enh-body {
  --ehg-hl-bg: rbga(0, 0, 0, 0);
}

#enh-settings {
  position: relative;
}

#enh-settings * {
    box-sizing: border-box;
  }

.enh-like-rate {
  display: none;
}

.enh-show-like-rates .videoTeaser .views, .enh-show-like-rates .imageTeaser .views {
    }

.enh-show-like-rates .videoTeaser .enh-like-rate, .enh-show-like-rates .imageTeaser .enh-like-rate {
      display: block;
    }

.enh-show-like-rates .videoTeaser .enh-like-rate + .text, .enh-show-like-rates .imageTeaser .enh-like-rate + .text {
        display: none;
      }

.enh-show-like-rates .page-start__subscriptions,
  .enh-show-like-rates .page-start__videos,
  .enh-show-like-rates .page-start__images {
    position: relative;
    z-index: 0;
  }

/* for all the affected pages, check out process-teaser.ts */
.enh-highlight:before {
    content: "";
    position: absolute;
    z-index: -1;
    top: -8px;
    bottom: 7px;
    left: 7px;
    right: 7px;
    background: var(--ehg-hl-bg);
  }
.page-video .enh-highlight:before,
    .page-image .enh-highlight:before {
      content: none;
    }
.page-profile .enh-highlight,
  .page-subscriptions .enh-highlight {
    position: relative;
  }
.page-profile .enh-highlight:before, .page-subscriptions .enh-highlight:before {
      top: -6px;
      bottom: -6px;
      left: -6px;
      right: -6px;
    }
.page-video .enh-highlight,
  .page-image .enh-highlight {
    background: var(--ehg-hl-bg);
  }

`)