[
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"parserOptions\": {\n        \"ecmaVersion\": 11\n    },\n    \"extends\": [\n        \"airbnb-base\",\n        \"prettier\"\n    ],\n    \"rules\": {\n        \"camelcase\": \"off\",\n        \"linebreak-style\": \"off\"\n    },\n    \"globals\": {\n        \"axios\": \"readonly\",\n        \"window\": \"readonly\",\n        \"browser\": \"readonly\",\n        \"chrome\": \"readonly\"\n    },\n    \"env\": {\n        \"browser\": true,\n        \"node\": false\n    },\n    \"ignorePatterns\": [\n        \"**/vendor/*.js\"\n    ]\n}"
  },
  {
    "path": ".github/workflows/eslint.yml",
    "content": "name: ESLint\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n\n      - name: Install Dependencies\n        run: npm ci\n\n      - name: Run ESLint\n        run: npx eslint .\n"
  },
  {
    "path": ".gitignore",
    "content": "# Folder view configuration files\n.DS_Store\n.vscode\n.eslintcache\n\nnode_modules\ndist\n_metadata"
  },
  {
    "path": ".prettierrc",
    "content": "{\n    \"singleQuote\": true,\n    \"trailingComma\": \"es5\"\n}"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Listen 1\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# Listen 1 (Chrome Extension) V2.33.0\n\n（最后更新于 2025 年 6 月 17 日）\n\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)\n\n[English Version](https://github.com/listen1/listen1_chrome_extension/blob/master/README_EN.md)\n\n## 缘起\n\n当我发现找个想听的歌因为版权听不了，需要打开好几个网站开始搜索，来回切换让我抓狂的时候，我知道是时候该做点什么了。\n\n妈妈再也不用担心我找不到我想听的歌了。\n\n支持音乐平台\n\n- 网易云音乐\n- QQ 音乐\n- 酷狗音乐\n- 酷我音乐\n- bilibili\n- 咪咕音乐\n- 千千音乐\n\n搜歌，听歌，就用 `Listen1`。\n\n[![imgur](https://i.imgur.com/dIVFtor.gif)]()\n\nV2.9.0 新特性：自动切换播放源(Beta)\n\n当一首歌的播放源不可用时，会自动搜索其他平台，获得可用的播放源。避免了用户手动搜索的麻烦。\n\n还有精选歌单哦。\n\n## 官方商店安装（推荐）\n\n按你的浏览器类型点击下面的链接安装\n\n- [Chrome Web Store 安装](https://chrome.google.com/webstore/detail/listen-1/indecfegkejajpaipjipfkkbedgaodbp)\n- [FireFox 安装](https://addons.mozilla.org/zh-CN/firefox/addon/listen1/)\n- [Microsoft Edge 安装](https://microsoftedge.microsoft.com/addons/detail/bcneiehcbgahghfmgigmblcgkhihehad)\n\n感谢 [@TNT-c](https://github.com/TNT-c) 维护 Firefox 的发布渠道\n\n感谢 [@dhxh](https://github.com/dhxh) 维护 Microsoft Edge 的发布渠道\n\n## Chrome 下载安装\n\n1. 下载项目的 zip 文件，在右上方有个 `Download ZIP`, 解压到本地\n\n2. chrome 右上角的设置按钮下找到更多工具，打开`扩展程序`\n\n3. 选择 `加载已解压的扩展程序`(如果没有显示先选中`开发者模式`)，选中解压后的文件夹，完成！\n\n## Firefox 打包安装\n\n1. 将根目录下 manifest_firefox.json 替换 manifest.json\n\n2. `cd listen1_chrome_extension`\n\n3. `zip -r ../listen1.xpi *`, 完成打包 xpi 文件\n\n4. 打开 Firefox，加载 xpi 文件，完成安装\n\n## QQ 音乐举报 Listen1 导致代码库临时关闭事件 （2017 年 11 月）\n\nListen1 的用户，有个坏消息希望和大家分享。Listen1 最近收到了[QQ 音乐的 DMCA Takedown Notice](https://github.com/github/dmca/blob/master/2017/2017-11-17-Listen1.md), 主要代码库已经因为此原因而临时关闭。悲观一点看，Listen1 项目可能会在今年内彻底消失。\n\nListen1 诞生的初衷从不是和大公司的争夺版权利益，而是为了给予热爱音乐的人更好的收听体验，所以，Listen1 是开源，免费的，并且不接受任何形式的捐助。正是因为有热爱音乐的 Listen1 的你们，Listen1 才发展到今天这一步。不管结果如何，Listen1 团队感谢所有支持过这个项目的人们。\n\n在这个关系项目生死存亡的时刻，我寻求项目因为 DMCA 被 github 关闭的援助。如果有对这个比较了解如何解决的人，或者你想对这个事情发表看法和建议，可以在[issue](https://github.com/listen1/listen1_chrome_extension/issues/113)留言，或者发送邮件到 githublisten1@gmail.com。我们会尽最大努力，来守护 Listen1，即使可能它即将成为历史。\n\n## 更新日志\n\n`2025-06`\n\n修复：\n\n- 修复咪咕无法播放的问题\n- 修复播放部分歌曲时播放控制失灵的问题(#1200) (#1201) （感谢 @reserveword 的提交）\n- 修复 electron remote 模块以支持更多的 electron 版本 （感谢 @xihale 的提交）\n- 修复新主题性能问题 (#1216) （感谢 @mikelxk 的提交）\n- 修复 electron 的 cookie 未发送问题\n- 修复文档拼写错误 (感谢 Nicholas Wilson)\n\n功能改进：\n\n- 将 chrome 插件的 manifest 升级到 V3\n\n`2023-08 ~ 2024-04`\n\n修复：\n\n- 迁移哔哩哔哩用户信息获取接口 （感谢@wuhao-igno 的提交）\n- 修复 QQ 音乐播放问题\n- 修复酷我播放和搜索问题\n- 修复酷狗音乐播放问题\n\n`2023-07`\n\n修复：\n\n- 酷我音乐搜索功能错误 （感谢@yhsj0919 提供解决方案）\n- 控制台报错 （感谢 @caoxiemeihao 的提交）\n\n`2022-12 ~ 2023-03`\n\n功能改进：\n\n- 增加了葡萄牙语(巴西)的翻译 (感谢 @nailtonvital 的提交)\n- 增加了 qq 音乐 320kbps 音质的支持 (感谢 @fecet 的提交)\n\n修复：\n\n- 修复了酷我音乐播放接口失效的问题 (感谢 @NickeyLin 的提交)\n- 修复导出歌单失败的问题 (感谢 @@IcedWatermelonJuice 的提交)\n- 修复了现在播放页面打开后图标状态的问题 (感谢 @@mnyon 的提交)\n- 修复了 lastfm 不记录最近播放的问题 (感谢 @@Anmizi 的提交)\n\n`2022-11`\n\n功能改进：\n\n- 增加 bilibili 歌单搜索功能(感谢 @Wei-bin-Wu 的提交)\n- 优化现代主题的显示风格 (感谢 @814959822 的提交)\n\n修复:\n\n- 修复 bilibili 部分音乐无法播放的问题\n- 修复本地音乐播放文件类型的问题 (感谢 @mikelxk 的提交)\n- 修复播放 b 站音乐时的图片显示问题 (感谢 @mikelxk 的提交)\n\n`2022-09`\n\n功能改进\n\n- 添加现代白，现代黑两款主题（感谢 @814959822 的提交）\n- 优化了新主题性能\n\n修复：\n\n- 修复 bilibili 的搜索错误 (感谢 @mikelxk 的提交)\n- 修复选取音乐平台时发生的闪动问题（感谢 @814959822 的提交）\n- 修复新主题在歌单少于 5 首时的显示问题\n\n`2022-08`\n\n修复：\n\n- 修复开启一段时间后无法播放的问题 #902 （感谢 @reserveword 的提交）\n- 修复 QQ 音乐无法搜索的问题 (感谢 lx-music-desktop 提供技术方案)\n- 修复 bilibili 搜索没有响应的问题\n\n`2022-07`\n\n功能改进：\n\n- 增加 bilibili 视频音源搜索功能\n\n修复：\n\n- 修复酷狗音乐的播放错误\n- 修复酷狗音乐热门歌单加载更多时的错误\n- 修复 qq 音乐歌手页的错误\n- 修复咪咕音乐排行榜打开时的错误\n\n`2022-06`\n\n功能改进\n\n- 添加双击歌单列表和搜索结果列表播放（感谢 @piz-ewing 的提交）\n- 桌面版本地音乐增加 wav 格式支持 (感谢 @mikelxk 的提交)\n\n  修复：\n\n- 修复清空列表当前播放音乐不停止的问题(感谢 @leca 的提交）\n- 酷狗音乐列表无法打开的问题\n- 修复随机播放模式出现重复音乐的 bug (感谢 @piz-ewing 的提交)\n\n`2022-02 ~ 2022-03`\n\n功能改进：\n\n- 增加韩语支持（感谢 @kkange 的提交）\n\n修复：\n\n- 酷狗音乐无法播放的问题\n- 千千音乐列表接口无法访问的问题（感谢 @mikelxk 的提交）\n\n`2021-08 ~ 2022-01`\n\n修复：\n\n- 修复音乐分类按钮显示没有间距的问题 （感谢 @yinzhenyu-su 的提交）\n- 修复在 firefox 无法打开 bilibili 音乐的问题 (感谢 @ktmzcpl 的提交)\n- 修复在 electron 环境启动时的 UI 崩溃问题\n\n优化：\n\n- 更平滑的当前播放切换效果 (感谢 @mikelxk 的提交)\n\n`2021-07`\n\n修复：\n\n- 禁止图片拖动\n- 增加快捷键中放大缩小功能的描述\n- 修改 windows 用户的窗口控制按钮位置到右上角 (感谢 @mikelxk 的提交)\n- 升级 howler 库 (感谢 @mikelxk 的提交)\n- 修复 QQ 音乐无法搜索的问题\n- 修复 chrome 浏览器媒体控制中进度条拖动的问题 (感谢 @mikelxk 的提交)\n- 增加本地音乐的本地 lrc 歌词文件支持 (感谢 @mikelxk 的提交)\n\n`2021-04`\n\n功能改进：\n\n- 增加 QQ 音乐的登录支持\n- 增加拖拽支持，支持歌单内歌曲调整顺序，歌单调整顺序，正在播放歌曲调整顺序，以及拖动歌曲加入歌单的操作\n- 支持歌单内搜索\n- 桌面版支持代理设置\n- 支持配置自动切换源的搜索平台\n- 增加显示当前最新版本\n- 增加对网易云平台的默认高码率音源支持\n\n重构和优化：\n\n- 将音乐平台接口做 class 改造 #553\n- github 模块去除 angular 依赖 #532 (感谢 @Dumeng 的提交)\n- lastfm 模块去除 angular 依赖 #532 (感谢 @Dumeng 的提交)\n- 优化 UI 细节，提升用户体验 #537\n\n修复：\n\n- 修复需要登录才能获取咪咕播放链接，并增加码率数据 #536 (感谢 @RecluseWind 的提交)\n- 修复音乐榜和影视榜在 Firefox 上的不能正确获取的 bug #536 (感谢 @RecluseWind 的提交)\n- 修复某些情况下歌曲在播放前总是等待 15 秒的 bug\n- 修复 QQ 音乐短链接歌单分享地址不被识别的问题\n- 修复开启关闭静音功能失效的问题\n- 修复 GitHub 账户无法退出的问题\n- 修复 kugou 部分音乐因专辑缺失导致的播放错误\n- 修复多首歌曲重复播放的问题\n\n`2021-03`\n\n功能改进：\n\n- 新增千千音乐平台 (感谢 @Dumeng 的提交)\n- 支持咪咕音乐的分类歌单和排行榜歌单功能 (感谢 @RecluseWind 的提交)\n- 桌面版支持放大功能 (感谢 @mikelxk 的提交)\n- 支持网易登录功能，支持打开我的歌单和推荐歌单\n- 支持咪咕登录功能\n- 支持在正在播放页面显示当前播放歌曲的码率和平台\n- 移除虾米平台\n\n重构和优化：\n\n- 替换了对 translate，i18n, hotkeys 的 angular 模块依赖，替换为纯 js 库 (感谢 @Dumeng 的提交)\n- 优化载入 feather 图标库的效率 (感谢 @Dumeng 的提交)\n- 改善了多个平台默认码率，默认播放高码率音乐文件\n- 将 app.js 按多个 controller 模块分为多个文件\n- 优化显示了因为版权问题无法播放的通知\n- 将大部分链接改成 https 协议\n\n修复：\n\n- 修复新语法导致媒体控制在某些系统中不可用的问题 (感谢 @mikelxk 的提交)\n- 修复音量控制快捷键失效的问题 (感谢 @mikelxk 的提交)\n- 修复了在 firefox 上的滚动条样式 (感谢 @RecluseWind 的提交)\n- 修复酷狗音乐封面的错误\n- 修复酷狗某些歌曲不能播放的问题\n- 修复通知无法显示的问题\n- 修复了删除当前播放列表歌曲后导致的各种异常\n\n`2021-02`\n\n功能改进：\n\n- 支持分类歌单和排行榜（感谢 https://github.com/lyswhut/lx-music-desktop 提供 QQ 音乐排行实现）\n- 增加繁体中文翻译 (感谢 @yujiangqaq 提供翻译)\n- 增加 chrome 媒体控制上一曲，下一曲和快进快退 （感谢 @mikelxk 的提交）\n- 改进桌面版桌面歌词，增加字体大小颜色设置和背景透明度调整\n\n重构：\n\n- 将媒体资源服务重构成 MediaService 模块，除去对 angularjs 的依赖 （特别感谢 @Dumeng 的提交）\n- 增加 prettier 配置文件和 commit 前检查 （感谢 @mikelxk 的提交）\n- 修正一些过往代码的格式错误 （感谢 @mikelxk 的提交）\n\n修复：\n\n- 修复 Github API （感谢 @NoDocCat 和 @Dumeng 的提交）\n- 修复因 svg 动画导致的性能问题 （感谢 @Dumeng 的提交）\n- 修复虾米部分失效 API（感谢 @RecluseWind 的提交）\n- 修复 Mac 桌面版无法导入本地音乐的问题 （感谢 @virgil1996 的提交）\n- 修复酷我搜索出错的问题\n\n`2021-01`\n\n功能改进：\n\n- 支持插件版后台播放功能 (特别感谢 @Dumeng 的提交)\n- 优化酷我代码 (感谢 @RecluseWind 的提交)\n- 优化咪咕音乐代码 (感谢 @RecluseWind 的提交)\n- 本地音乐支持 flac 格式 (感谢 @mikelxk 的提交)\n- 在软件中增加反馈链接 (感谢 @mikelxk 的提交)\n- 增加虾米歌单搜索，统一端口代码 (感谢 @RecluseWind 的提交)\n- 优化了歌单访问，增加本地缓存\n\n重构：\n\n- 更换所有加解密库到 forge (感谢 @Dumeng 的提交)\n- 去除对 jquery 库的依赖 (感谢 @Dumeng 的提交)\n- 更换音频播放库到 howler.js (感谢 @Dumeng 的提交)\n- 更换 http 请求库到 axios (感谢 @Dumeng 的提交)\n- 支持 eslint 的 github action 语法检查 (感谢 @Dumeng 的提交)\n\nbug 修复：\n\n- 修复 MediaSession 不支持时的报错问题 (感谢 @Jyuaan 的提交)\n- 修复咪咕歌单的 404 错误\n- 修复正在播放窗口点击空白处弹回的功能 (感谢 @Demeng 的提交)\n\n`2020-12-28`\n\n- 修复最大，最小，关闭按钮在桌面版失效的问题\n\n`2020-12-27`\n\n- 修复无法显示收藏歌单的 bug\n- 支持一次输入搜索所有平台（Beta）\n- 修复咪咕音乐歌单只显示前 20 首歌的 bug\n- 修复网易和酷狗音乐搜索错误未处理的 bug\n- 修复虾米音乐歌词解析错误导致无法显示的 bug\n- 根据 chrome web store 上架要求修改部分权限\n\n`2020-12-22`\n\n- 修复酷我音乐无法播放的问题\n- 修复我创建的歌单升级后无法播放的问题\n\n`2020-12-20`\n\n- 修复版权问题造成的播放中断和循环弹出提示通知的 bug\n- 修改歌曲封面为背景时歌词看不清的问题\n- 修复 qq 搜索的一个错误，优化接口返回时处理（感谢@RecluseWind 的提交）\n\n`2020-12-12`\n\n- 支持 QQ 音乐歌单搜索 (感谢@RecluseWind 的提交）\n- 修复网易云音乐无法打开手机分享的歌单链接的 bug (感谢@RecluseWind 的提交）\n- 修复咪咕音乐无法搜索的 bug\n\n`2020-10-28`\n\n- 增加本地音乐（仅限桌面版）\n\n`2020-10-27`\n\n- 增加歌单搜索功能（暂时只支持网易云）\n- 优化歌词显示\n- 修复 blili 歌手 API 错误，修复歌词时间轴格式不统一产生的错误 (感谢@RecluseWind 的提交)\n- 优化 UI，正在播放页增加翻译按钮\n\n`2020-10-26`\n\n- 增加歌词翻译功能 QQ 音乐和虾米音乐的支持（感谢@RecluseWind 的提交)\n- 更新了虾米音乐获取歌曲播放地址，获取歌单，搜索 API 的获取方式，增加可靠性 (感谢@RecluseWind 的提交)\n- 修复安装插件后 qq 音乐网页部分歌单无法打开的 bug\n\n`2020-10-18`\n\n- 增加歌词翻译功能，暂时只支持网易云音乐 (感谢@reserveword 的提交)\n- 修复 bilibili 音乐无法播放的 bug\n- 修复虾米播放页歌曲封面无法显示的 bug\n- 修复酷我音乐歌单无法打开的 bug\n\n`2020-09-12`\n\n- 修复网易歌单超过 1000 首时导入失败的 bug (感谢@YueShangGuan 的提交）\n- 支持显示歌曲封面作为正在播放背景 (感谢@YueShangGuan 的提交）\n\n`2020-08-24`\n\n- 修复虾米歌单歌曲只显示部分歌曲的 bug (感谢@RecluseWind 的提交)\n- 修复歌单图片和标题显示问题 (感谢@RecluseWind 的提交)\n- 支持桌面版点击链接打开系统默认浏览器\n\n`2020-08-04`\n\n- 增加正在播放窗口和播放列表弹窗的动画效果\n- 修复虾米艺人封面图片无法显示的问题 （感谢@RecluseWind 的提交）\n- 优化打开歌单功能，支持网易云排行榜单，艺人页面，专辑页面网址（感谢@whtiehack 的提交）\n- 优化专辑图片显示，避免图片被压缩 （感谢@RecluseWind 的提交）\n\n`2020-07-10`\n\n- 修复咪咕音乐无法播放的问题\n- 支持顶部搜索栏回车触发 （感谢@kangbb 的提交）\n- 支持歌单歌曲数显示，支持播放/暂停全局快捷键（桌面版）（感谢@x2009again 的提交）\n- 支持返回时回到滚动条历史位置（感谢@x2009again 参与完成）\n- 优化 firefox 滑动条，修改 qq 音乐图标网址，解决 firefox 上架 jquery 代码问题 （感谢@RecluseWind 的提交）\n\n`2020-06-29`\n\n- 支持播放失败时自动切换播放源(Beta)\n\n`2020-06-28`\n\n- 修复网易歌单仅显示 10 首歌曲的问题\n\n`2020-04-30`\n\n- 修复咪咕音质较差的问题\n\n`2020-04-27`\n\n- 增加收藏歌单功能，特别感谢 @zhenyiLiang\n- 修复咪咕音乐无法播放的 bug\n- 一些细节优化\n\n`2019-11-27`\n\n- 加入法语支持, 特别感谢 @Leoche\n\n`2019-09-07`\n\n- 修复 migu 无法播放的 bug\n\n`2019-08-09`\n\n- 增加深色主题\n\n`2019-07-03`\n\n- 修复咪咕音乐无法播放的 bug\n\n`2019-06-24`\n\n- 增加咪咕音乐\n- 修复网易音乐无法播放的 bug\n- 修复酷狗音乐无法播放的 bug\n\n`2019-06-23`\n\n- 修复无法连接到 github 的 bug\n\n`2019-05-26`\n\n- 修复酷狗音乐无法播放的 bug\n\n`2019-04-26`\n\n- 修复虾米音乐无法播放的 bug\n- 修复播放器未在页面底端显示的 bug\n\n`2019-03-03`\n\n- 修复删除单个歌曲导致歌单所有歌曲消失的 bug\n- 修复删除单个歌单导致所有歌单消失的 bug\n\n`2019-02-26`\n\n- 修复 qq 音乐歌单无法显示的 bug\n\n`2018-12-30`\n\n- 修复酷我音乐歌单缺失歌曲的问题\n- 自动检测客户端语言\n\n`2018-12-29`\n\n- 修复虾米音乐搜索失败的问题\n- 修复部分 QQ 音乐歌曲无法播放的问题\n- 修复使用插件时 QQ 官方网站无法使用的问题\n\n`2018-12-24`\n\n- 多语言支持，支持英文\n- 新添加到歌单的歌曲将出现在歌单头部\n- 修复版权通知占满屏幕的 bug\n\n`2018-12-22`\n\n- 全新版本 2.0 发布，更新界面(特别感谢@iparanoid 提供主题设计)\n- 升级 jquery 和 angular 版本\n\n`2018-12-21`\n\n- 修复虾米音乐歌单访问的问题\n- 修复网易云音乐歌单只有一首歌的问题\n- 修复 bilibili 滚动时加载重复歌单的问题\n- 修复酷狗部分音乐无法播放的问题\n- 修复 Github Gist 备份无法导入的问题\n- 升级 soundmanager2 库到最新版本\n\n`2018-12-05`\n\n- 完全修复虾米音乐歌单访问的问题\n\n`2018-08-25`\n\n- 修复虾米音乐无法播放的 bug\n\n`2018-06-15`\n\n- 增加酷我音乐的支持（特别感谢@WinterXMQ 的提交)\n\n`2018-06-10`\n\n- 修复酷狗音乐收藏歌单后可能显示空歌单的 bug\n\n`2018-06-10`\n\n- 修复虾米音乐无法显示歌词的 bug\n\n`2018-06-05`\n\n- 增加酷狗音乐的支持（感谢@WinterXMQ )\n\n`2018-05-30`\n\n- 修复 QQ 音乐无法播放的问题（感谢@noschoollee 提供修复方案）\n\n`2018-04-23`\n\n- 修复虾米音乐无法播放的问题\n\n`2018-02-18`\n\n- 修复无法创建歌单的 bug\n- 修复点击关闭歌单按钮后无法再打开歌单的 bug\n- 增加歌曲主页，点击封面可进入（特别感谢@iparanoid 提供歌曲页面 UI 设计）\n\n`2018-02-15`\n\n- 修复随机播放在播放列表播放结束后自动停止的问题，开启无限洗脑循环（感谢@sunjie21 的提交）\n- 增加将当前播放列表全部添加到歌单的功能 (感谢@sunjie21 的提交)\n- 修复标题播放状态不实时更新的 bug (感谢@sibojia 的提交)\n\n`2018-02-14`\n\n- 修复主页在加载更多数据时出现双重滚动条的 bug，并修改了滚动条样式（感谢@zhuzhuyule 的提交)\n- 修复打开歌单时，网易云音乐个人歌单地址无法解析的 bug（感谢@zhuzhuyule 的提交)\n\n`2017-12-26`\n\n- 增加同步歌单到 Github Gist 功能。(特别感谢@ConstLhq 提供创意和部分代码实现）\n\n`2017-12-20`\n\n- 增加搜索翻页功能，你可以看到更多的搜索结果了。(感谢@ConstLhq 的提交）\n- 增加合并歌单功能。可以快速的把其它你创建的歌曲合并到当前的歌单中了。(感谢@Dumeng 的提交）\n\n`2017-11-27`\n\n- 修复网易云音乐歌单只显示第一首歌的 Bug（感谢[@Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)提供接口实现）\n\n`2017-11-18`\n\n- 修复版权原因无法播放歌曲时自动暂停的问题\n\n`2017-11-17`\n\n- 在我的歌单页面增加“打开歌单”功能，可打开支持网页的歌单链接地址。这样就可以导入你喜欢的歌单了。\n- HTTP 请求头部的 Origin 字段设置为正常网址\n\n`2017-10-16`\n\n- 修复 QQ 音乐歌单翻页显示重复的问题(感谢@Moobusy 的提交)\n\n`2017-10-03`\n\n- 修复网易云音乐歌单无法显示的问题(感谢@Moobusy 的提交)\n\n`2017-09-14`\n\n- 修复 QQ 音乐无法播放的 bug\n\n`2016-05-27`\n\n- 增加快捷键功能（输入?查看快捷键设置）\n- 支持同步播放记录到 last.fm\n- 增加搜索 loading 时的图标(感谢@richdho 的提交）\n- 页面标题增加显示当前播放信息\n- 修复了在收藏对话框点击取消出现新建歌单的 bug\n- 重新组织代码文件夹结构\n\n`2016-05-21`\n\n- 增加歌单分页加载功能(感谢@wild-flame 的提交)\n- 修复关闭按钮随网页滚动的 bug\n- 修复点击暂停按钮会重置进度条和歌词的 bug\n- 修复点击歌单名称不跳转的 bug\n- 调整歌单水平位置居中\n\n`2016-05-14`\n\n- 增加 firefox 插件支持（感谢 fulesdle 的提交）\n\n`2016-05-13`\n\n- 增加我的歌单功能，可以收藏现有歌单，并创建自己的歌单\n- 点击 Listen 1 和图标可以回到首页\n- 标记了部分因版权无法播放的歌曲,增加版权提示\n- 重构了音乐平台代码，使用统一的接口规范\n- 重构了歌单接口，合并歌手，专辑和歌单接口\n- 修复了阿里云歌手链接点击错误的 bug\n\n`2016-05-08`\n\n- 增加歌词显示\n- 精选歌单：添加歌单到当前播放列表，可点击跳转到原始链接\n- 修复了搜索 qq 音乐时的乱码问题\n- 修复了循环播放网易歌曲一段时间后暂停的 bug\n- 修复了可能导致微信公众号无法登录的 bug\n- 优化性能，删除了不必要的事件消息触发\n\n`2016-05-02`\n\n- 增加音量控制\n\n## License\n\nMIT\n"
  },
  {
    "path": "README_EN.md",
    "content": "# Listen 1 (Chrome Extension) V2.33.0\n\n（Last Update June 17th, 2025)\n\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)\n\n## One for all free music in China\n\nWhen I found many songs are unavailable because copyright issue, I realized there's something I should do.\nMom never need to worry about I can't listen my favorite songs.\n\nSupported music platform:\n\n- Netease\n- QQ\n- Kugou\n- Kuwo\n- Bilibili\n- Migu\n- Qianqian (taihe)\n\nSearch songs, listen songs from multiple platforms, that's `Listen 1`.\n\nV2.9.0 New Feature: Auto choose source\n\nwhen music play source url is not available, auto choose source from other sources.\n\nMaking your own playlist is also supported.\n\n## How to change language ?\n\n1. Click Settings icon in right top of application\n2. Click `English` under `Language` or `语言`\n\n## Install (Chrome)\n\n1. download zip file from github and uncompress to local.\n\n2. open Extensions from chrome.\n\n3. Choose `Load unpacked`(Open Develop Mode first)，Click folder you just uncompressed, finish!\n\n## Install (Firefox)\n\n1. Visit Listen1 Firefox Page https://addons.mozilla.org/zh-CN/firefox/addon/listen-1/\n2. Click Add to Firefox button\n\n## Changelog\n\n`2025-06`\n\nFix bugs：\n\n- fix migu playing error\n- fix playing control problem (#1200) (#1201) （thanks @reserveword ）\n- fix electron remote to support more electron version （thanks @xihale ）\n- fix performance issue for new theme (#1216) （thanks @mikelxk ）\n- fix electron cookie send problem\n- fix spell error (thanks Nicholas Wilson)\n  Features：\n\n- migrate chrome extension manifest version to V3\n\n`2023-08 ~ 2024-04`\n\nFix bugs：\n\n- Migrate bilibili artist info api （thanks @wuhao-igno）\n- fix qq music\n- fix kugou music\n- fix kuwo music\n\n`2023-07`\n\nFix bugs：\n\n- kuwo search api error（thanks @yhsj0919）\n- console output error（thanks @caoxiemeihao）\n\n`2022-12 ~ 2023-03`\n\nFeatures：\n\n- Add language support for Brazilian portuguese (thanks @nailtonvital)\n- Add new quality 320kpbs for qq music (thanks @fecet)\n\nFix bugs:\n\n- fix kuwo music play url error (thanks @NickeyLin)\n- fix songlist export problem (thanks @@IcedWatermelonJuice)\n- fix icon state when toggle now playing page (thanks @@mnyon)\n- fix lastfm record error (thanks @@Anmizi)\n\n`2022-11`\n\nFeatures：\n\n- Search playlist for bilibili video (thanks @Wei-bin-Wu)\n- Optimize modern theme style (thanks @814959822)\n\nFix bugs:\n\n- Fix bilibili tracks play error\n- Fix local music play problem related to file types (thanks @mikelxk)\n- Fix image related problem when playing bilibili music (thanks @mikelxk)\n\n`2022-09`\n\nFeatures:\n\n- Add new themes: modern white, modern black（thanks @814959822）\n- Optimize new theme performance\n\nFix bugs:\n\n- fix bilibili search problem (thanks @mikelxk)\n- fix hover shaking problem when choose platform（thanks @814959822）\n- fix player bar empty when playlist contains less than five songs in modern theme\n\n`2022-08`\n\nFix bugs:\n\n- fix music can't play after idle for a while #902 （thanks @reserveword）\n- fix qq music search not working (thanks to lx-music-desktop for solution)\n- fix bilibili search response error\n\n`2022-07`\n\nFeatures:\n\n- add bilibili search page for audio in video search\n\nFix bugs:\n\n- fix kugou music play error\n- fix kugou hot playlist load more bug\n- fix qq singer page error\n- fix migu top list open error\n\n`2022-06`\n\nFeatures:\n\n- Double click to play in playlist and search result page（thanks @piz-ewing）\n- Support wav format local file in desktop version (thanks @mikelxk)\n\nFix bugs:\n\n- fix shuffle mode play duplicate music bug (thanks @piz-ewing)\n- fix music continue to play when clear now playing playlist (thanks @leca）\n- fix kugou music play fail\n\n`2022-02 ~ 2022-03`\n\nFeatures：\n\n- Add Korean language support（thanks @kkange）\n\nFix bugs：\n\n- fix kugou api play song error\n- fix qianqian music api error（thanks @mikelxk）\n\n`2021-08 ~ 2022-01`\n\nFix bugs：\n\n- fix music category line height （thanks @yinzhenyu-su）\n- fix bilibili play issue in firefox (thanks @ktmzcpl)\n- fix UI crash in electron environment\n\nOptimaze：\n\n- More fluent effect for current playing switching (thanks @mikelxk)\n\n`2021-07`\n\nFix Bugs：\n\n- disable image drag\n- add shortcuts description for zoom in/out\n- move window control panel to top right for windows users (thanks @mikelxk)\n- upgrade howler lib (thanks @mikelxk)\n- fix QQ search problem\n- fix media center progress bar control for chrome users\n- add local lrc file support when import local music (thanks @mikelxk)\n\n  `2021-04`\n\nFeatures:\n\n- QQ Login\n- Drag and drop to reorder songs in playlist, reorder playlist and quick add song to playlist\n- Search in playlist\n- Proxy setting (desktop version only)\n- Configure auto detect playable source list\n- Display latest version in setting page\n- Highest bitrate for netease music\n\nRefactor：\n\n- Change music platform resource API to class #553\n- remove angular dependency for github module #532 (thanks @Dumeng)\n- remove angular dependency for lastfm module #532 (thanks @Dumeng)\n- UX optimaze #537\n\nFix Bugs：\n\n- Fix migu resource api to use without login, add bitrate info #536 (thanks @RecluseWind)\n- Fix display error in firefox for migu hot rank #536 (thanks @RecluseWind)\n- Fix sometimes song keep waiting for 15 seconds before playing bug\n- Fix qq short link parse error\n- Fix toggle mute error\n- Fix GitHub logout error\n- Fix some kugou music without album play error\n- Fix two songs play in same time\n\n`2021-03`\n\nFeatures:\n\n- Add qianqian music platform (thanks @Dumeng)\n- Support playlist filters and top list in migu (thanks @RecluseWind)\n- Zoom in/out function for desktop version (thanks @mikelxk)\n- Support netease login, show my playlist and recommend playlist\n- Support migu login\n- Show bitrate and music platform in now playing page\n- deprecated xiami\n\nRefactor:\n\n- Replace angular module dependencies: translate，i18n, hotkeys，replace with js library (thanks @Dumeng)\n- Optimaze feather load performance (thanks @Dumeng)\n- Optimaze bitrate for qq and kugou platform, default high bitrate\n- Split app.js into files by controller\n- Optimaze copyright notice show\n- Change http to https for several links\n\nFix bugs:\n\n- Fix media control invalid because new es6 optional chain (thanks @mikelxk)\n- Fix volume control not working (thanks @mikelxk)\n- Fix scroll bar style in firefox (thanks @RecluseWind)\n- Fix kugou music cover url\n- Fix kugou music play url\n- Fix notification not shown bug\n- Fix delete songs in current playlist mess up playing bug\n\n`2021-02`\n\nFeatures:\n\n- Support playlist filters and top playlist （special thanks [lyswhut/lx-music-desktop](https://github.com/lyswhut/lx-music-desktop) ）\n- Add Traditional Chinese language (thanks @yujiangqaq)\n- Add chrome media panel function: prev/next track, back/forward (thanks @mikelxk）\n- New lyric floating window, support config font size, color and background transparency\n\nRefactor：\n\n- Build MediaService module，remove dependency on angularjs（special thanks @Dumeng）\n- Add prettier config file, add pre-commit style check（thanks @mikelxk）\n- Fix history code style problems（thanks @mikelxk）\n\nFix bugs：\n\n- Fix Github API （thanks @NoDocCat 和 @Dumeng）\n- Fix svg animation performance issue （thanks @Dumeng）\n- Fix xiami API（thanks @RecluseWind）\n- Fix import local music error for mac desktop version（thanks @virgil1996）\n- Fix kuwo search error\n\n`2021-01`\n\nFeatures：\n\n- support play music background (thanks @Dumeng)\n- optimaze kugo related code (thanks @RecluseWind)\n- optimaze migu related code (thanks @RecluseWind)\n- support flac for local music (thanks @mikelxk)\n- add feedback link (thanks @mikelxk)\n- optimaze xiami music, add playlist search (thanks @RecluseWind)\n- optimaze cache for playlist\n\nRefactor：\n\n- replace encrypt lib to forge (thanks @Dumeng)\n- remove jquery (thanks @Dumeng)\n- replace ngsoundmanager2 to howler.js (thanks @Dumeng)\n- replace angular http to axios (thanks @Dumeng)\n- support eslint check in github action (thanks @Dumeng)\n\nFix bugs：\n\n- fix MediaSession error when not supported (thanks @Jyuaan)\n- fix migu playlist 404 link\n- fix current playing music list modal (thanks @Demeng)\n\n`2020-12-28`\n\n- fix bug for desktop: max,min,close button not available\n\n`2020-12-27`\n\n- fix bug: can't play favorite playlist\n- feature: search all music (beta)\n- fix bug: migu playlist shows first 20 tracks\n- fix bug: netease/kugou search error not handle\n- fix bug: xiami lyric parse error\n- change manitest permission config to pass chrome web store review\n\n`2020-12-22`\n\n- fix bug: kuwo music can't be played\n- fix bug: after upgrade v2.17.2, my playlist can't be played\n\n`2020-12-20`\n\n- fix play interrupted by copyright notice bug, infinite notice popup bug\n- change style for now playing page when using album cover as background\n- fix minor bug for qq search and optimaze api handler（thanks @RecluseWind）\n\n`2020-12-12`\n\n- support search songlist for qq music (thanks @RecluseWind）\n- fix bug: netease songlist shared by mobile open error (thanks @RecluseWind）\n- fix bug: migu search song error\n\n`2020-10-28`\n\n- add local music (desktop version only)\n\n`2020-10-27`\n\n- support search playlist (only for netease by now)\n- optimaze lyric display\n- fix bilibili artist api, fix lyric time tag format parse error (thanks @RecluseWind)\n- optimaze UI, add translate button in now playing page\n\n`2020-10-26`\n\n- add lyric translation support for qq music, xiami music (thanks @RecluseWind)\n- update xiami api including get playlist, search, play music (thanks @RecluseWind)\n- fix bug some playlist not response in qq music website after installed extension\n\n`2020-10-18`\n\n- add lyric translation, now for netease music only (thanks @reserveword)\n- fix bilibili play fail bug\n- fix xiami now playing page music cover missing bug\n- fix kuwo music can't open bug\n\n`2020-09-12`\n\n- fix netease songlist contains more than 1k tracks import error (thanks @YueShangGuan）\n- support album cover as nowplaying background (thanks @YueShangGuan）\n\n`2020-08-24`\n\n- fix xiami songlist only shows part of songs bug (thanks @RecluseWind)\n- fix songlist cover and title display bug (thanks @RecluseWind)\n- support open url using system default browser for desktop version\n\n`2020-08-04`\n\n- add animation for now playing and current playlist window\n- fix xiami cover image not loaded bug (thanks @RecluseWind)\n- optimaze open songlist url, support netease toplist, artist, album (thanks @whtiehack)\n- optimaze cover image display, avoid resize (thanks @RecluseWind)\n\n`2020-07-10`\n\n- fix migu play fail bug\n- support press enter key to search in search bar thanks @kangbb）\n- support playlist song count show, support play/pause shortcut, desktop only（thanks @x2009again）\n- support restore scrollbar offset when go back（thanks @x2009again for discuss solution）\n- optimaze firefox scorlling bar, modify source image url for qq music, fix firefox jquery lib md5 error（thanks @RecluseWind）\n\n`2020-06-29`\n\n- support auto choose source when play fail\n\n`2020-06-28`\n\n- fix netease music only show 10 tracks bug\n\n`2020-04-30`\n\n- fix migu poor music quality bug\n\n`2020-04-27`\n\n- support adding playlist to favorite, special thanks to @zhenyiLiang\n- fix migu music\n- some minor optimaze\n\n`2019-11-27`\n\n- add frech language, special thanks to @Leoche\n\n`2019-09-07`\n\n- fix migu\n\n`2019-08-09`\n\n- add dark theme\n\n`2019-07-03`\n\n- fix migu play error\n\n`2019-06-24`\n\n- add migu music\n- fix kugou play bug\n- fix netease play bug\n\n`2019-06-23`\n\n- fix connect to github.com error\n\n`2019-05-26`\n\n- fix kugou music can't play bug\n\n`2019-04-26`\n\n- fix xiami music can't play bug\n- fix footer player out of page bug\n\n`2019-03-03`\n\n- fix delete single playlist destroy all playlists bug\n\n`2019-02-26`\n\n- fix qq music songlist not shown bug\n\n`2018-12-30`\n\n- fix songs missing in kuwo playlist\n- auto detect language\n\n`2018-12-29`\n\n- fix fail on xiami search\n- fix some qq songs fail to play\n- fix qq music web visit problem after extension installed\n\n`2018-12-24`\n\n- i18n support, support English language.\n- new song will now add to top of playlist\n- copyright notification will not mess up the screen\n\n`2018-12-22`\n\n- Version 2.0 released. New UI(Special Thanks to @iparanoid)\n- Upgrade jquery, Angular\n\n`2018-12-21`\n\n- Fix xiami playlist bug\n- Fix netease playlist only shows one song bug\n- Fix bilibili first load duplicate playlists\n- Fix can't play some kugou songs\n- Fix github gist backup recover bug\n- Upgrade soundmanager2\n\n## License\n\nMIT\n"
  },
  {
    "path": "css/common.css",
    "content": "html,\nbody {\n  margin: 0;\n  padding: 0;\n  font-size: var(--text-default-size);\n  color: var(--text-default-color);\n  font-family: system-ui, 'PingFang SC', STHeiti, sans-serif;\n}\n\na {\n  cursor: pointer;\n}\n\n.wrap {\n  /* https://stackoverflow.com/questions/28897089/z-index-on-borders */\n  outline: solid 1px var(--windows-border-color);\n  box-sizing: border-box;\n}\n\n/* remove focus highlight */\ninput:focus,\nselect:focus,\ntextarea:focus,\nbutton:focus {\n  outline: none;\n}\n\nul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\ninput,\nsvg,\n.icon {\n  -webkit-app-region: no-drag;\n}\n\nbutton {\n  background-color: var(--button-background-color);\n  color: var(--text-default-color);\n  cursor: pointer;\n  border: solid 1px var(--button-background-color);\n  border-radius: var(--default-border-radius);\n  padding: 5px;\n  min-width: 80px;\n  min-height: 32px;\n}\nbutton:hover {\n  background-color: var(--button-hover-background-color);\n}\nimg {\n  -webkit-user-drag: none;\n}\n.l1-button {\n  background-color: var(--button-background-color);\n  color: var(--text-default-color);\n  border-radius: var(--default-border-radius);\n  padding: 5px;\n  margin-right: 4px;\n  color: var(--text-default-color);\n  cursor: pointer;\n  display: inline-block;\n}\n.l1-button:hover {\n  background: var(--button-hover-background-color);\n}\nsvg {\n  width: 24px;\n  height: 24px;\n  stroke: currentColor;\n  stroke-width: 1;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  fill: none;\n  cursor: pointer;\n  /*    stroke: var(--icon-default-color);*/\n}\n\n/* svg:hover {\n  fill: var(--icon-highlight-color);\n  stroke: var(--icon-highlight-color);\n} */\n\n.icon {\n  /* default icon settings */\n  font-size: 16px;\n  cursor: pointer;\n}\n\n/* tools utils */\n.flex-scroll-wrapper {\n  flex: 1;\n  height: 100px;\n  overflow-y: scroll;\n  scrollbar-width: thin;\n  scrollbar-color: var(--scroll-color) var(--content-background-color);\n}\n\n/* scroll bar style */\n::-webkit-scrollbar {\n  width: 14px;\n  height: 18px;\n  background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n  height: 49px;\n  border: 5px solid rgba(0, 0, 0, 0);\n  background-clip: padding-box;\n  border-radius: 7px;\n  -webkit-border-radius: 7px;\n  background-color: var(--scroll-color);\n  /*rgba(151, 151, 151, 0.4);*/\n\n  /*    -webkit-box-shadow: inset -1px -1px 0px rgba(0, 0, 0, 0.05), inset 1px 1px 0px rgba(0, 0, 0, 0.05);*/\n}\n\n::-webkit-scrollbar-button {\n  width: 0;\n  height: 0;\n  display: none;\n}\n\n::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n/* main framework start */\n.wrap {\n  display: flex;\n  height: 100vh;\n  flex-direction: column;\n  margin: auto;\n}\n\n/* split screen to up/down 2 parts */\n.main {\n  flex: 1;\n  display: flex;\n  overflow: hidden;\n}\n\n.footer {\n  background: var(--foot-background-color);\n  height: 60px;\n  border-top: solid 1px var(--line-default-color);\n  display: flex;\n  position: relative;\n  z-index: 99;\n}\n\n/* split main to left/right 2 parts */\n.main .sidebar {\n  flex: 0 0 200px;\n  display: flex;\n  flex-direction: column;\n  background: var(--sidebar-background-color);\n}\n\n.main .content {\n  background: var(--content-background-color);\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n}\n\n/* split content to up/down 2 parts */\n.main .content .navigation {\n  height: 46px;\n  flex: 0 0 46px;\n  border-bottom: solid 1px var(--line-default-color);\n  display: flex;\n  align-items: center;\n  -webkit-app-region: drag;\n}\n\n.main .content .browser {\n  flex: 1;\n}\n\n/* main framework end */\n\n/*****************************************************************/\n\n/* main sidebar start */\n.sidebar .menu-control {\n  height: 43px;\n  width: 125px;\n  -webkit-app-region: drag;\n}\n.sidebar .menu-title {\n  height: 28px;\n  line-height: 28px;\n  margin: 0 12px 4px 12px;\n  color: var(--link-default-color);\n  padding-left: 10px;\n  display: flex;\n  align-items: center;\n  font-size: 12px;\n}\n.sidebar .menu-title .title {\n  flex: 1;\n}\n.sidebar .menu-title svg {\n  flex: 0 0 18px;\n}\n\n.sidebar ul li {\n  cursor: pointer;\n  padding-left: 10px;\n  border-top: solid 2px transparent;\n  border-bottom: solid 2px transparent;\n  margin-bottom: -2px;\n}\n.sidebar ul li .sidebar-block {\n  display: flex;\n  align-items: center;\n  line-height: 28px;\n  padding-left: 12px;\n  margin: 3px 0;\n  color: var(--text-default-color);\n\n  border-radius: var(--default-border-radius);\n}\n\n.sidebar svg {\n  width: 18px;\n  height: 18px;\n}\n\n.sidebar ul li a {\n  margin-left: 10px;\n  width: 125px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.sidebar ul li:hover .sidebar-block {\n  background: var(--sidebar-hover-background-color);\n  color: var(--sidebar-hover-text-color);\n}\n\n.sidebar ul li.active .sidebar-block,\n.sidebar ul li.active:hover .sidebar-block {\n  background: var(--sidebar-highlight-background-color);\n  color: var(--sidebar-highlight-text-color);\n}\n.sidebar ul li.dragover .sidebar-block {\n  background: var(--sidebar-highlight-background-color);\n  color: var(--sidebar-highlight-text-color);\n}\n/*\navoid hover effect trigger dragleave event\nhttps://stackoverflow.com/questions/19889615/can-an-angular-directive-pass-arguments-to-functions-in-expressions-specified-in\n*/\n.sidebar ul li * {\n  pointer-events: none;\n}\n/* main sidebar end */\n\n/* widget navigation start */\n.navigation svg {\n  width: 18px;\n  height: 18px;\n  color: var(--icon-default-color);\n}\n.navigation .icon svg {\n  color: var(--text-default-color);\n}\n.navigation .backfront {\n  flex: 0 0 45px;\n  line-height: 46px;\n  vertical-align: middle;\n  padding: 0 13px;\n}\n\n.navigation .search {\n  flex: 1;\n}\n\n.navigation .settings {\n  flex: 0 0 32px;\n}\n\n.navigation .icon {\n  color: var(--text-default-color);\n  opacity: 0.5;\n}\n\n.navigation .icon:hover {\n  opacity: 1;\n}\n\n.navigation .backfront .icon {\n  display: inline-block;\n  vertical-align: middle;\n  margin-bottom: 4px;\n}\n\n.navigation .backfront .icon:nth-of-type(1) {\n  margin-right: 8px;\n}\n\n.navigation .search-input {\n  width: 270px;\n  height: 23px;\n  background: var(--search-input-background-color);\n  border-style: none;\n  border-radius: var(--default-border-radius);\n  padding-left: 10px;\n  font-size: 12px;\n  color: var(--text-default-color);\n}\n\n.navigation .window-control {\n  flex: 0 0 105px;\n  border-left: solid 1px var(--window-control-border-color);\n  margin-left: 15px;\n}\n\n.navigation .window-control svg {\n  margin-left: 8px;\n}\n\n.navigation .window-control svg:first-of-type {\n  margin-left: 15px;\n}\n\n/* navigation end */\n\n/* page hot-playlist start */\n.page-hot-playlist {\n  max-width: 850px;\n  margin: 0 auto;\n}\n\n.playlist-covers {\n  margin: 0;\n  padding: 0 13px;\n  display: flex;\n  flex-flow: row wrap;\n  position: relative;\n}\n\n.playlist-covers li {\n  flex: 0 1 calc(20% - 26px);\n  min-height: 156px;\n  color: var(--text-default-color);\n  margin: 0 13px;\n}\n\n.playlist-covers .u-cover {\n  display: flex;\n  position: relative;\n}\n\n.playlist-covers .u-cover img {\n  height: 136px;\n  min-width: 136px;\n  max-width: 100%;\n  object-fit: cover;\n  margin: auto;\n  border: solid 1px var(--line-default-color);\n  margin-bottom: 2px;\n  cursor: pointer;\n}\n\n.playlist-covers .u-cover .bottom {\n  position: absolute;\n  right: 5px;\n  bottom: 10px;\n  height: 30px;\n  width: 30px;\n  cursor: pointer;\n  opacity: 0;\n  transition: opacity 0.2s linear;\n}\n\n.playlist-covers .u-cover:hover .bottom {\n  opacity: 1;\n}\n\n.playlist-covers .u-cover .bottom svg {\n  height: 30px;\n  width: 30px;\n  fill: rgba(200, 200, 200, 0.5);\n  stroke-width: 1;\n  stroke: #ffffff;\n}\n\n.playlist-covers .u-cover .bottom svg:hover {\n  fill: rgba(100, 100, 100, 0.5);\n}\n\n.playlist-covers .desc {\n  cursor: pointer;\n}\n\n.playlist-covers .desc .title {\n  display: flex;\n  min-height: 32px;\n  margin: 0 0 5px;\n}\n/* page hot-playlist end */\n\n/* page playlist-detail start */\n.page .playlist-detail {\n  padding-bottom: 37px;\n}\n\n.page .playlist-detail .detail-head {\n  display: flex;\n}\n\n.page .playlist-detail .detail-head img {\n  height: 150px;\n}\n\n.page .playlist-detail .detail-head .detail-head-cover {\n  flex: 0 0 150px;\n  padding: 26px 26px 8px 26px;\n}\n\n.page .playlist-detail .detail-head .detail-head-title {\n  flex: 1;\n}\n\n.playlist-button-list {\n  display: flex;\n  flex-flow: row wrap;\n}\n\n.playlist-button-list .playlist-button {\n  height: 26px;\n  border: solid 1px var(--button-border-color);\n  cursor: pointer;\n  border-radius: 2px;\n  display: flex;\n  margin: 0 20px 20px 0;\n}\n\n.playlist-button-list .playlist-button.playadd-button {\n  flex: 0 0 136px;\n}\n\n.playlist-button-list .playlist-button .play-list {\n  flex: 1;\n  padding: 0 18px;\n  display: flex;\n  align-items: center;\n}\n.playlist-button-list .playlist-button .play-list svg {\n  margin-right: 4px;\n}\n\n.playlist-button-list .playlist-button.playadd-button .play-list svg {\n  width: 14px;\n  height: 14px;\n  flex: 0 0 14px;\n  margin-right: 4px;\n  stroke: var(--important-color);\n  fill: var(--important-color);\n}\n.playlist-button-list .playlist-button .play-list .icon {\n  margin-right: 8px;\n}\n.playlist-button-list .playlist-button.playadd-button .play-list .icon {\n  flex: 0 0 14px;\n  margin-right: 4px;\n  color: var(--important-color);\n}\n\n.playlist-button-list .playlist-button.playadd-button .add-list {\n  flex: 0 0 26px;\n  height: 26px;\n  width: 26px;\n  border-left: solid 1px var(--button-border-color);\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.playlist-button-list .playlist-button.edit-button .play-list.favorited {\n  color: var(--text-default-color);\n}\n.playlist-button-list .playlist-button.edit-button .play-list.notfavorite {\n  color: var(--text-default-color);\n}\n\n.playlist-button-list .playlist-button .play-list:hover,\n.playlist-button-list .playlist-button.playadd-button .add-list:hover {\n  background: var(--button-hover-background-color);\n}\n.playlist-button-list .playlist-button.playadd-button .add-list svg {\n  width: 14px;\n  height: 14px;\n}\n\n.playlist-button-list .playlist-button.clone-button,\n.playlist-button-list .playlist-button.edit-button,\n.playlist-button-list .playlist-button.fav-button {\n  flex: 0 0 auto;\n}\n\n.playlist-button-list .playlist-button.clone-button .play-list svg,\n.playlist-button-list .playlist-button.edit-button .play-list svg,\n.playlist-button-list .playlist-button.fav-button .play-list svg {\n  width: 16px;\n  height: 16px;\n  flex: 0 0 16px;\n  margin-right: 8px;\n  stroke: rgb(102, 102, 102);\n}\n\n.playlist-button-list .playlist-button.fav-button .play-list.favorited svg {\n  fill: rgb(102, 102, 102);\n}\n\n.page .playlist-detail .detail-head .detail-head-title h2 {\n  font-size: var(--h2-title-font-size);\n}\n/* page playlist detail end */\n\n/* page song detail start */\n.page .songdetail-wrapper {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 60px;\n  background: var(--now-playing-page-background-color);\n  overflow: hidden;\n  border: solid 1px var(--windows-border-color);\n  -webkit-app-region: no-drag;\n  transition: all 0.3s;\n}\n\n.page .songdetail-wrapper .draggable-zone {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  -webkit-app-region: drag;\n  height: 80px;\n}\n\n.page .songdetail-wrapper.slidedown .draggable-zone {\n  display: none;\n  -webkit-app-region: no-drag;\n}\n\n.page .songdetail-wrapper .translate-switch {\n  border: solid 1px;\n  width: 20px;\n  height: 20px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: absolute;\n  bottom: 30px;\n  right: 30px;\n  color: #888888;\n  cursor: pointer;\n  -webkit-app-region: no-drag;\n}\n.page .songdetail-wrapper .translate-switch:hover {\n  color: var(--text-default-color);\n}\n.page .songdetail-wrapper .translate-switch.selected {\n  color: var(--text-default-color);\n}\n.page .songdetail-wrapper.slidedown {\n  top: calc(100% - 60px);\n}\n\n.page .songdetail-wrapper .close {\n  position: absolute;\n  top: 24px;\n  left: 24px;\n  height: 24px;\n  width: 24px;\n  cursor: pointer;\n  -webkit-app-region: no-drag;\n}\n.page .songdetail-wrapper .close.mac {\n  top: 44px;\n}\n\n.page .songdetail-wrapper .window-control {\n  position: absolute;\n  top: 24px;\n  right: 24px;\n  height: 24px;\n  cursor: pointer;\n  -webkit-app-region: no-drag;\n  z-index: 99;\n}\n\n.page .songdetail-wrapper .window-control svg {\n  margin-left: 8px;\n  stroke: var(--now-playing-close-icon-color);\n}\n\n.page .songdetail-wrapper .close svg {\n  stroke: var(--now-playing-close-icon-color);\n}\n\n.page .bg {\n  opacity: 0.5;\n  height: 100%;\n  text-align: center;\n  line-height: 100%;\n  float: left;\n  width: 100%;\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: cover;\n  filter: blur(10px) brightness(0.6);\n  transition: background ease-in-out 1.5s;\n}\n\n.page .playsong-detail {\n  position: absolute;\n  left: 10px;\n  right: 10px;\n  max-width: 770px;\n  margin: 0 auto;\n  display: flex;\n  height: 100%;\n}\n\n.page .playsong-detail .detail-head {\n  flex: 0 0 350px;\n  overflow: hidden;\n}\n\n.page .playsong-detail .detail-head .detail-head-cover {\n  width: 250px;\n  height: 250px;\n  margin-top: 110px;\n}\n\n.page .playsong-detail .detail-head img {\n  width: 250px;\n  height: 250px;\n  object-fit: cover;\n}\n\n.page .playsong-detail .detail-songinfo {\n  flex: 1;\n  margin-top: 80px;\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n  -webkit-app-region: no-drag;\n}\n.page .playsong-detail .detail-songinfo .title {\n  display: flex;\n  align-items: center;\n}\n.page .playsong-detail .detail-songinfo .title h2 {\n  font-size: var(--h2-title-font-size);\n  font-weight: 400;\n}\n.page .playsong-detail .detail-songinfo .title .badge {\n  font-size: var(--badge-font-size);\n  color: var(--badge-font-color);\n  border: solid 1px var(--badge-border-color);\n  border-radius: 3px;\n  margin-left: 5px;\n  padding-left: 4px;\n  padding-right: 4px;\n  margin-top: 4px;\n  box-sizing: border-box;\n  height: 20px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  white-space: nowrap;\n}\n.page .playsong-detail .detail-songinfo .title .badge.platform {\n  padding-top: 1px;\n}\n.page .playsong-detail .detail-songinfo .title .badge:first-of-type {\n  margin-left: 15px;\n}\n.page .playsong-detail .detail-songinfo .info {\n  border-bottom: solid 1px var(--line-default-color);\n  padding-bottom: 6px;\n  flex: 0 0 20px;\n  display: flex;\n}\n.page .playsong-detail .detail-songinfo .info a {\n  cursor: pointer;\n}\n.page .playsong-detail .detail-songinfo .info .singer {\n  flex: 1;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.page .playsong-detail .detail-songinfo .info .album {\n  flex: 2;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.page .playsong-detail .detail-songinfo .info span {\n  color: var(--lyric-default-color);\n}\n.page .coverbg .playsong-detail .detail-songinfo .info span {\n  color: var(--lyric-on-cover-color);\n}\n.page .playsong-detail .detail-songinfo .lyric {\n  position: relative;\n  flex: 0 0 380px;\n  overflow-y: scroll;\n  color: var(--lyric-default-color);\n  scrollbar-width: thin;\n  scrollbar-color: var(--scroll-color) transparent;\n  font-size: var(--lyric-font-size);\n}\n.page .coverbg .playsong-detail .detail-songinfo .lyric {\n  color: var(--lyric-on-cover-color);\n}\n.page .playsong-detail .detail-songinfo .lyric p {\n  margin: var(--lyric-line-margin) 0 0 0;\n}\n.page .playsong-detail .detail-songinfo .lyric p.translate {\n  margin: 5px 0 0 0;\n}\n.page .playsong-detail .detail-songinfo .lyric p.hide {\n  display: none;\n}\n.page .playsong-detail .detail-songinfo .lyric p.highlight {\n  color: var(--lyric-important-color);\n}\n.page .coverbg .playsong-detail .detail-songinfo .lyric p.highlight {\n  color: var(--lyric-important-on-cover-color);\n}\n\nul.detail-songlist {\n  padding: 0 25px;\n  position: relative;\n}\n\nul.detail-songlist .playlist-search {\n  position: absolute;\n  right: 0;\n  top: -30px;\n}\nul.detail-songlist .playlist-search .playlist-search-icon {\n  width: 14px;\n  position: absolute;\n  left: 7px;\n  top: 1px;\n}\nul.detail-songlist .playlist-search .playlist-clear-icon {\n  width: 14px;\n  position: absolute;\n  left: 158px;\n}\nul.detail-songlist .playlist-search .playlist-search-input {\n  margin-right: 28px;\n  margin-bottom: 10px;\n  border: none;\n  height: 24px;\n  border-radius: 12px;\n  padding: 0 30px;\n  background: var(--content-background-color);\n  color: #bbbbbb;\n  width: 120px;\n}\nul.detail-songlist .playlist-search .playlist-search-input:hover {\n  background-color: var(--songlist-odd-background-color);\n}\nul.detail-songlist .playlist-search .playlist-search-input::placeholder {\n  color: #bbbbbb;\n}\n\nul.detail-songlist li {\n  /* https://stackoverflow.com/questions/4157005/css-positioning-z-index-negative-margins */\n  position: relative;\n  display: flex;\n  border-top: solid 2px var(--songlist-border-color);\n  border-bottom: solid 2px var(--songlist-border-color);\n  height: 37px;\n  align-items: center;\n  padding: 0 20px;\n  font-size: 14px;\n  margin-bottom: -2px;\n}\n\nul.detail-songlist li.playlist-result {\n  height: 80px;\n  padding: 0 10px;\n}\nul.detail-songlist li.odd {\n  background-color: var(--songlist-odd-background-color);\n}\n\nul.detail-songlist li:hover,\nul.detail-songlist li.odd:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n\nul.detail-songlist li a {\n  cursor: pointer;\n}\nul.detail-songlist li a.disabled {\n  color: var(--disable-song-title-color);\n}\nul.detail-songlist li a span.source {\n  border: solid 1px #ccc;\n  border-radius: 4px;\n  margin-right: 10px;\n  display: inline-block;\n  padding: 0 4px;\n  color: #ccc;\n  font-size: 12px;\n  width: 24px;\n  text-align: center;\n}\nul.detail-songlist li a span.source.playlist {\n  margin-left: 10px;\n  margin-right: 0;\n}\nul.detail-songlist li.head {\n  height: 28px;\n  color: var(--text-disable-color);\n  border-top: none;\n  padding-bottom: 2px;\n}\nul.detail-songlist li.head:hover {\n  background-color: transparent;\n}\n\nul.detail-songlist li .title {\n  flex: 2;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  line-height: 17px;\n  max-height: 38px;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n}\nul.detail-songlist li.playlist-result .title {\n  max-height: 80px;\n}\n\nul.detail-songlist li.playlist-result .title a {\n  display: flex;\n  align-items: center;\n}\n\nul.detail-songlist li.playlist-result .title img {\n  height: 60px;\n  width: 60px;\n  display: block;\n  margin-right: 10px;\n}\n\nul.detail-songlist li .artist {\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  line-height: 17px;\n  max-height: 38px;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n}\n\nul.detail-songlist li .album {\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  line-height: 17px;\n  max-height: 38px;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n}\n\nul.detail-songlist li .tools {\n  flex: 0 0 110px;\n  display: flex;\n  align-items: center;\n}\nul.detail-songlist li .tools .icon {\n  height: 16px;\n  width: 16px;\n  color: #9d9d9d;\n  margin-top: 2px;\n  margin-right: 10px;\n}\n/* page song detail end */\n\n/* page login start */\n.page .login {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  min-height: calc(100vh - 192px);\n}\n.page .login .login-logo {\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n}\n.page .login .login-logo img {\n  height: 64px;\n  margin: 20px;\n}\n.page .login .login-title {\n  font-size: 18px;\n  margin-bottom: 10px;\n}\n.page .login .login-form .login-form_field {\n  display: flex;\n  align-items: center;\n  height: 40px;\n  margin: 24px;\n  width: 270px;\n  border: solid 1px var(--button-background-color);\n}\n.page .login .login-form .login-form_field input {\n  background: var(--content-background-color);\n  color: var(--text-default-color);\n}\n.page .login .login-form .login-form_field input.login-form_field_countrycode {\n  flex: 0 0 40px;\n  width: 40px;\n}\n.page .login .login-form .login-form_field svg {\n  margin-left: 12px;\n  margin-right: 12px;\n  color: var(--icon-default-color);\n  width: 18px;\n  height: 18px;\n}\n.page .login .login-form .login-form_field input {\n  border: none;\n  flex: 1;\n  font-size: 16px;\n}\n.page .login .login-submit_button {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 14px;\n  margin-top: 24px;\n  padding: 8px;\n  width: 270px;\n  cursor: pointer;\n  border: solid 1px var(--button-border-color);\n}\n.page .login .login-switcher {\n  margin-top: 24px;\n  cursor: pointer;\n}\n.page .login .login-notice {\n  width: 270px;\n  border-top: 1px solid var(--button-border-color);\n  margin-top: 30px;\n  padding-top: 12px;\n  font-size: 12px;\n  color: var(--text-subtitle-color);\n}\n.page .login .usercard {\n  display: flex;\n  align-items: center;\n  width: 400px;\n  border: solid 1px var(--button-border-color);\n  margin-bottom: 20px;\n}\n.page .login .usercard img {\n  width: 60px;\n  height: 60px;\n  margin: 10px;\n}\n.page .login .usercard .usercard-title {\n  flex: 1;\n  height: 50px;\n  font-size: 16px;\n  font-weight: 700;\n}\n.page .login .usercard .usercard-title .usercard-info {\n  color: var(--link-inactive-color);\n  font-size: 12px;\n}\n.page .login .usercard button {\n  margin: 10px;\n}\n/* page login end */\n\n/* page setting start */\n.page .settings-title {\n  border-bottom: solid 1px var(--line-default-color);\n  padding-bottom: 10px;\n  max-width: 800px;\n  margin: 0 25px;\n  font-size: 17px;\n  margin-bottom: 10px;\n}\n.page .settings-title:first-of-type {\n  margin-top: 20px;\n}\n.page .settings-content {\n  margin: 0 25px 25px 25px;\n}\n.page .settings-content label.upload-button,\n.page .settings-content .language-button {\n  padding: 5px;\n  background: var(--button-background-color);\n  margin-right: 4px;\n  color: var(--text-default-color);\n  cursor: pointer;\n}\n\n.page .settings-content label.upload-button:hover,\n.page .settings-content .language-button:hover {\n  background: var(--button-hover-background-color);\n}\n.page .settings-content .shortcut {\n  display: flex;\n  margin-top: 10px;\n}\n.page .settings-content .shortcut svg {\n  width: 18px;\n  height: 18px;\n  margin-right: 10px;\n}\n.page .searchbox .search-pagination {\n  text-align: center;\n  padding: 15px;\n}\n.page .settings-content .shortcut_table .shortcut_table-header,\n.page .settings-content .shortcut_table .shortcut_table-line {\n  display: flex;\n  color: var(--text-default-color);\n  box-sizing: border-box;\n  align-items: center;\n  height: 40px;\n}\n.page .settings-content .shortcut_table .shortcut_table-header {\n  color: var(--link-default-color);\n  height: 30px;\n}\n.page .settings-content .shortcut_table .shortcut_table-function {\n  flex: 0 140px;\n  padding: 0 10px;\n  box-sizing: border-box;\n}\n.page .settings-content .shortcut_table .shortcut_table-key {\n  flex: 0 200px;\n  margin-right: 20px;\n  box-sizing: border-box;\n}\n.page .settings-content .shortcut_table .shortcut_table-globalkey {\n  flex: 0 240px;\n  box-sizing: border-box;\n}\n.page\n  .settings-content\n  .shortcut_table\n  .shortcut_table-line\n  .shortcut_table-key {\n  border: solid 1px var(--button-border-color);\n  border-radius: 5px;\n  padding: 0 10px;\n  height: 30px;\n  display: flex;\n  align-items: center;\n}\n.page\n  .settings-content\n  .shortcut_table\n  .shortcut_table-line\n  .shortcut_table-globalkey {\n  border: solid 1px var(--button-border-color);\n  border-radius: 5px;\n  height: 30px;\n  padding: 0 10px;\n  display: flex;\n  align-items: center;\n  box-sizing: border-box;\n}\n\n.page .settings-content .custom-proxy {\n  margin-top: 10px;\n}\n.page .settings-content .custom-proxy .rule-input {\n  margin-top: 8px;\n}\n.page .settings-content .custom-proxy input {\n  margin-right: 15px;\n  height: 24px;\n  width: 200px;\n}\n.page .settings-content .search-description {\n  margin: 10px 0 5px 0;\n}\n.page .settings-content .search-source-list {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n  line-height: 30px;\n}\n.page .settings-content .search-source-list .search-source {\n  display: flex;\n  align-items: center;\n  width: 130px;\n}\n.page .settings-content .search-source-list .search-source svg {\n  width: 18px;\n  height: 18px;\n  margin-right: 4px;\n}\n/* page setting end */\n\n.loading_bottom {\n  display: block;\n  width: 40px;\n  margin: 0 auto;\n}\n\nsvg.searchspinner {\n  width: 20px;\n  height: 20px;\n  vertical-align: top;\n  margin-left: 15px;\n}\n/* footer start */\n\n.footer {\n  background: var(--foot-background-color);\n  height: 60px;\n  border-top: solid 1px var(--foot-border-color);\n  display: flex;\n  position: relative;\n}\n\n.footer .left-control {\n  flex: 0 0 300px;\n  display: flex;\n  align-items: center;\n}\n\n.footer .left-control .icon {\n  font-size: 22px;\n  color: var(--player-left-icon-color);\n  margin: 0 13px;\n}\n\n.footer .left-control .icon.play {\n  margin-right: 10px;\n}\n\n.footer .left-control .icon:first-of-type {\n  margin-left: 42px;\n}\n\n.footer .left-control .icon.play {\n  color: var(--player-icon-color);\n}\n.footer .left-control .icon.play:hover {\n  color: var(--player-icon-hover-color);\n}\n\n.footer .main-info {\n  flex: 1;\n  background: var(--footer-main-background-color);\n  display: flex;\n  overflow: hidden;\n  z-index: 1;\n}\n\n.footer .main-info .logo-banner {\n  text-align: center;\n  flex: 1;\n  display: flex;\n  align-items: center;\n}\n\n.footer .main-info .logo-banner svg.logo {\n  height: 48px;\n  width: 48px;\n  fill: #666666;\n  stroke: #666666;\n  margin: 0 auto;\n}\n\n.footer .main-info .cover {\n  height: 60px;\n  width: 60px;\n  object-fit: cover;\n  flex: 0 0 60px;\n  cursor: pointer;\n  position: relative;\n  color: #ffffff;\n}\n.footer .main-info .cover img {\n  height: 60px;\n  width: 60px;\n  object-fit: cover;\n}\n.footer .main-info .cover .mask {\n  display: none;\n}\n.footer .main-info .cover:hover .mask {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: rgba(0, 0, 0, 0.6);\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  bottom: 0;\n}\n\n.footer .main-info .detail {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n.footer .main-info .detail .ctrl {\n  position: absolute;\n  right: 0px;\n  top: 4px;\n  padding-right: 6px;\n  /* background: #eeeeee; */\n}\n.footer .main-info .detail .ctrl:first-of-type .icon {\n  margin-right: 5px;\n}\n.footer .main-info .detail .ctrl .icon {\n  color: var(--text-default-color);\n  opacity: 0.5;\n}\n.footer .main-info .detail .ctrl .icon:hover {\n  opacity: 1;\n}\n\n.footer .main-info .detail .title {\n  text-align: center;\n  font-size: 14px;\n  color: var(--text-default-color);\n  min-width: 0px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  margin: 3px 60px 0 60px;\n}\n\n.footer .main-info .detail .more-info {\n  padding: 0 10px;\n  display: flex;\n  color: var(--text-subtitle-color);\n}\n\n.footer .main-info .detail .more-info .singer {\n  flex: 1;\n  text-align: center;\n  font-size: 12px;\n  min-width: 0px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n.footer .main-info .detail .more-info .singer a {\n  cursor: pointer;\n}\n\n.footer .main-info .detail .more-info .current {\n  width: 50px;\n  font-size: 12px;\n}\n\n.footer .main-info .detail .more-info .total {\n  width: 50px;\n  text-align: right;\n  font-size: 12px;\n}\n\n.footer .main-info .detail .playbar {\n  width: 100%;\n}\n.footer .main-info .detail .playbar .playbar-clickable {\n  padding: 8px 10px;\n}\n.footer .main-info .detail .playbar .barbg {\n  height: 3px;\n  background: var(--footer-player-bar-background-color);\n}\n\n.footer .main-info .detail .playbar .barbg .cur {\n  height: 100%;\n  background: var(--footer-player-bar-cur-background-color);\n  position: relative;\n}\n\n.footer .main-info .detail .playbar .barbg .cur .btn {\n  background: var(--footer-player-bar-cur-button-color);\n  height: 8px;\n  width: 2px;\n  position: absolute;\n  right: -2px;\n  top: -5px;\n}\n\n.footer .main-info .detail .playbar .playbar-clickable:hover .barbg .cur .btn {\n  width: 10px;\n  height: 10px;\n  border-radius: 5px;\n  top: -3px;\n}\n\n.footer .menu-modal {\n  left: 0;\n  right: 0;\n  top: 0;\n  position: fixed;\n  background: rgba(255, 255, 255, 0.2);\n}\n.footer .menu-modal.slideup {\n  bottom: 60px;\n}\n\n.footer .menu {\n  background: var(--foot-background-color);\n  border: solid 1px var(--foot-border-color);\n  border-radius: 3px;\n  position: fixed;\n  height: 370px;\n  bottom: -311px;\n  left: 300px;\n  right: 300px;\n  -webkit-app-region: no-drag;\n  transition: all 0.3s;\n  overflow: hidden;\n}\n.footer .menu.slideup {\n  bottom: 60px;\n}\n\n.footer .menu .menu-header {\n  height: 30px;\n  border-bottom: solid 1px var(--footer-header-background-color);\n  display: flex;\n  align-items: center;\n  color: #9e9e9e;\n  font-size: 12px;\n}\n\n.footer .menu .menu-header .menu-title {\n  flex: 1;\n  padding: 20px;\n}\n\n.footer .menu .menu-header .add-all {\n  border-right: solid 1px #e5e5e5;\n  flex: 0 0 auto;\n  display: flex;\n  align-items: center;\n  padding-right: 10px;\n}\n\n.footer .menu .menu-header .remove-all {\n  margin-left: 10px;\n  flex: 0 0 auto;\n  display: flex;\n  align-items: center;\n}\n\n.footer .menu .menu-header .close {\n  margin-left: 10px;\n  flex: 0 0 25px;\n  align-items: center;\n  cursor: pointer;\n}\n.footer .menu .menu-header .add-all span,\n.footer .menu .menu-header .remove-all span {\n  cursor: pointer;\n}\n\n.footer .menu .menu-header .add-all .icon,\n.footer .menu .menu-header .remove-all .icon {\n  margin-right: 7px;\n}\n\n.footer .menu .menu-header .close svg {\n  margin-right: 3px;\n  height: 16px;\n  width: 16px;\n  cursor: pointer;\n}\n\n.footer .menu ul.menu-list {\n  overflow-y: scroll;\n  height: 340px;\n  font-size: 12px;\n}\n\n.footer .menu ul.menu-list li {\n  display: flex;\n  align-items: center;\n  height: 30px;\n  padding-right: 20px;\n  position: relative;\n  margin-bottom: -2px;\n  border-top: solid 2px var(--songlist-border-color);\n  border-bottom: solid 2px var(--songlist-border-color);\n}\n\n.footer .menu ul.menu-list li.even {\n  background: var(--footer-menu-even-background-color);\n}\n\n.footer .menu ul.menu-list li:hover {\n  background: var(--footer-menu-hover-background-color);\n}\n\n.footer .menu ul.menu-list li.playing {\n  color: var(--important-color);\n}\n.footer .menu ul.menu-list li .song-status-icon {\n  flex: 0 0 20px;\n  width: 20px;\n  height: 30px;\n  text-align: center;\n  display: flex;\n  align-items: center;\n}\n.footer .menu ul.menu-list li .song-status-icon svg {\n  width: 10px;\n  height: 10px;\n  fill: var(--important-color);\n  stroke: var(--important-color);\n  flex: 1;\n}\n.footer .menu ul.menu-list li .song-title {\n  flex: 2;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n.footer .menu ul.menu-list li .song-title.disabled {\n  color: #777777;\n}\n.footer .menu ul.menu-list li .song-title a {\n  cursor: pointer;\n}\n\n.footer .menu ul.menu-list li .song-singer {\n  flex: 1;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  cursor: pointer;\n}\n\n/* div.visited{\n  color: green;\n} */\n\n.footer .menu ul.menu-list li .tools {\n  flex: 0 0 42px;\n  width: 42px;\n}\n.footer .menu ul.menu-list li .tools .icon {\n  color: #666666;\n  cursor: pointer;\n  opacity: 0.5;\n}\n.footer .menu ul.menu-list li .tools .icon:first-of-type {\n  margin-right: 5px;\n}\n.footer .menu ul.menu-list li .tools .icon:hover {\n  opacity: 1;\n}\n\n.footer .menu ul.menu-list li .song-time {\n  flex: 1;\n  text-align: right;\n}\n\n.footer .right-control {\n  flex: 0 0 300px;\n  background: var(--foot-background-color);\n  display: flex;\n  align-items: center;\n}\n\n.footer .right-control .playlist-toggle {\n  margin-left: 29px;\n  cursor: pointer;\n}\n\n.footer .right-control .playlist-toggle .icon {\n  color: var(--player-right-icon-color);\n}\n\n.footer .right-control .playlist-toggle .icon:hover {\n  color: var(--player-right-icon-hover-color);\n}\n\n.footer .right-control .lyric-toggle {\n  margin-right: 30px;\n  cursor: pointer;\n}\n\n.footer .right-control .lyric-toggle .lyric-icon,\n.footer .right-control .lyric-toggle .lyric-icon.selected:hover {\n  border: solid 1px #7f7f7f;\n  height: 16px;\n  line-height: 16px;\n  font-size: 14px;\n  color: #7f7f7f;\n  background-color: var(--lyric-icon-background-color);\n  user-select: none;\n}\n\n.footer .right-control .lyric-toggle .lyric-icon.selected {\n  border: solid 1px #7f7f7f;\n  background-color: #7f7f7f;\n  color: #fff;\n}\n\n.footer .right-control .volume-ctrl {\n  flex: 1;\n  display: flex;\n}\n\n.footer .right-control .volume-ctrl .icon {\n  flex: 0 0 24px;\n  color: var(--volume-icon-color);\n  cursor: pointer;\n  margin-left: 21px;\n}\n.footer .right-control .volume-ctrl .m-pbar {\n  flex: 1;\n}\n\n.footer .right-control .volume-ctrl .barbg {\n  height: 3px;\n\n  background: var(--volume-bar-background-color);\n  margin-top: 7px;\n  width: 140px;\n}\n\n.footer .right-control .volume-ctrl .barbg .cur {\n  height: 100%;\n  background: var(--volume-bar-current-background-color);\n\n  position: relative;\n}\n\n.footer .right-control .volume-ctrl .barbg .cur .btn {\n  background: #ffffff;\n  height: 13px;\n  width: 13px;\n  border: solid 1px #e4e4e4;\n  border-radius: 13px;\n  position: absolute;\n  right: -13px;\n  top: -6px;\n}\n\n/* footer end */\n\n/* dialog start */\n.shadow {\n  position: fixed;\n  background: rgba(30, 30, 30, 0.9);\n  _position: absolute;\n  z-index: 9999;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n  background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);\n}\n\n.dialog {\n  position: absolute;\n  top: 120px;\n  width: 400px;\n  height: 430px;\n  z-index: 10000;\n  overflow: hidden;\n  border-radius: 4px;\n  background-color: var(--dialog-background-color);\n  color: var(--dialog-text-color);\n  box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n}\n\n.dialog-header {\n  height: 30px;\n  font-size: 15px;\n  font-weight: bold;\n  text-align: left;\n  padding: 12px;\n}\n\n.dialog-header .dialog-close {\n  float: right;\n  font-size: 26px;\n  cursor: pointer;\n  margin-top: -10px;\n}\n\n.dialog-body {\n  padding: 0 20px;\n  height: 370px;\n  overflow-y: auto;\n  background-color: var(--dialog-background-color);\n}\n\n.dialog-body .buttons {\n  display: flex;\n  justify-content: center;\n  margin-top: 20px;\n}\n\n.dialog-body .buttons .confirm-button {\n  margin-right: 20px;\n}\n.dialog .detail-songlist li:hover {\n  background-color: #e3e3e5;\n  cursor: pointer;\n}\n.dialog-body input {\n  width: 100%;\n}\n\n.dialog-playlist,\n.dialog-backuplist,\n.dialog-merge-playlist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.dialog-playlist li,\n.dialog-backuplist li,\n.dialog-merge-playlist li {\n  cursor: pointer;\n  height: 48px;\n  padding: 6px;\n}\n.dialog-backuplist li {\n  height: 96px;\n  padding: 6px;\n}\n\n.dialog-playlist li:hover,\n.dialog-backuplist li:hover,\n.dialog-merge-playlist li:hover {\n  background-color: var(--dialog-highlight-color);\n}\n\n.dialog-playlist li img,\n.dialog-backuplist li img,\n.dialog-merge-playlist li img {\n  float: left;\n  height: 48px;\n  width: 48px;\n}\n\n.dialog-playlist li h2,\n.dialog-backuplist li h2,\n.dialog-merge-playlist li h2 {\n  margin: 0 0 0 58px;\n  font-size: 13px;\n  font-weight: inherit;\n}\n.dialog-backuplist li h2 {\n  margin-top: 0;\n}\n.dialog-newplaylist {\n  padding: 10px;\n}\n\n.dialog-newbackup {\n  text-align: center;\n}\n\n.dialog-editplaylist label,\n.dialog-open-url label {\n  display: block;\n  height: 30px;\n  line-height: 30px;\n}\n\n.dialog-editplaylist .dialog-footer {\n  position: absolute;\n  bottom: 20px;\n}\n.dialog-body .field-name {\n  margin: 10px 0 5px 0;\n}\n\n/* dialog end */\n\n/* widget source-list start */\n.source-list {\n  margin: 20px 26px 10px 26px;\n}\n\n.source-list .source-button {\n  display: inline-block;\n  color: var(--link-inactive-color);\n  cursor: pointer;\n  padding-bottom: 4px;\n  font-size: 14px;\n}\n\n.source-list .source-button.active,\n.source-list .source-button:hover {\n  color: var(--link-active-color);\n  border-bottom: solid 1px var(--link-active-color);\n}\n\n.source-list .splitter {\n  display: inline-block;\n  background: #a9a9a9;\n  margin-top: 1px;\n  height: 12px;\n  width: 1px;\n  margin: 0 10px;\n}\n.source-list .search-type {\n  float: right;\n}\n/* widget source-list end */\n\n/* widget playlist-filter start */\n\n.playlist-filter {\n  line-height: 38px;\n  margin: 0 26px 10px 26px;\n}\n\n.playlist-filter .filter-item {\n  line-height: 20px;\n  padding: 5px 15px;\n  margin-right: 10px;\n}\n\n.playlist-filter .filter-item.active {\n  font-weight: 600;\n  background: var(--button-hover-background-color);\n}\n\n/* widget playlist-filter end */\n\n/* widget all-playlist-filter start */\n.all-playlist-filter .category {\n  margin-bottom: 10px;\n  display: flex;\n}\n\n.all-playlist-filter .category .category-title {\n  margin-left: 30px;\n  margin-top: 12px;\n  min-width: 50px;\n  font-size: 18px;\n}\n.all-playlist-filter .category .category-filters {\n  margin-left: 10px;\n  display: flex;\n  flex-wrap: wrap;\n}\n.all-playlist-filter .category .category-filters .filter-item {\n  min-width: 80px;\n  margin-top: 10px;\n  display: flex;\n}\n.all-playlist-filter .category .category-filters .filter-item span {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  padding: 5px 10px;\n}\n.all-playlist-filter .category .category-filters .filter-item span:hover {\n  background-color: var(--button-background-color);\n  border-radius: var(--default-border-radius);\n}\n/* widget all-playlist-filter end */\n"
  },
  {
    "path": "css/common2.css",
    "content": "html,\nbody {\n  margin: 0;\n  padding: 0;\n  font-size: var(--text-default-size);\n  color: var(--text-default-color);\n  font-family: system-ui, 'PingFang SC', STHeiti, sans-serif;\n \n}\nbody .body{\n  background-color: var(--color-body-bg);\n  transition: background 0.2s;\n  border-radius: 8px;\n}\na {\n  cursor: pointer;\n  color: inherit;\n  text-decoration: none;\n}\na:hover {\n  text-decoration: underline;\n}\n\n.wrap {\n  /* https://stackoverflow.com/questions/28897089/z-index-on-borders */\n  outline: solid 1px var(--windows-border-color);\n  box-sizing: border-box;\n}\n\n/* remove focus highlight */\ninput:focus,\nselect:focus,\ntextarea:focus,\nbutton:focus {\n  outline: none;\n}\ninput:focus,\ntextarea:focus {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\nul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\ninput,\nsvg,\n.icon {\n  -webkit-app-region: no-drag;\n}\n\nbutton {\n  background-color: var(--button-background-color);\n  color: var(--text-default-color);\n  cursor: pointer;\n  border: solid 1px var(--button-background-color);\n  border-radius: var(--default-border-radius);\n  padding: 5px;\n  min-width: 80px;\n  min-height: 32px;\n}\nbutton:hover {\n  background-color: var(--button-hover-background-color);\n}\nimg {\n  -webkit-user-drag: none;\n  object-fit: cover;\n}\n.l1-button {\n  background-color: var(--button-background-color);\n  color: var(--text-default-color);\n  border-radius: var(--default-border-radius);\n  padding: 5px;\n  margin-right: 4px;\n  color: var(--text-default-color);\n  cursor: pointer;\n  display: inline-block;\n}\n.l1-button:hover {\n  background: var(--button-hover-background-color);\n  color: var(--text-default-color);\n}\nsvg {\n  width: 24px;\n  height: 24px;\n  stroke: currentColor;\n  stroke-width: 1;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  fill: none;\n  cursor: pointer;\n  /*    stroke: var(--icon-default-color);*/\n}\n\n.icon {\n  /* default icon settings */\n  font-size: 16px;\n  cursor: pointer;\n}\n\n/* tools utils */\n.flex-scroll-wrapper {\n  flex: 1;\n  height: 100px;\n  /* overflow-y: scroll; */\n  scrollbar-width: thin;\n  scrollbar-color: var(--scroll-color) var(--content-background-color);\n}\n\n/* scroll bar style */\n::-webkit-scrollbar {\n  width: 8px;\n  background: transparent;\n  transition: background 0.4s;\n}\n\n::-webkit-scrollbar-thumb {\n  transition: background 0.4s;\n  width: 8px;\n  border-radius: 4px;\n  background: hsla(0, 0%, 50.2%, 0.38);\n  /*rgba(151, 151, 151, 0.4);*/\n\n  /*    -webkit-box-shadow: inset -1px -1px 0px rgba(0, 0, 0, 0.05), inset 1px 1px 0px rgba(0, 0, 0, 0.05);*/\n}\n\n::-webkit-scrollbar-thumb:hover,\n::-webkit-scrollbar-thumb:active {\n  background-color: hsla(0, 0%, 50.2%, 0.58);\n  transition: background 0.4s;\n}\n\n::-webkit-scrollbar-button {\n  width: 0;\n  height: 0;\n  display: none;\n}\n\n::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n/* main framework start */\n.wrap {\n  display: flex;\n  height: 100vh;\n  flex-direction: column;\n  margin: auto;\n}\n\n/* split screen to up/down 2 parts */\n.main {\n  flex: 1;\n  display: flex;\n  overflow: hidden;\n}\n\n/* split main to left/right 2 parts */\n.main .sidebar {\n  padding-left: 1vw;\n  display: flex;\n  flex-direction: column;\n}\n\n.main .content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n\n/* split content to up/down 2 parts */\n.main .content .navigation {\n  user-select: none;\n  height: 64px;\n  flex: 0 0 64px;\n  display: flex;\n  align-items: center;\n  -webkit-app-region: drag;\n  margin-right: 20px;\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  z-index: 100;\n  -webkit-backdrop-filter: saturate(180%) blur(20px);\n  backdrop-filter: saturate(180%) blur(20px);\n  background-color: var(--nav-background-color);\n  transition: background 0.2s;\n}\n\n.main .content .browser {\n  flex: 1;\n}\n\n/* main framework end */\n\n/*****************************************************************/\n\n/* main sidebar start */\n.main .sidebar-content {\n  height: calc(100vh - 210px);\n  overflow: hidden;\n  width: 60px;\n  transition: 0.2s;\n  background: var(--sidebar-background);\n  border-radius: 10px;\n  cursor: default;\n}\n.main .sidebar-content.footerdef {\n  height: calc(100vh - 130px);\n}\n.main .sidebar-content.opensidebar {\n  width: 200px;\n}\n.sidebar-content .logo-content {\n  display: flex;\n  align-items: center;\n  justify-content: flex-start;\n  margin: 10px;\n  margin-bottom: 0;\n  padding-bottom: 10px;\n  border-bottom: 1px solid var(--sidebar-splitter);\n  transition: 0.2s;\n  cursor: pointer;\n}\n.sidebar-content .logo-content .logo-svg {\n  padding: 10px;\n  padding-right: 0;\n  transition: 0.2s;\n}\n.sidebar-content .logo-content .logo-title {\n  padding-right: 10px;\n  display: flex;\n}\n.sidebar-content .logo-content .logo-title svg {\n  color: var(--sidebar-splitter);\n  opacity: 0;\n  width: 0;\n  transition: 0.2s;\n}\n.opensidebar > .logo-content {\n  border-bottom: 1px solid transparent;\n}\n.opensidebar > .logo-content .logo-title svg {\n  opacity: 1;\n  width: 90px;\n}\n.sidebar .sidebar-scroll-content {\n  overflow-x: hidden;\n  overflow-y: overlay;\n\n  height: calc(100% - 80px);\n}\n.sidebar .sidebar-scroll-content::-webkit-scrollbar {\n  display: none;\n}\n.sidebar .sidebar-scroll-content::-webkit-scrollbar-thumb {\n  background: var(--theme-color-ope);\n}\n.sidebar .opensidebar > .sidebar-scroll-content:hover::-webkit-scrollbar {\n  display: block;\n  width: 2px;\n}\n.sidebar .menu-control {\n  height: 74px;\n  -webkit-app-region: drag;\n}\n\n.sidebar .menu-title {\n  height: 28px;\n  line-height: 28px;\n  margin: 8px 12px 8px 12px;\n  color: var(--link-default-color);\n  padding-left: 10px;\n  display: flex;\n  align-items: center;\n  font-size: 12px;\n}\n.sidebar .menu-title .title {\n  user-select: none;\n  white-space: nowrap;\n  opacity: 0;\n  transition: 0.2s;\n  width: 0;\n  flex: 0;\n}\n.sidebar .menu-title .title.opensidebar {\n  opacity: 1;\n  flex: 1;\n  width: auto;\n}\n.sidebar ul li .sidebar-block > div {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-right: 10px;\n}\n.sidebar ul li .sidebar-block .sidebar .menu-title svg {\n  flex: 0 0 18px;\n}\n\n.sidebar ul li {\n  cursor: pointer;\n  padding: 2px 10px;\n}\n.sidebar ul li .sidebar-block {\n  display: flex;\n  align-items: center;\n  line-height: 28px;\n  padding: 6px 12px;\n  margin: 1px 0;\n  transition: all 0.2s;\n\n  color: var(--sidebar-hover-text-color);\n  border-radius: var(--default-border-radius);\n  background-color: var(--sidebar-button-background);\n}\n.sidebar ul li .sidebar-block.opensidebar {\n  background-color: transparent;\n}\n.sidebar svg {\n  width: 18px;\n  height: 18px;\n  z-index: 10;\n}\n.sidebar .icon {\n  margin-right: 10px;\n  font-size: 18px;\n}\n.sidebar ul li a {\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.sidebar ul li:hover .sidebar-block {\n  background: var(--theme-color-hover);\n  color: var(--text-default-color);\n  border-radius: 10px;\n  transition: all 0.3s;\n}\n.sidebar ul li:hover .sidebar-block.opensidebar {\n  background: var(--sidebar-button-background);\n}\n\n.sidebar ul li.active .sidebar-block,\n.sidebar ul li.active:hover .sidebar-block {\n  background: var(--theme-color);\n  color: #fff;\n  border-radius: 10px;\n}\n.sidebar ul li.dragover .sidebar-block {\n  background: var(--sidebar-highlight-background-color);\n  color: var(--text-default-color);\n  border-radius: 10px;\n}\n/*\navoid hover effect trigger dragleave event\nhttps://stackoverflow.com/questions/19889615/can-an-angular-directive-pass-arguments-to-functions-in-expressions-specified-in\n*/\n.sidebar ul li * {\n  pointer-events: none;\n}\n/* main sidebar end */\n\n/* widget navigation start */\n.navigation svg {\n  width: 24px;\n  height: 24px;\n}\n.navigation .icon svg {\n  color: var(--text-default-color);\n}\n.navigation .backfront {\n  flex: 0 0 45px;\n  line-height: 46px;\n  vertical-align: middle;\n  padding: 0 13px;\n  flex: 1;\n}\n\n.navigation .search {\n  display: flex;\n  width: 200px;\n  height: 32px;\n  background: var(--search-input-background-color);\n  border-style: none;\n  border-radius: var(--default-border-radius);\n  padding-left: 10px;\n  margin-right: 20px;\n  align-items: center;\n}\n.navigation .settings {\n  margin-right: 8px;\n}\n\n.navigation .settings.is-setting {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  padding: 8px;\n  margin-right: 1vw;\n  transition: 0.2s;\n}\n.navigation .settings.is-setting:hover {\n  opacity: 1;\n  background-color: var(--songlist-hover-background-color);\n\n  border-radius: 25%;\n}\n.navigation .settings.is-setting:hover svg {\n  color: var(--text-default-color);\n}\n\n.navigation .icon {\n  color: var(--text-default-color);\n  background-color: transparent;\n  /* opacity: 0.5; */\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 8px;\n  border-radius: 25%;\n  transition: 0.2s;\n}\n.navigation .icon img {\n  border-radius: 50%;\n  width: 30px;\n  height: 30px;\n}\n\n.navigation .icon:hover {\n  opacity: 1;\n  background-color: var(--songlist-hover-background-color);\n}\n\n.navigation .backfront .icon {\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.navigation .backfront .icon:nth-of-type(1) {\n  margin-right: 8px;\n}\n\n.navigation .search-input {\n  width: 174px;\n  background-color: transparent;\n  border-style: none;\n  font-size: 16px;\n  font-weight: 600;\n  color: var(--text-default-color);\n}\n\n.navigation .window-control {\n  display: flex;\n}\n\n.navigation .window-control svg {\n  margin-left: 8px;\n}\n\n.navigation .window-control svg:first-of-type {\n  margin-left: 15px;\n}\n\n/* navigation end */\n\n/* page hot-playlist start */\n\n.playlist-covers,\n.detail-songlist.isSearch {\n  padding-right: 2vw;\n  padding-top: 30px;\n  display: flex;\n  flex-flow: row wrap;\n  position: relative;\n\n  margin: 0 14px;\n  grid-template-columns: repeat(5, 1fr);\n  gap: 40px 0px;\n}\n.playlist-covers {\n  transition: padding 0.3s;\n}\n.playlist-covers li {\n  color: var(--text-default-color);\n  margin: 0 12px;\n}\n@media screen and (max-width:1000px){\n  .playlist-covers li {\n    flex: 0 1 calc(25% - 26px);\n  }\n}\n\n@media screen and (min-width:1000px) and (max-width:1480px){\n  .playlist-covers li {\n    flex: 0 1 calc(20% - 26px);\n  }\n}\n\n@media screen and (min-width:1480px){\n  .playlist-covers li {\n    flex: 0 1 calc(16.66% - 26px);\n  }\n}\n.playlist-covers .u-cover,\nul.detail-songlist li .u-cover {\n  display: flex;\n  position: relative;\n  justify-content: center;\n  align-items: center;\n  user-select: none;\n}\n\n.playlist-covers .u-cover img,\nul.detail-songlist li .u-cover img {\n  transition: all 0.1s ease-in-out 0s;\n  box-sizing: border-box;\n  width: 100%;\n  border-radius: 0.75em;\n  /* min-width: 136px;\n  max-width: 100%; */\n  object-fit: cover;\n  border: solid 1px rgba(0, 0, 0, 0.04);\n  cursor: pointer;\n  z-index: 1;\n}\n\n.playlist-covers .u-cover .bottom,\n.detail-head-cover .bottom,\nul.detail-songlist li .u-cover .bottom {\n  position: absolute;\n  z-index: 2;\n  cursor: pointer;\n  opacity: 0;\n  transition: all 0.2s ease 0s;\n\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: rgb(255, 255, 255);\n  backdrop-filter: blur(8px);\n  background: hsla(0, 0%, 100%, 0.14);\n  border: 1px solid hsla(0, 0%, 100%, 0.08);\n  height: 22%;\n  width: 22%;\n  border-radius: 50%;\n}\n.playlist-covers .u-cover .covershadow,\n.detail-head-cover .covershadow,\nul.detail-songlist li .u-cover .covershadow {\n  transition: all 0.4s;\n  opacity: 0;\n  position: absolute;\n  top: 12px;\n  height: 100%;\n  width: 100%;\n  filter: blur(16px) opacity(0.6);\n  transform: scale(0.92, 0.96);\n  z-index: 0;\n  background-size: cover;\n  border-radius: 0.75em;\n}\n.playlist-covers .u-cover:hover img,\nul.detail-songlist li .u-cover:hover img {\n  margin-top: -10px;\n  margin-bottom: 10px;\n  padding-bottom: 0;\n}\n.playlist-covers .u-cover:hover .covershadow,\nul.detail-songlist li .u-cover:hover .covershadow {\n  display: block;\n  opacity: 1;\n}\n.playlist-covers .u-cover:hover .bottom,\n.detail-head-cover:hover .bottom,\nul.detail-songlist li .u-cover:hover .bottom {\n  opacity: 1;\n}\n.playlist-covers .u-cover:hover .bottom:hover,\n.detail-head-cover:hover .bottom:hover,\nul.detail-songlist li .u-cover .bottom:hover {\n  background: hsla(0, 0%, 100%, 0.28);\n}\n\n.playlist-covers .u-cover .bottom svg,\nul.detail-songlist li .u-cover .bottom svg {\n  height: 30px;\n  width: 30px;\n  fill: rgba(200, 200, 200, 0.5);\n  stroke-width: 1;\n  stroke: #ffffff;\n}\n\n.playlist-covers .u-cover .bottom svg:hover,\nul.detail-songlist li .u-cover .bottom svg:hover {\n  fill: rgba(100, 100, 100, 0.5);\n}\n\n.playlist-covers .desc,\nul.detail-songlist li .desc {\n  cursor: default;\n  padding-top: 8px;\n  height: 65px;\n}\n\n.playlist-covers .desc .title,\nul.detail-songlist li .desc div.title {\n  word-break: break-all;\n  font-size: 16px;\n  font-weight: 600;\n  line-height: 20px;\n  word-break: break-all;\n  display: flex;\n  margin: 0 0 5px;\n  z-index: 1;\n  text-overflow: ellipsis;\n  -webkit-line-clamp: 3;\n  -webkit-box-orient: vertical;\n  display: -webkit-box;\n  text-decoration: none;\n  overflow: hidden;\n}\n\n/* .playlist-covers .desc .title:hover,\nul.detail-songlist li .desc div.title:hover{\n  text-decoration: underline;\n} */\n/* page hot-playlist end */\n\n/* page playlist-detail start */\n.page .playlist-detail {\n  padding-bottom: 37px;\n}\n\n.page .playlist-detail .detail-head {\n  display: flex;\n  margin-top: 11px;\n  margin-bottom: 72px;\n}\n\n.page .playlist-detail .detail-head img {\n  position: relative;\n  z-index: 1;\n  height: 100%;\n  width: 100%;\n  border-radius: 0.75em;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  aspect-ratio: 1/1;\n}\n\n.page .playlist-detail .detail-head .covershadow {\n  transition: all 0.4s;\n  opacity: 0;\n  position: absolute;\n  top: 12px;\n  height: 100%;\n  width: 100%;\n  filter: blur(16px) opacity(0.6);\n  transform: scale(0.92, 0.96);\n  z-index: 0;\n  background-size: cover;\n  border-radius: 0.75em;\n}\n\n.page .playlist-detail .detail-head .detail-head-cover {\n  position: relative;\n  margin-left: 2vw;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  z-index: 1;\n  height: 25vh;\n  width: 25vh;\n}\n\n.page .playlist-detail .detail-head .detail-head-title {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n  margin-left: 56px;\n  margin-right: 2vw;\n}\n.page .playlist-detail .detail-head .detail-head-title h2 {\n  font-size: 36px;\n  font-weight: 700;\n  margin-top: 10px;\n}\n\n.playlist-button-list {\n  display: flex;\n  flex-flow: row wrap;\n}\n\n.playlist-button-list .playlist-button {\n  margin-top: 10px;\n  height: 26px;\n  cursor: pointer;\n  display: flex;\n  margin-right: 16px;\n  border-radius: 8px;\n  padding: 8px 16px;\n  width: auto;\n  background-color: var(--button-background-color);\n}\n.playlist-button-list .playlist-button.favorited {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n\n.playlist-button-list .playlist-button.playadd-button {\n  flex: 0 0 136px;\n}\n\n.playlist-button-list .playlist-button .play-list {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  font-size: 17px;\n  line-height: 17px;\n  font-weight: 500;\n  user-select: none;\n}\n.playlist-button-list .playlist-button .play-list svg {\n  margin-right: 4px;\n}\n\n.playlist-button-list .playlist-button.playadd-button .play-list svg {\n  width: 14px;\n  height: 14px;\n  flex: 0 0 14px;\n  margin-right: 4px;\n  stroke: var(--important-color);\n  fill: var(--important-color);\n}\n.playlist-button-list .playlist-button .play-list .icon {\n  margin-right: 8px;\n}\n.playlist-button-list .playlist-button.playadd-button .play-list .icon {\n  flex: 0 0 14px;\n  margin-right: 4px;\n  color: var(--important-color);\n  transition: 0.2s;\n}\n\n.playlist-button-list .playlist-button.playadd-button .add-list {\n  flex: 0 0 26px;\n  height: 26px;\n  width: 26px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: 0.2s;\n  font-size: 17px;\n  line-height: 17px;\n  font-weight: 500;\n}\n\n.playlist-button-list .playlist-button.edit-button .play-list.favorited {\n  color: var(--text-default-color);\n}\n.playlist-button-list .playlist-button.edit-button .play-list.notfavorite {\n  color: var(--text-default-color);\n}\n\n.playlist-button-list .playlist-button:hover,\n.playlist-button-list .playlist-button.playadd-button .add-list:hover,\n.playlist-button-list .playlist-button.playadd-button .play-list:hover {\n  transform: scale(1.1);\n  transition: 0.2s;\n}\n.playlist-button-list .playlist-button.playadd-button .add-list svg {\n  width: 14px;\n  height: 14px;\n}\n\n.playlist-button-list .playlist-button.clone-button,\n.playlist-button-list .playlist-button.edit-button,\n.playlist-button-list .playlist-button.fav-button {\n  flex: 0 0 auto;\n}\n\n.playlist-button-list .playlist-button.clone-button .play-list svg,\n.playlist-button-list .playlist-button.edit-button .play-list svg,\n.playlist-button-list .playlist-button.fav-button .play-list svg {\n  width: 16px;\n  height: 16px;\n  flex: 0 0 16px;\n  margin-right: 8px;\n  stroke: rgb(102, 102, 102);\n}\n\n.playlist-button-list .playlist-button.fav-button .play-list.favorited svg {\n  fill: rgb(102, 102, 102);\n}\n\n/* page playlist detail end */\n\n/* page song detail start */\n.songdetail-wrapper {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 100px;\n  overflow: hidden;\n  -webkit-app-region: no-drag;\n  transition: all 0.5s;\n  z-index: 100;\n  opacity: 1;\n}\n\n.songdetail-wrapper .draggable-zone {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  -webkit-app-region: drag;\n  height: 80px;\n}\n\n.songdetail-wrapper.slidedown .draggable-zone {\n  display: none;\n  -webkit-app-region: no-drag;\n}\n\n.songdetail-wrapper.slidedown {\n  top: calc(100% - 100px);\n  /* opacity: 0; */\n}\n\n.songdetail-wrapper .close {\n  position: absolute;\n  top: 24px;\n  left: 24px;\n  height: 19px;\n  width: 19px;\n  cursor: pointer;\n  padding: 5px;\n  border-radius: 50%;\n  -webkit-app-region: no-drag;\n  transition: 0.2s;\n  z-index: 100;\n}\n.songdetail-wrapper .close:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n.songdetail-wrapper .close.mac {\n  top: 44px;\n}\n\n.songdetail-wrapper .window-control {\n  position: absolute;\n  top: 24px;\n  right: 24px;\n  height: 24px;\n  cursor: pointer;\n  -webkit-app-region: no-drag;\n  z-index: 99;\n}\n\n.songdetail-wrapper .window-control svg {\n  margin-left: 8px;\n  stroke: var(--now-playing-close-icon-color);\n}\n\n.songdetail-wrapper .close svg {\n  stroke: var(--now-playing-close-icon-color);\n}\n.bgwrapper {\n  overflow: hidden;\n  border-radius: 10px;\n  width: 100%;\n}\n.bg {\n  opacity: 0.6;\n  width: 100%;\n  height: 100%;\n  filter: blur(200px) contrast(75%) brightness(150%);\n  float: left;\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: cover;\n  transition: background ease-in-out 1.5s;\n}\n.playsong-detail {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  bottom: 0;\n  display: flex;\n  clip: rect(auto, auto, auto, auto);\n}\n\n.playsong-detail .detail-head {\n  flex: 1;\n  display: flex;\n  justify-content: flex-end;\n  margin-right: 32px;\n  margin-top: 24px;\n  align-items: center;\n  transition: all 0.5s;\n  z-index: 1;\n}\n\n.playsong-detail .detail-head-cover {\n  position: relative;\n}\n\n.playsong-detail .detail-head img {\n  border-radius: 10px;\n  width: 54vh;\n  height: 54vh;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -o-object-fit: cover;\n  object-fit: cover;\n}\n\n.playsong-detail .detail-songinfo {\n  flex: 1;\n  font-weight: 600;\n  color: var(--color-text);\n  margin-right: 24px;\n  z-index: 0;\n}\n.playsong-detail .detail-head-title {\n  max-width: 54vh;\n  margin-top: 24px;\n}\n\n.playsong-detail .title {\n  display: flex;\n  align-items: center;\n}\n.playsong-detail .title h2 {\n  font-size: var(--h2-title-font-size);\n  margin-top: 8px;\n  margin-bottom: 0;\n  font-weight: 600;\n  opacity: 0.88;\n  display: -webkit-box;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: 1;\n  overflow: hidden;\n}\n.playsong-detail .title .badge {\n  font-size: var(--badge-font-size);\n  color: var(--theme-color);\n  border: solid 1px var(--theme-color);\n  border-radius: 5px;\n  margin-left: 10px;\n  padding-left: 4px;\n  padding-right: 4px;\n  margin-top: 4px;\n  box-sizing: border-box;\n  height: 20px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  white-space: nowrap;\n}\n.playsong-detail .title .badge.platform {\n  padding-top: 1px;\n}\n.playsong-detail .title .badge:first-of-type {\n  margin-left: 15px;\n}\n.playsong-detail .info {\n  margin-top: 4px;\n  font-size: 16px;\n  opacity: 0.58;\n  display: -webkit-box;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: 1;\n  overflow: hidden;\n}\n\n.playsong-detail .info .singer {\n  display: inline;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.playsong-detail .info .album {\n  display: inline;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.coverbg .playsong-detail .info span {\n  color: var(--lyric-on-cover-color);\n}\n.playsong-detail .detail-songinfo .lyric {\n  font-size: 16px;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  padding-left: 78px;\n  max-width: 460px;\n  overflow-y: auto;\n  transition: 0.5s;\n\n  color: var(--lyric-default-color);\n}\n.playsong-detail .detail-songinfo .lyric::-webkit-scrollbar {\n  display: none;\n}\n\n.coverbg .playsong-detail .detail-songinfo .lyric {\n  color: var(--lyric-on-cover-color);\n}\n.playsong-detail .detail-songinfo .lyric .placeholder {\n  margin-top: 50vh;\n}\n.playsong-detail .detail-songinfo .lyric p {\n  padding: 18px;\n  transition: all 0.2s;\n  border-radius: 12px;\n  margin: 0;\n  opacity: 0.28;\n  cursor: default;\n  background: transparent;\n  color: var(--text-default-color);\n}\n.playsong-detail .detail-songinfo .lyric p:hover {\n  background: hsla(0, 0%, 100%, 0.08);\n  opacity: 0.6;\n  color: var(--text-default-color);\n}\n.playsong-detail .detail-songinfo .lyric p.translate {\n  margin: 5px 0 0 0;\n}\n.playsong-detail .detail-songinfo .lyric p.hide {\n  display: none;\n}\n.playsong-detail .detail-songinfo .lyric p.highlight {\n  color: var(--text-default-color);\n  opacity: 1;\n  font-size: 26px;\n}\n.coverbg .playsong-detail .detail-songinfo .lyric p.highlight {\n  color: var(--lyric-important-on-cover-color);\n  opacity: 1;\n}\n\nul.detail-songlist {\n  position: relative;\n}\n.detail-songlist.playlist-songlist {\n  margin: 0 2vw;\n  padding-top: 13px;\n  transition: 0.3s;\n}\nul.detail-songlist.isSearchOne {\n  padding: 0 25px;\n}\n\nul.detail-songlist .playlist-search {\n  position: absolute;\n  right: 0;\n  top: -50px;\n\n  display: flex;\n  width: 200px;\n  height: 32px;\n  background: var(--songlist-odd-background-color);\n  border-style: none;\n  border-radius: var(--default-border-radius);\n  padding-left: 10px;\n  margin-right: 40px;\n  align-items: center;\n}\nul.detail-songlist .playlist-search .playlist-search-icon {\n  width: 14px;\n  position: absolute;\n  left: 7px;\n  top: 1px;\n}\nul.detail-songlist .playlist-search .playlist-clear-icon {\n  width: 14px;\n  position: absolute;\n  left: 158px;\n}\nul.detail-songlist .playlist-search .playlist-search-input {\n  width: 174px;\n  background-color: transparent;\n  border-style: none;\n  font-size: 16px;\n  font-weight: 600;\n  color: var(--text-default-color);\n}\nul.detail-songlist .playlist-search:hover,\nul.detail-songlist .playlist-search:active {\n  background-color: var(--search-input-background-color);\n}\n\nul.detail-songlist li.isSearchType {\n  /* https://stackoverflow.com/questions/4157005/css-positioning-z-index-negative-margins */\n  position: relative;\n\n  transition: all 0.3s;\n  display: flex;\n  align-items: center;\n  padding: 8px;\n  border-radius: 12px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\nul.detail-songlist li.isSearchGeDan {\n  flex: 0 1 calc(20% - 26px);\n  min-height: 156px;\n  color: var(--text-default-color);\n  margin: 0 12px;\n  cursor: default;\n}\n\nul.detail-songlist li.playlist-result.isSearchType {\n  height: 80px;\n  padding: 0 10px;\n}\n\nul.detail-songlist li > img {\n  object-fit: cover;\n  border-radius: 8px;\n  height: 46px;\n  width: 46px;\n  margin-right: 20px;\n  border: 1px solid rgba(0, 0, 0, 0.04);\n  cursor: pointer;\n}\n\nul.detail-songlist li.isSearchType:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n\nul.detail-songlist li.isSearchType.playing,\nul.detail-songlist li.isSearchType.playing:hover {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\nul.detail-songlist li .title-and-artist {\n  flex: 1;\n  display: flex;\n}\nul.detail-songlist li .container {\n  display: flex;\n  flex-direction: column;\n}\n\nul.detail-songlist li a {\n  cursor: pointer;\n}\nul.detail-songlist li a.disabled {\n  color: var(--disable-song-title-color);\n}\nul.detail-songlist li a span.source,\nul.detail-songlist span {\n  border: solid 1px #ccc;\n  border-radius: 4px;\n  margin-right: 10px;\n  display: inline-block;\n  padding: 0 4px;\n  color: #ccc;\n  font-size: 12px;\n  width: 24px;\n  text-align: center;\n  white-space: nowrap;\n  height: min-content;\n}\nul.detail-songlist li a span.source.playlist {\n  margin-left: 10px;\n  margin-right: 0;\n}\n\nul.detail-songlist li .title {\n  font-size: 18px;\n  font-weight: 600;\n\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n\n  -webkit-line-clamp: 1;\n  -webkit-box-orient: vertical;\n  word-break: break-all;\n}\nul.detail-songlist li.playlist-result .title {\n  max-height: 80px;\n}\n\nul.detail-songlist li.playlist-result .title img {\n  height: 60px;\n  width: 60px;\n  display: block;\n  margin-right: 10px;\n}\n\nul.detail-songlist li .artist {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  /* line-height: 17px;\n  max-height: 38px; */\n  -webkit-line-clamp: 1;\n  -webkit-box-orient: vertical;\n\n  font-size: 13px;\n  margin-top: 2px;\n  font-size: 13px;\n  opacity: 0.68;\n}\n\nul.detail-songlist li .album {\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  /* line-height: 17px;\n  max-height: 38px; */\n  -webkit-line-clamp: 1;\n  -webkit-box-orient: vertical;\n\n  display: flex;\n  font-size: 16px;\n  opacity: 0.88;\n}\n\nul.detail-songlist li .tools {\n  flex: 0 0 110px;\n  display: flex;\n  align-items: center;\n}\nul.detail-songlist li .tools a:hover {\n  color: var(--text-default-color) !important;\n}\n\nul.detail-songlist li .tools .icon {\n  height: 16px;\n  width: 16px;\n  color: #9d9d9d;\n  margin-top: 2px;\n  margin-right: 10px;\n}\n/* page song detail end */\n\n/* page login start */\n.page .login {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  min-height: calc(100vh - 192px);\n}\n.page .login .login-logo {\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n}\n.page .login .login-logo img {\n  height: 64px;\n  margin: 20px;\n}\n.page .login .login-title {\n  font-size: 18px;\n  margin-bottom: 10px;\n}\n.page .login .login-form .login-form_field {\n  display: flex;\n  align-items: center;\n  height: 40px;\n  margin: 24px;\n  width: 270px;\n  border: solid 1px var(--button-background-color);\n}\n.page .login .login-form .login-form_field input {\n  background: var(--content-background-color);\n  color: var(--text-default-color);\n}\n.page .login .login-form .login-form_field input.login-form_field_countrycode {\n  flex: 0 0 40px;\n  width: 40px;\n}\n.page .login .login-form .login-form_field svg {\n  margin-left: 12px;\n  margin-right: 12px;\n  color: var(--icon-default-color);\n  width: 18px;\n  height: 18px;\n}\n.page .login .login-form .login-form_field input {\n  border: none;\n  flex: 1;\n  font-size: 16px;\n}\n.page .login .login-form .login-form_field input:focus {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n.page .login .login-submit_button {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 14px;\n  margin-top: 24px;\n  padding: 8px;\n  width: 270px;\n  cursor: pointer;\n  border: solid 1px var(--button-border-color);\n}\n.page .login .login-switcher {\n  margin-top: 24px;\n  cursor: pointer;\n}\n.page .login .login-notice {\n  width: 270px;\n  border-top: 1px solid var(--button-border-color);\n  margin-top: 30px;\n  padding-top: 12px;\n  font-size: 12px;\n  color: var(--text-subtitle-color);\n}\n.page .login .usercard {\n  padding: 20px;\n  display: flex;\n  align-items: center;\n  width: 400px;\n  background-color: var(--button-background-color);\n  margin-bottom: 20px;\n  border-radius: 10px;\n  cursor: pointer;\n  transition: all 0.3s;\n}\n.page .login .usercard .logoin-icon {\n  width: 60px;\n  height: 60px;\n  margin: 10px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.page .login .usercard .logoin-icon svg {\n  width: 35px;\n  height: 35px;\n}\n.page .login .usercard:hover,\n.page .login .usercard:active {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n  width: 500px;\n}\n.page .login .usercard:hover .usercard-info,\n.page .login .usercard:active .usercard-info {\n  color: var(--theme-color);\n}\n.page .login .usercard img {\n  border-radius: 10px;\n  width: 60px;\n  height: 60px;\n  margin: 10px;\n}\n.page .login .usercard .usercard-title {\n  margin-left: 10px;\n  flex: 1;\n  height: 50px;\n  font-size: 18px;\n  font-weight: 700;\n}\n.page .login .usercard .usercard-title .usercard-info {\n  color: var(--text-subtitle-color);\n  font-size: 12px;\n}\n.page .login .usercard button {\n  margin: 10px;\n  margin: 10px;\n  font-size: 14px;\n  font-weight: 600;\n  padding: 8px 16px;\n  transition: 0.2s;\n}\n\n.page .login .usercard button:hover {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n/* page login end */\n\n/* page setting start */\n\n.page .settings-title {\n  max-width: 800px;\n  margin: 0 auto;\n  padding: 0 20px;\n  font-weight: bold;\n  padding-bottom: 12px;\n  font-size: 26px;\n  user-select: none;\n  border-bottom: 1px solid rgba(128, 128, 128, 0.18);\n}\n\n.page .settings-content {\n  max-width: 800px;\n  margin: 25px auto;\n  padding: 0 20px;\n  font-size: 16px;\n  font-weight: 500;\n  opacity: 0.78;\n}\n.page .settings-content label.upload-button,\n.page .settings-content .language-button,\n.page .settings-content .theme-button,\n.page .settings-content button {\n  background: var(--button-background-color);\n  margin-right: 4px;\n  color: var(--text-default-color);\n  cursor: pointer;\n  opacity: 1;\n  font-weight: 600;\n  border: none;\n  padding: 8px 12px;\n  border-radius: 8px;\n  appearance: none;\n  transition: all 0.2s;\n}\n\n.page .settings-content label.upload-button:hover,\n.page .settings-content .language-button:hover,\n.page .settings-content button:hover {\n  transform: scale(1.1);\n  background: var(--button-hover-background-color);\n}\n.page .settings-content .shortcut {\n  display: flex;\n  margin-bottom: 25px;\n}\n.page .settings-content .shortcut svg {\n  width: 18px;\n  height: 18px;\n  margin-right: 10px;\n}\n.page .searchbox .search-pagination {\n  text-align: center;\n  padding: 32px;\n}\n.page .searchbox .search-pagination .btn-pagination {\n  padding: 8px 16px;\n  width: auto;\n  height: 40px;\n  min-width: 40px;\n  font-size: 18px;\n  line-height: 18px;\n  font-weight: 600;\n  transition: 0.2s all;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  opacity: 0.78;\n}\n.page .searchbox .search-pagination .ng-binding {\n  font-size: 18px;\n  line-height: 18px;\n  font-weight: 600;\n  padding: 0 20px;\n}\n.page .settings-content .shortcut_table .shortcut_table-header,\n.page .settings-content .shortcut_table .shortcut_table-line {\n  display: flex;\n  color: var(--text-default-color);\n  box-sizing: border-box;\n  align-items: center;\n  height: 40px;\n}\n.page .settings-content .shortcut_table .shortcut_table-header {\n  color: var(--link-default-color);\n  height: 30px;\n}\n.page .settings-content .shortcut_table .shortcut_table-function {\n  flex: 0 140px;\n  padding: 0 10px;\n  box-sizing: border-box;\n}\n.page .settings-content .shortcut_table .shortcut_table-key {\n  flex: 0 200px;\n  margin-right: 20px;\n  box-sizing: border-box;\n}\n.page .settings-content .shortcut_table .shortcut_table-globalkey {\n  flex: 0 240px;\n  box-sizing: border-box;\n}\n.page\n  .settings-content\n  .shortcut_table\n  .shortcut_table-line\n  .shortcut_table-key {\n  border: solid 1px var(--button-border-color);\n  border-radius: 5px;\n  padding: 0 10px;\n  height: 30px;\n  display: flex;\n  align-items: center;\n}\n.page\n  .settings-content\n  .shortcut_table\n  .shortcut_table-line\n  .shortcut_table-globalkey {\n  border: solid 1px var(--button-border-color);\n  border-radius: 5px;\n  height: 30px;\n  padding: 0 10px;\n  display: flex;\n  align-items: center;\n  box-sizing: border-box;\n}\n\n.page .settings-content .custom-proxy {\n  margin-top: 10px;\n}\n.page .settings-content .custom-proxy .rule-input {\n  margin-top: 8px;\n}\n.page .settings-content .custom-proxy input {\n  margin-right: 15px;\n  height: 24px;\n  width: 200px;\n}\n.page .settings-content .custom-proxy input:focus {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n.page .settings-content .search-description,\n.page .settings-content p {\n  margin: 0 0 25px 0;\n}\n.page .settings-content .search-source-list {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n  line-height: 30px;\n}\n.page .settings-content .search-source-list .search-source {\n  display: flex;\n  align-items: center;\n  width: 130px;\n}\n.page .settings-content .search-source-list .search-source svg {\n  width: 18px;\n  height: 18px;\n  margin-right: 4px;\n}\n/* page setting end */\n\n.loading_bottom {\n  display: block;\n  width: 40px;\n  margin: 0 auto;\n}\n\nsvg.searchspinner {\n  width: 20px;\n  height: 20px;\n  vertical-align: top;\n  margin-left: 15px;\n}\n/* footer start */\n\n.footer {\n  height: 100px;\n  display: flex;\n  align-items: flex-end;\n  z-index: 130;\n\n  margin: 1vh 1vw;\n  border-radius: 10px;\n  position: fixed;\n  bottom: 0;\n  width: 98vw;\n  transition: 0.5s;\n  color: var(--text-default-color);\n}\n\n.footer.footerdef {\n  opacity: 0;\n  bottom: -140px;\n  transition: 0.5s;\n}\n.footer .footer-main {\n  position: relative;\n  z-index: 140;\n  height: 100px;\n  border-radius: 10px;\n  display: flex;\n  flex: 1;\n  transition: 0.5s;\n  backdrop-filter: saturate(180%) blur(20px);\n  background-color: var(--nav-background-color);\n  border: 1px solid rgba(255, 255, 255, 0.08);\n  box-shadow: 0px 0px 16px rgb(0 0 0 / 10%);\n  border-top: solid 1px var(--line-default-color);\n}\n.footer .footer-main.slidedown {\n  height: calc(98vh - 2px);\n}\n.footer .footerwrap {\n  width: 100%;\n  display: flex;\n  height: 100px;\n  position: absolute;\n  bottom: 0;\n}\n.footer .left-control {\n  flex: 0 0 36%;\n  display: flex;\n  align-items: center;\n  overflow: hidden;\n  transition: 0.5s;\n  opacity: 1;\n}\n.footer .left-control.slidedown {\n  flex: 0 0 0;\n  opacity: 0;\n  transform: scaleX(0);\n}\n.footer .left-control .icon {\n  display: flex;\n  font-size: 22px;\n  border-radius: 10px;\n  padding: 7px;\n  margin: 37px;\n  transition: all 0.3s;\n  background: transparent;\n}\n.footer .left-control .icon:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n.footer .left-control .icon.playlistactive {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n.footer .left-control .splitter {\n  height: 20px;\n  width: 1px;\n  display: inline-block;\n  background: #a9a9a9;\n}\n\n.footer .left-control .icon.play {\n  margin-right: 10px;\n}\n\n.footer .left-control .icon.play {\n  color: var(--player-icon-color);\n}\n.footer .left-control .icon.play:hover {\n  color: var(--player-icon-hover-color);\n}\n\n.footer .main-info {\n  flex: 1;\n  display: flex;\n  z-index: 1;\n  justify-content: center;\n  align-items: center;\n  flex-direction: column;\n  z-index: 110;\n}\n\n.footer .main-info .logo-banner {\n  text-align: center;\n  flex: 1;\n  display: flex;\n  align-items: center;\n}\n\n.footer .main-info .logo-banner svg.logo {\n  height: 48px;\n  width: 48px;\n  fill: #666666;\n  stroke: #666666;\n  margin: 0 auto;\n}\n.footer .circlemark {\n  display: flex;\n  justify-content: center;\n  width: 100px;\n  height: 50px;\n  position: absolute;\n  top: 45px;\n  z-index: -1;\n  overflow: hidden;\n  transform-origin: top center;\n}\n.rotatemark {\n  animation: rotatemark 1s 1 forwards ease-in-out;\n}\n.circlmark {\n  animation: circlmark 1s 1 forwards ease-in-out;\n}\n.rotatecircl {\n  animation: rotatecircl 16s 0.5s infinite forwards linear;\n}\n.lipause {\n  animation-play-state: paused;\n}\n.liplay {\n  animation-play-state: running;\n}\n/* .circlopacity{\n  animation:circlopacity 1s forwards;\n} */\n@keyframes rotatemark {\n  0% {\n    transform-origin: top center;\n    transform: rotate(0deg);\n  }\n  50% {\n    transform-origin: top center;\n    transform: rotate(180deg);\n  }\n  100% {\n    transform-origin: top center;\n    transform: rotate(360deg);\n  }\n}\n@keyframes circlmark {\n  0% {\n    transform: rotate(0deg);\n  }\n  50% {\n    transform: rotate(180deg);\n  }\n  100% {\n    transform: rotate(360deg);\n  }\n}\n/* @keyframes circlopacity{\n  0%{\n    opacity: 1;\n  }\n  50%{\n    opacity: 0;\n  }\n  100%{\n    opacity: 1;\n  }\n} */\n@keyframes rotatecircl {\n  0% {\n    transform: rotate(0deg);\n  }\n  100% {\n    transform: rotate(360deg);\n  }\n}\n.footer .cover {\n  height: 90px;\n  width: 90px;\n  object-fit: cover;\n  position: relative;\n  color: transparent;\n  top: -30px;\n  display: flex;\n  justify-content: center;\n}\n.footer .cover ul,\n.footer .cover .cover-list {\n  width: 220px;\n  height: 90px;\n  position: absolute;\n}\n.footer .cover .cover-list span {\n  bottom: 0;\n  cursor: pointer;\n  transition: 0.3s;\n  color: var(--white--black);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  opacity: 0;\n}\n.footer .cover .cover-list span:hover,\n.footer .cover .cover-list span.show {\n  opacity: 1;\n  background-color: var(--white--black-background);\n}\n.footer .cover li {\n  transition: all 0.3s;\n  left: 0;\n  bottom: 0;\n  height: 45px;\n  width: 30px;\n}\n.footer .cover li.hid {\n  opacity: 0;\n  z-index: -1;\n  height: 45px;\n  width: 32px;\n  display: none;\n  float: left;\n  position: absolute;\n  overflow: hidden;\n  transition: all 0.3s;\n  border-radius: 16px;\n  left: 0;\n  bottom: 0;\n}\n.footer .cover li.def {\n  display: block;\n}\n.footer .cover ul .a,\n.footer .cover-list .a {\n  height: 45px;\n  width: 32px;\n  left: 0;\n  position: absolute;\n  overflow: hidden;\n  border-radius: 16px;\n  opacity: 1;\n  z-index: 100;\n  display: block;\n}\n.footer .cover ul .b,\n.footer .cover-list .b {\n  height: 90px;\n  width: 90px;\n  left: 65px;\n  position: absolute;\n  overflow: hidden;\n  border-radius: 50%;\n  opacity: 1;\n  z-index: 101;\n  display: block;\n}\n.footer .cover ul .c,\n.footer .cover-list .c {\n  height: 45px;\n  width: 32px;\n  left: 190px;\n  position: absolute;\n  overflow: hidden;\n  border-radius: 16px;\n  opacity: 1;\n  z-index: 99;\n  display: block;\n}\n.footer .cover img {\n  height: 100%;\n  width: 100%;\n  object-fit: cover;\n  /* border-radius: 50%; */\n  /* position: absolute; */\n  /* border: 1px solid transparent; */\n  box-sizing: border-box;\n}\n.footer .cover .circle {\n  width: 100px;\n  height: 100px;\n  position: relative;\n  top: -50px;\n  z-index: -1;\n  overflow: hidden;\n  transition: all 0.1s linear;\n}\n.footer .circlemark .topmark {\n  width: 100px;\n  height: 50px;\n  /* position: absolute; */\n  z-index: -1;\n  overflow: hidden;\n}\n.footer .cover .top {\n  width: 96px;\n  height: 96px;\n  z-index: -1;\n  border-radius: 50%;\n  border: 2px solid;\n  border-color: var(--text-default-color);\n}\n.footer .cover .bottom {\n  width: 100px;\n  height: 50px;\n  /* position: absolute; */\n  overflow: hidden;\n}\n.footer .cover .bottom .bottomcircle {\n  width: 96px;\n  height: 96px;\n  transform: translateY(-50px);\n  z-index: -1;\n  border-radius: 50%;\n  border: 2px solid;\n  border-color: var(--footer-player-bar-background-color);\n}\n\n.footer .footertime {\n  padding-bottom: 15px;\n  font-size: 12px;\n  flex: 0;\n  cursor: default;\n  font-weight: 500;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  flex-direction: column;\n  transition: 0.3s;\n  width: 100%;\n  max-width: 30vw;\n}\n.footer .footertime:hover {\n  padding: 0;\n}\n.footer .footertime:hover .timeswitch {\n  display: none;\n}\n.footer .footertime:hover .bottomprogressbar {\n  display: flex;\n}\n\n.footer .left-control .detail {\n  max-width: 356px;\n  margin-left: 37px;\n  position: relative;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n.footer .left-control .detail .ctrl {\n  position: absolute;\n  right: 0px;\n  top: 4px;\n  padding-right: 6px;\n}\n.footer .left-control .detail .ctrl:first-of-type .icon {\n  margin-right: 5px;\n}\n.footer .left-control .detail .ctrl .icon {\n  color: var(--text-default-color);\n  opacity: 0.5;\n}\n.footer .left-control .detail .ctrl .icon:hover {\n  opacity: 1;\n}\n\n.footer .left-control .detail .title {\n  font-size: 14px;\n  color: var(--text-default-color);\n  min-width: 0px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  margin: 5px 0;\n  font-size: 18px;\n  font-weight: 600;\n}\n\n.footer .left-control .detail .more-info {\n  margin: 5px 0;\n  display: flex;\n  color: var(--text-subtitle-color);\n}\n\n.footer .left-control .detail .more-info .singer {\n  flex: 1;\n  font-size: 12px;\n  min-width: 0px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n.footer .left-control .detail .more-info .singer a {\n  cursor: pointer;\n}\n\n.footer .left-control .detail .more-info .current {\n  width: 50px;\n  font-size: 12px;\n}\n\n.footer .left-control .detail .more-info .total {\n  width: 50px;\n  text-align: right;\n  font-size: 12px;\n}\n.footer .main-info .bottomprogressbar svg {\n  cursor: default;\n}\n.footer .main-info .volume-ctrl .icon svg {\n  cursor: pointer;\n}\n.footer .main-info .bottomprogressbar {\n  justify-content: center;\n  align-items: center;\n  flex-wrap: nowrap;\n  width: 100%;\n  display: none;\n}\n.footer .main-info .playbar {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  width: 50%;\n}\n.footer .main-info .playbar .playbar-clickable {\n  margin: 5px 10px 5px 0;\n  padding: 5px 0;\n  flex: 1;\n  cursor: pointer;\n}\n.footer .main-info .barbg {\n  height: 3px;\n  background: var(--footer-player-bar-background-color);\n}\n\n.footer .main-info .barbg .cur {\n  height: 100%;\n  background: var(--footer-player-bar-cur-background-color);\n  position: relative;\n}\n.footer .main-info .playbar .playbar-clickable:hover .cur,\n.footer .main-info .m-pbar:hover .barbg .cur {\n  background: var(--theme-color);\n}\n.footer .main-info .barbg .cur .btn {\n  background: var(--footer-player-bar-cur-button-color);\n  height: 8px;\n  width: 2px;\n  position: absolute;\n  right: -2px;\n  top: -5px;\n  transition: 0.3s;\n}\n\n.footer .main-info .playbar .playbar-clickable:hover .barbg .cur .btn,\n.footer .main-info .m-pbar:hover .barbg .cur .btn {\n  width: 10px;\n  height: 10px;\n  border-radius: 5px;\n  top: -3px;\n}\n\n.volume-ctrl {\n  width: 50%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.bottomprogressbar .icon {\n  flex: 0 0 24px;\n  color: var(--text-default-color);\n  cursor: default;\n  padding: 7px;\n  display: flex;\n}\n.volume-ctrl .m-pbar {\n  flex: 1;\n  margin: 5px 0;\n  padding: 5px 0;\n  cursor: pointer;\n}\n\n.footer .menu-modal {\n  border-radius: 10px;\n  transition: 0.3s;\n  left: 0;\n  right: 0;\n  top: 0;\n  position: fixed;\n  opacity: 0;\n  background: var(--shadow-mask);\n}\n.footer .menu-modal.slideup {\n  bottom: 0px;\n  opacity: 1;\n  transition: 0.3s;\n}\n\n.footer .menu {\n  border-radius: 10px;\n  position: absolute;\n  z-index: 120;\n  bottom: 120px;\n  height: 0;\n  opacity: 0;\n  box-sizing: border-box;\n  border: 1px solid var(--white--black);\n\n  left: 0;\n  -webkit-app-region: no-drag;\n  transition: all 0.3s;\n  overflow: hidden;\n  width: 530px;\n  -webkit-backdrop-filter: saturate(180%) blur(20px);\n  backdrop-filter: saturate(180%) blur(20px);\n  background-color: var(--nav-background-color);\n  box-shadow: 0px 0px 16px rgb(0 0 0 / 10%);\n  padding-bottom: 20px;\n}\n.footer .menu.slideup {\n  bottom: 125px;\n  height: 500px;\n  opacity: 1;\n  box-sizing: border-box;\n  border: 1px solid rgba(255, 255, 255, 0.08);\n}\n\n.footer .menu .menu-header {\n  height: 30px;\n  display: flex;\n  align-items: center;\n  color: #9e9e9e;\n  padding: 30px;\n  user-select: none;\n}\n\n.footer .menu .menu-header .menu-title {\n  flex: 1;\n  padding: 0 20px;\n  font-size: 24px;\n  font-weight: 600;\n  color: var(--text-default-color);\n}\n\n.footer .menu .menu-header .add-all {\n  border-right: solid 1px #e5e5e5;\n  flex: 0 0 auto;\n  display: flex;\n  align-items: center;\n  padding-right: 10px;\n  font-size: 14px;\n}\n\n.footer .menu .menu-header .remove-all {\n  margin-left: 10px;\n  flex: 0 0 auto;\n  display: flex;\n  align-items: center;\n  font-size: 14px;\n}\n.footer .menu .menu-header .remove-all:hover .ng-binding,\n.footer .menu .menu-header .add-all:hover .ng-binding {\n  text-decoration: underline;\n  color: var(--theme-color);\n}\n.footer .menu .menu-header .remove-all:hover,\n.footer .menu .menu-header .add-all:hover {\n  text-decoration: none;\n  color: var(--theme-color);\n}\n\n.footer .menu .menu-header .close {\n  margin-left: 15px;\n  flex: 0 0 25px;\n  align-items: center;\n  cursor: pointer;\n  color: var(--icon-default-color);\n}\n.footer .menu .menu-header .close:hover {\n  color: var(--theme-color);\n}\n.footer .menu .menu-header .add-all span,\n.footer .menu .menu-header .remove-all span {\n  cursor: pointer;\n}\n\n.footer .menu .menu-header .add-all .icon,\n.footer .menu .menu-header .remove-all .icon {\n  margin-right: 7px;\n  width: 18px;\n  height: 18px;\n}\n\n.footer .menu .menu-header .close svg {\n  margin-right: 3px;\n  width: 20px;\n  height: 20px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.footer .menu ul.menu-list {\n  overflow-y: scroll;\n  height: 370px;\n  padding: 0 30px;\n  font-size: 14px;\n}\n\n.footer .menu ul.menu-list li {\n  border-radius: 10px;\n  display: flex;\n  align-items: center;\n  height: 30px;\n  position: relative;\n  margin-bottom: -2px;\n  padding: 10px 20px 10px 0;\n  transition: 0.3s;\n}\n\n.footer .menu ul.menu-list li:hover {\n  background: var(--songlist-hover-background-color);\n}\n\n.footer .menu ul.menu-list li.playing {\n  color: var(--important-color);\n  background: var(--songlist-hover-background-color);\n}\n.footer .menu ul.menu-list li .song-status-icon {\n  flex: 0 0 30px;\n  width: 20px;\n  height: 30px;\n  text-align: center;\n  display: flex;\n  align-items: center;\n}\n.footer .menu ul.menu-list li .song-status-icon svg {\n  width: 10px;\n  height: 10px;\n  fill: var(--important-color);\n  stroke: var(--important-color);\n  flex: 1;\n}\n.footer .menu ul.menu-list li .song-title {\n  flex: 2;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  font-size: 15px;\n  font-weight: 400;\n  padding-right: 10px;\n}\n.footer .menu ul.menu-list li .song-title.disabled {\n  color: #777777;\n}\n.footer .menu ul.menu-list li .song-title a {\n  cursor: pointer;\n}\n\n.footer .menu ul.menu-list li .song-singer {\n  flex: 1;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  cursor: pointer;\n  padding: 0 10px;\n  font-weight: 300;\n}\n\n.footer .menu ul.menu-list li .tools {\n  flex: 0 0 42px;\n  width: 42px;\n}\n.footer .menu ul.menu-list li .tools .icon {\n  cursor: pointer;\n  opacity: 0.5;\n}\n.footer .menu ul.menu-list li .tools .icon:first-of-type {\n  margin-right: 5px;\n}\n.footer .menu ul.menu-list li .tools .icon:hover {\n  opacity: 1;\n}\n\n.footer .menu ul.menu-list li .song-time {\n  flex: 1;\n  text-align: right;\n}\n\n.footer .right-control {\n  flex: 0 0 36%;\n  display: flex;\n  align-items: center;\n  justify-content: flex-end;\n}\n\n.footer .right-control .playlist-toggle {\n  cursor: pointer;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  margin-right: 37px;\n  padding: 7px;\n}\n.footer .right-control .ctrl {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.footer .right-control .ctrl a {\n  margin-right: 32px;\n  padding: 7px;\n  display: flex;\n  transition: 0.3s;\n  border-radius: 10px;\n}\n.footer .right-control .ctrl a:hover {\n  text-decoration: none;\n  background-color: var(--songlist-hover-background-color);\n}\n.footer .right-control .translate-switch {\n  border-radius: 10px;\n  display: flex;\n  cursor: pointer;\n  -webkit-app-region: no-drag;\n\n  height: 0px;\n  box-sizing: border-box;\n  width: 0px;\n  transition: 0.3s;\n  overflow: hidden;\n  margin: 0;\n  padding: 0;\n}\n.footer .right-control .translate-switch.slidedown {\n  padding: 7px;\n  margin-right: 32px;\n  height: 35px;\n  box-sizing: border-box;\n  width: 32px;\n}\n.footer .right-control .translate-switch:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n.footer .right-control .translate-switch.selected {\n  color: var(--theme-color);\n}\n.footer .right-control .mask {\n  margin-right: 32px;\n  padding: 7px;\n  display: flex;\n  transition: 0.3s;\n  border-radius: 50%;\n}\n.footer .right-control .mask.slidedown {\n  transform: rotate(180deg);\n}\n.footer .right-control .mask:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n\n.footer .right-control .playlist-toggle .icon {\n  color: var(--player-right-icon-color);\n}\n\n.footer .right-control .playlist-toggle .icon:hover {\n  color: var(--player-right-icon-hover-color);\n}\n\n.footer .right-control .lyric-toggle {\n  margin-right: 32px;\n  padding: 7px;\n  display: flex;\n  cursor: pointer;\n  transition: 0.3s;\n  border-radius: 10px;\n}\n.footer .right-control .lyric-toggle:hover {\n  background-color: var(--songlist-hover-background-color);\n}\n\n/* footer end */\n\n/* dialog start */\n.shadow {\n  position: fixed;\n  background: var(--shadow-mask);\n  _position: absolute;\n  z-index: 9999;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n  background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);\n}\n\n.dialog {\n  position: absolute;\n  top: 120px;\n  width: 400px;\n  height: 430px;\n  z-index: 10000;\n  overflow: hidden;\n  border-radius: 4px;\n  background-color: var(--dialog-background-color);\n  color: var(--dialog-text-color);\n  box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n}\n\n.dialog-header {\n  height: 30px;\n  font-size: 24px;\n  font-weight: bold;\n  text-align: center;\n  padding: 20px;\n}\n\n.dialog-header .dialog-close {\n  float: right;\n  font-size: 26px;\n  cursor: pointer;\n  margin-top: -10px;\n}\n\n.dialog-body {\n  padding: 0 20px;\n  height: 370px;\n  overflow-y: auto;\n  background-color: var(--dialog-background-color);\n}\n\n.dialog-body .buttons {\n  display: flex;\n  justify-content: center;\n  margin-top: 20px;\n}\n\n.dialog-body .buttons button,\n.dialog-body .dialog-footer button {\n  margin-right: 20px;\n\n  font-size: 16px;\n  font-weight: 500;\n  transition: 0.2s;\n}\n.dialog-body .buttons .btn-primary {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n.dialog-body .buttons button:hover,\n.dialog-body .dialog-footer button:hover {\n  transform: scale(1.2);\n}\n.dialog .detail-songlist li:hover {\n  background-color: #e3e3e5;\n  cursor: pointer;\n}\n.dialog-body input {\n  width: calc(100% - 20px);\n  height: 30px;\n  border-radius: 10px;\n  background: #eee;\n  color: #666;\n  border: transparent;\n  padding: 5px 10px;\n  margin-bottom: 10px;\n  font-weight: 500;\n}\n.dialog-body input:focus {\n  background-color: var(--theme-color-hover);\n  color: var(--theme-color);\n}\n\n.dialog-playlist,\n.dialog-backuplist,\n.dialog-merge-playlist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.dialog-playlist li,\n.dialog-backuplist li {\n  cursor: pointer;\n  height: 48px;\n  padding: 6px;\n  border-radius: 10px;\n}\n.dialog-merge-playlist li {\n  cursor: pointer;\n  height: 48px;\n  padding: 10px;\n  border-radius: 10px;\n  transition: 0.2s;\n}\n.dialog-backuplist li {\n  height: 96px;\n  padding: 6px;\n}\n\n.dialog-playlist li:hover,\n.dialog-backuplist li:hover,\n.dialog-merge-playlist li:hover {\n  background-color: var(--dialog-highlight-color);\n}\n\n.dialog-playlist li img,\n.dialog-backuplist li img,\n.dialog-merge-playlist li img {\n  float: left;\n  height: 48px;\n  width: 48px;\n  object-fit: cover;\n}\n\n.dialog-playlist li h2,\n.dialog-backuplist li h2,\n.dialog-merge-playlist li h2 {\n  margin: 0 0 0 58px;\n  font-size: 13px;\n  font-weight: inherit;\n}\n.dialog-backuplist li h2 {\n  margin-top: 0;\n}\n.dialog-newplaylist {\n  padding: 10px;\n}\n\n.dialog-newbackup {\n  text-align: center;\n}\n\n.dialog-editplaylist label,\n.dialog-open-url label {\n  font-size: 18px;\n  font-weight: 500;\n  padding: 10px 0;\n  display: block;\n  height: 30px;\n  line-height: 30px;\n}\n\n.dialog-editplaylist .dialog-footer {\n  position: absolute;\n  bottom: 20px;\n}\n.dialog-body .field-name {\n  margin: 10px 0 5px 0;\n}\n\n/* dialog end */\n\n/* widget source-list start */\n.searchbox {\n  margin-bottom: 150px;\n  transition: 0.3s;\n}\n.searchbox.footerdef {\n  margin-bottom: 0;\n}\n.source-list {\n  margin: 10px 26px 24px 26px;\n  user-select: none;\n  display: flex;\n  align-items: center;\n  flex-wrap: nowrap;\n}\n\n.source-list .source-button {\n  display: inline-block;\n  cursor: pointer;\n  transition: 0.1s;\n}\n.source-list .source-button:hover,\n.source-list .source-button.active {\n  transition: 0.2s;\n  padding: 0;\n}\n.source-list .source-button a:hover {\n  text-decoration: none;\n}\n\n.source-list .source-button.active .buttontext,\n.source-list .source-button:hover .buttontext,\n.source-list .source-button .buttontext:hover,\n.source-list .source-button .buttontext.active {\n  color: var(--text-default-color);\n  -webkit-app-region: no-drag;\n  font-size: 24px;\n  font-weight: 700;\n  text-decoration: none;\n  border-radius: 10px;\n  padding: 6px 10px;\n  transition: all 0.2s, background 0.3s;\n  -webkit-user-drag: none;\n  margin: 0px 12px;\n  white-space: nowrap;\n}\n.source-list .source-button.active .buttontext,\n.source-list .source-button .buttontext.active {\n  border-bottom: solid 2px var(--text-default-color);\n}\n.source-list .source-button:hover .buttontext,\n.source-list .source-button .buttontext:hover {\n  background-color: var(--button-background-color);\n}\n.source-list .source-button .buttontext {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  -webkit-line-clamp: 1;\n  -webkit-box-orient: vertical;\n  font-size: 14px;\n  margin: 4px 10px;\n  color: var(--text-subtitle-color);\n  transition: 0.1s;\n}\n\n.source-list .splitter {\n  display: inline-block;\n  background: #a9a9a9;\n  margin-top: 1px;\n  height: 12px;\n  width: 1px;\n}\n.source-list .search-type {\n  float: right;\n  flex: 1;\n  display: flex;\n  align-items: center;\n  flex-wrap: nowrap;\n  justify-content: flex-end;\n}\n/* widget source-list end */\n\n/* widget playlist-filter start */\n\n.playlist-filter {\n  display: flex;\n  flex-wrap: wrap;\n  line-height: 38px;\n  margin: 0 26px 0px 26px;\n}\n\n.playlist-filter .filter-item {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  line-height: 20px;\n  padding: 8px 16px;\n  margin: 10px 16px 6px 0;\n  color: var(--black--white);\n  font-weight: 600;\n  font-size: 16px;\n  border-radius: 10px;\n  transition: all 0.2s;\n}\n\n.playlist-filter .filter-item.active,\n.playlist-filter .filter-item:hover {\n  background: var(--theme-color-hover);\n  color: var(--theme-color);\n  transition: all 0.2s;\n}\n\n/* widget playlist-filter end */\n\n/* widget all-playlist-filter start */\n.all-playlist-filter {\n  margin: 10px 26px 0px 26px;\n  border-radius: 10px;\n  padding: 8px;\n  background-color: var(--button-background-color);\n}\n\n.all-playlist-filter .category {\n  margin-bottom: 32px;\n  display: flex;\n}\n\n.all-playlist-filter .category .category-title {\n  font-size: 24px;\n  font-weight: 700;\n  opacity: 0.68;\n  margin-left: 24px;\n  min-width: 54px;\n  height: 26px;\n  margin-top: 8px;\n  /* color: var(--icon-default-color); */\n}\n.all-playlist-filter .category .category-filters {\n  margin-left: 24px;\n  display: flex;\n  flex-wrap: wrap;\n  /* color: var(--icon-default-color); */\n}\n.all-playlist-filter .category .category-filters .filter-item {\n  min-width: 98px;\n  margin-top: 4px;\n  display: flex;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  align-items: center;\n  font-weight: 500;\n  font-size: 16px;\n  transition: 0.2s;\n}\n.all-playlist-filter .category .category-filters .filter-item span {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  cursor: pointer;\n  padding: 6px 10px;\n  height: 26px;\n  border-radius: 12px;\n  opacity: 0.88;\n  transition: 0.2s;\n}\n.all-playlist-filter .category .category-filters .filter-item span:hover {\n  opacity: 1;\n  background-color: var(--theme-color-hover);\n  border-radius: var(--default-border-radius);\n  transition: 0.2s;\n  color: var(--theme-color);\n}\n.all-playlist-filter .category .category-filters .filter-item span.active {\n  color: var(--theme-color);\n}\n\n/* widget all-playlist-filter end */"
  },
  {
    "path": "css/cover.css",
    "content": "/*\n * Globals\n */\n\n/* Links */\na,\na:focus,\na:hover {\n  color: #fff;\n}\n\n/* Custom default button */\n.btn-default,\n.btn-default:hover,\n.btn-default:focus {\n  color: #333;\n  text-shadow: none; /* Prevent inheritence from `body` */\n  background-color: #fff;\n  border: 1px solid #fff;\n}\n\n/*\n * Base structure\n */\n\nhtml,\nbody {\n  height: 100%;\n  background-color: #333;\n}\nbody {\n  color: #fff;\n  overflow: hidden !important;\n  /*  text-align: center;*/\n  /*text-shadow: 0 1px 3px rgba(0,0,0,.5);*/\n}\n\n/* Extra markup and styles for table-esque vertical and horizontal centering */\n.site-wrapper {\n  display: table;\n  width: 100%;\n  height: 100%; /* For at least Firefox */\n  min-height: 100%;\n  /*  -webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5);\n          box-shadow: inset 0 0 100px rgba(0,0,0,.5);*/\n}\n.site-wrapper-inner {\n  display: table-cell;\n  vertical-align: top;\n}\n.cover-container {\n  margin-right: auto;\n  margin-left: auto;\n}\n\n/* Padding for spacing */\n.inner {\n  padding: 30px;\n}\n\n/*\n * Header\n */\n.masthead-brand {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n\n.masthead-nav > li {\n  display: inline-block;\n}\n.masthead-nav > li + li {\n  margin-left: 20px;\n}\n.masthead-nav > li > a {\n  padding-right: 0;\n  padding-left: 0;\n  font-size: 16px;\n  font-weight: bold;\n  color: #fff; /* IE8 proofing */\n  color: rgba(255, 255, 255, 0.75);\n  border-bottom: 2px solid transparent;\n}\n.masthead-nav > li > a:hover,\n.masthead-nav > li > a:focus {\n  background-color: transparent;\n  border-bottom-color: #a9a9a9;\n  border-bottom-color: rgba(255, 255, 255, 0.25);\n}\n.masthead-nav > .active > a,\n.masthead-nav > .active > a:hover,\n.masthead-nav > .active > a:focus {\n  color: #fff;\n  border-bottom-color: #fff;\n}\n\n@media (min-width: 768px) {\n  .masthead-brand {\n    float: left;\n  }\n  .masthead-nav {\n    float: right;\n  }\n}\n\n/*\n * Cover\n */\n\n.cover {\n  padding: 0 20px;\n}\n.cover .btn-lg {\n  padding: 10px 20px;\n  font-weight: bold;\n}\n\n/*\n * Footer\n */\n\n.mastfoot {\n  color: #999; /* IE8 proofing */\n  color: rgba(255, 255, 255, 0.5);\n}\n\n/*\n * Affix and center\n */\n\n@media (min-width: 768px) {\n  /* Pull out the header and footer */\n  .masthead {\n    position: fixed;\n    top: 0;\n  }\n  .mastfoot {\n    position: fixed;\n    bottom: 0;\n  }\n  /* Start the vertical centering */\n  .site-wrapper-inner {\n    vertical-align: middle;\n  }\n  /* Handle the widths */\n  .masthead,\n  .mastfoot,\n  .cover-container {\n    width: 100%; /* Must be percentage or pixels for horizontal alignment */\n  }\n}\n\n@media (min-width: 992px) {\n  .masthead,\n  .mastfoot,\n  .cover-container {\n    width: 880px;\n  }\n}\n\n/*\n * Scroll bar\n *\n */\n:root {\n  --scrollWidth: 10px;\n}\n\n::-webkit-scrollbar {\n  width: var(--scrollWidth);\n  height: var(--scrollWidth);\n}\n::-webkit-scrollbar-button {\n  width: 0;\n  height: 0;\n}\n::-webkit-scrollbar-button:start:increment,\n::-webkit-scrollbar-button:end:decrement {\n  display: none;\n}\n::-webkit-scrollbar-corner {\n  display: block;\n}\n\n::-webkit-scrollbar-track,\n::-webkit-scrollbar-thumb {\n  border-radius: 8px;\n  border-right: 1px solid transparent;\n  border-left: 1px solid transparent;\n}\n\n::-webkit-scrollbar-thumb {\n  background-color: rgba(255, 255, 255, 0.2);\n}\n::-webkit-scrollbar-thumb:hover {\n  background-color: rgba(255, 255, 255, 0.3);\n}\n::-webkit-scrollbar-track:hover {\n  background-color: rgba(255, 255, 255, 0.08);\n}\n"
  },
  {
    "path": "css/hotkeys.css",
    "content": "/*! \n * angular-hotkeys v1.7.0\n * https://chieffancypants.github.io/angular-hotkeys\n * Copyright (c) 2016 Wes Cruver\n * License: MIT\n */\n.cfp-hotkeys-container {\n  display: table !important;\n  position: fixed;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n  color: #333;\n  font-size: 1em;\n  background-color: rgba(255, 255, 255, 0.9);\n}\n\n.cfp-hotkeys-container.fade {\n  z-index: -1024;\n  visibility: hidden;\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n  -moz-transition: opacity 0.15s linear;\n  -o-transition: opacity 0.15s linear;\n  transition: opacity 0.15s linear;\n}\n\n.cfp-hotkeys-container.fade.in {\n  z-index: 10002;\n  visibility: visible;\n  opacity: 1;\n}\n\n.cfp-hotkeys-title {\n  font-weight: bold;\n  text-align: center;\n  font-size: 1.2em;\n}\n\n.cfp-hotkeys {\n  width: 100%;\n  height: 100%;\n  display: table-cell;\n  vertical-align: middle;\n}\n\n.cfp-hotkeys table {\n  margin: auto;\n  color: #333;\n}\n\n.cfp-content {\n  display: table-cell;\n  vertical-align: middle;\n}\n\n.cfp-hotkeys-keys {\n  padding: 5px;\n  text-align: right;\n}\n\n.cfp-hotkeys-key {\n  display: inline-block;\n  color: #fff;\n  background-color: #333;\n  border: 1px solid #333;\n  border-radius: 5px;\n  text-align: center;\n  margin-right: 5px;\n  box-shadow: inset 0 1px 0 #666, 0 1px 0 #bbb;\n  padding: 5px 9px;\n  font-size: 1em;\n}\n\n.cfp-hotkeys-text {\n  padding-left: 10px;\n  font-size: 1em;\n}\n\n.cfp-hotkeys-close {\n  position: fixed;\n  top: 20px;\n  right: 20px;\n  font-size: 2em;\n  font-weight: bold;\n  padding: 5px 10px;\n  border: 1px solid #ddd;\n  border-radius: 5px;\n  min-height: 45px;\n  min-width: 45px;\n  text-align: center;\n}\n\n.cfp-hotkeys-close:hover {\n  background-color: #fff;\n  cursor: pointer;\n}\n\n@media all and (max-width: 500px) {\n  .cfp-hotkeys {\n    font-size: 0.8em;\n  }\n}\n\n@media all and (min-width: 750px) {\n  .cfp-hotkeys {\n    font-size: 1.2em;\n  }\n}\n"
  },
  {
    "path": "css/icon.css",
    "content": "@font-face {\n  font-family: 'listen1-icon';\n  src: url('../fonts/listen1-icon.eot?4ftssm');\n  src: url('../fonts/listen1-icon.eot?4ftssm#iefix') format('embedded-opentype'),\n    url('../fonts/listen1-icon.ttf?4ftssm') format('truetype'),\n    url('../fonts/listen1-icon.woff?4ftssm') format('woff'),\n    url('../fonts/listen1-icon.svg?4ftssm#listen1-icon') format('svg');\n  font-weight: normal;\n  font-style: normal;\n}\n\n[class^='li-'],\n[class*=' li-'] {\n  /* use !important to prevent issues with browser extensions that change fonts */\n  font-family: 'listen1-icon' !important;\n  speak: none;\n  font-style: normal;\n  font-weight: normal;\n  font-variant: normal;\n  text-transform: none;\n  line-height: 1;\n\n  /* Better Font Rendering =========== */\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.li-play:before {\n  content: '\\e900';\n}\n.li-previous:before {\n  content: '\\e901';\n}\n.li-next:before {\n  content: '\\e902';\n}\n.li-pause:before {\n  content: '\\e903';\n}\n.li-random-loop:before {\n  content: '\\e904';\n}\n.li-single-cycle:before {\n  content: '\\e905';\n}\n.li-mute:before {\n  content: '\\e906';\n}\n.li-volume:before {\n  content: '\\e907';\n}\n.li-list:before {\n  content: '\\e908';\n}\n.li-loop:before {\n  content: '\\e909';\n}\n.li-del:before {\n  content: '\\e90a';\n}\n.li-close:before {\n  content: '\\e90b';\n}\n.li-back:before {\n  content: '\\e90c';\n}\n.li-play-s:before {\n  content: '\\e90d';\n}\n.li-collapse:before {\n  content: '\\e90e';\n}\n.li-add:before {\n  content: '\\e90f';\n}\n.li-advance:before {\n  content: '\\e910';\n}\n.li-link:before {\n  content: '\\e911';\n}\n.li-setting:before {\n  content: '\\e912';\n}\n.li-songlist:before {\n  content: '\\e913';\n}\n.li-featured-list:before {\n  content: '\\e914';\n}\n"
  },
  {
    "path": "css/iparanoid.css",
    "content": "/* global settings (theme related) */\n:root {\n  --icon-default-color: #666666;\n  --icon-highlight-color: #111111;\n\n  --text-default-color: #111111;\n  --text-subtitle-color: #666666;\n  --text-disable-color: #999999;\n\n  --lyric-default-color: #666666;\n  --lyric-on-cover-color: #333333;\n  --lyric-important-color: #ff4444;\n  --lyric-important-on-cover-color: #ffffff;\n\n  --link-default-color: #999999;\n  --link-highlight-color: #111111;\n  --link-active-color: #323232;\n  --link-inactive-color: #a9a9a9;\n\n  --line-default-color: #e5e5e5;\n\n  --sidebar-background-color: rgba(245, 245, 245, 0.7);\n  --sidebar-highlight-background-color: #4d4d4d;\n  --sidebar-highlight-text-color: #ffffff;\n  --sidebar-hover-background-color: #5f5f5f;\n  --sidebar-hover-text-color: #ffffff;\n\n  --content-background-color: #ffffff;\n\n  --foot-background-color: #ffffff;\n  --foot-border-color: #e5e5e5;\n  --footer-main-background-color: #f2f2f2;\n  --footer-player-bar-background-color: #e0e0e0;\n  --footer-player-bar-cur-background-color: #666666;\n  --footer-header-background-color: #e7e7e7;\n  --footer-menu-even-background-color: #f7f7f7;\n  --footer-menu-hover-background-color: #e5e5e5;\n  --search-input-background-color: #f2f2f2;\n  --footer-player-bar-cur-button-color: #111111;\n\n  --window-control-border-color: #dddddd;\n\n  --important-color: #ff4444;\n\n  --button-background-color: #eeeeee;\n  --button-border-color: #bebebe;\n  --button-hover-background-color: #dddddd;\n\n  --now-playing-page-background-color: #ffffff;\n  --now-playing-close-icon-color: #666666;\n\n  --disable-song-title-color: #b7b7b7;\n  --windows-border-color: #dddddd;\n\n  --default-border-radius: 2px;\n  --text-default-size: 13px;\n  --h2-title-font-size: 24px;\n  --badge-font-size: 12px;\n  --badge-font-color: #c75449;\n  --badge-border-color: #d98e87;\n  --songlist-odd-background-color: #f5f5f5;\n  --songlist-border-color: #f4f4f4;\n  --songlist-hover-background-color: #eeeeee;\n\n  --player-left-icon-color: #b1b1b1;\n  --player-icon-color: #666666;\n  --player-icon-hover-color: #666666;\n  --player-right-icon-color: #333333;\n  --player-right-icon-hover-color: #000000;\n\n  --dialog-highlight-color: #e3e3e5;\n  --dialog-background-color: #fafafa;\n  --dialog-text-color: #565656;\n\n  --volume-icon-color: #333333;\n  --volume-bar-background-color: #e0e0e0;\n  --volume-bar-current-background-color: #333333;\n\n  --scroll-color: #c2c2c2;\n\n  --lyric-icon-background-color: #ffffff;\n  --lyric-font-size: 15px;\n  --lyric-line-margin: 20px;\n}\n"
  },
  {
    "path": "css/iparanoid2.css",
    "content": "/* global settings (theme related) */\n:root {\n  --icon-default-color: #000;\n\n  --text-default-color: #000;\n  --text-subtitle-color: #7a7a7b;\n  /* --text-disable-color: #999999; */\n\n  --lyric-default-color: #666666;\n  --lyric-on-cover-color: #333333;\n  --lyric-important-on-cover-color: #ffffff;\n\n  --link-default-color: #999999;\n  --link-active-color: #323232;\n\n  --line-default-color: #e5e5e5;\n\n  --sidebar-highlight-background-color: #f2f2f3;\n  --sidebar-hover-background-color: #f2f2f3;\n  --sidebar-hover-text-color: #262626;\n\n  --content-background-color: #ffffff;\n\n  --footer-player-bar-cur-background-color: #666666;\n  --search-input-background-color: #f2f2f3;\n\n  --footer-player-bar-cur-button-color: #111111;\n  --footer-player-bar-background-color: #e0e0e0;\n\n  --window-control-border-color: #dddddd;\n\n  --important-color: #017afe;\n\n  --button-background-color: #f5f5f7;\n  --button-border-color: #bebebe;\n  --button-hover-background-color: #dddddd;\n\n  --now-playing-close-icon-color: #666666;\n\n  --disable-song-title-color: #b7b7b7;\n  --windows-border-color: #dddddd;\n\n  --default-border-radius: 10px;\n  --text-default-size: 13px;\n  --h2-title-font-size: 24px;\n  --badge-font-size: 12px;\n  --songlist-odd-background-color: #f5f5f5;\n  --songlist-hover-background-color: #f2f2f7;\n\n  --player-icon-color: #666666;\n  --player-icon-hover-color: #666666;\n  --player-right-icon-color: #333333;\n  --player-right-icon-hover-color: #000000;\n\n  --dialog-highlight-color: #e3e3e5;\n  --dialog-background-color: #fafafa;\n  --dialog-text-color: #565656;\n\n  --volume-icon-color: #333333;\n\n  --scroll-color: #c2c2c2;\n\n  --lyric-icon-background-color: #ffffff;\n\n  --footer-background-color: hsla(0, 0%, 100%, 0.86);\n  --nav-background-color: hsla(0, 0%, 100%, 0.86);\n  --color-body-bg: #fff;\n  --white--black: #fff;\n  --black--white: #222;\n  --white--black-background: #222;\n\n  --theme-color: #017afe;\n  --theme-color-ope: rgba(1, 122, 254, 0.4);\n  --theme-color-hover: #f0f7ff;\n\n  --sidebar-background: #f2f2f7;\n  --sidebar-button-background: #fff;\n  --sidebar-splitter: #1d1d1f;\n\n  --shadow-mask: rgba(30, 30, 30, 0.5);\n}"
  },
  {
    "path": "css/notyf_custom.css",
    "content": ".notyf__toast {\n  border-radius: 10px;\n}\n\n.notyf__dismiss-btn {\n  outline: 0;\n}\n"
  },
  {
    "path": "css/origin.css",
    "content": "/* global settings (theme related) */\n:root {\n  --icon-default-color: #666666;\n  --icon-highlight-color: #111111;\n\n  --text-default-color: #ffffff;\n  --text-subtitle-color: #666666;\n  --text-disable-color: #999999;\n\n  --lyric-default-color: #666666;\n  --lyric-on-cover-color: #bbbbbb;\n  --lyric-important-color: #ffffff;\n  --lyric-important-on-cover-color: #ffffff;\n\n  --link-default-color: #999999;\n  --link-highlight-color: #ffffff;\n  --link-active-color: #ffffff;\n  --link-inactive-color: rgba(255, 255, 255, 0.75);\n\n  --line-default-color: #333333;\n\n  --sidebar-background-color: #2e2e2e;\n  --sidebar-highlight-background-color: #4d4d4d;\n  --sidebar-highlight-text-color: #ffffff;\n  --sidebar-hover-background-color: #3c3c3c;\n  --sidebar-hover-text-color: #ffffff;\n\n  --content-background-color: #333333;\n\n  --foot-background-color: #222222;\n  --foot-border-color: #222222;\n  --footer-main-background-color: #222222;\n  --footer-player-bar-background-color: #666666;\n  --footer-player-bar-cur-background-color: #e0e0e0;\n  --footer-header-background-color: #333333;\n  --footer-menu-even-background-color: #2d2d2d;\n  --footer-menu-hover-background-color: #333333;\n  --search-input-background-color: #222222;\n  --footer-player-bar-cur-button-color: #e0e0e0;\n\n  --window-control-border-color: #dddddd;\n\n  --important-color: #fff;\n\n  --button-background-color: #222222;\n  --button-border-color: #222222;\n  --button-hover-background-color: #444444;\n\n  --now-playing-page-background-color: #333333;\n  --now-playing-close-icon-color: #b3b3b3;\n\n  --disable-song-title-color: #b7b7b7;\n  --windows-border-color: #333333;\n\n  --default-border-radius: 2px;\n  --text-default-size: 13px;\n  --h2-title-font-size: 24px;\n  --badge-font-size: 12px;\n  --badge-font-color: #c3473a;\n  --badge-border-color: #843932;\n  --songlist-odd-background-color: #2d2d2d;\n  --songlist-border-color: transparent;\n  --songlist-hover-background-color: #3e3e3e;\n\n  --player-left-icon-color: #b3b3b3;\n  --player-icon-color: #b3b3b3;\n  --player-icon-hover-color: #eeeeee;\n  --player-right-icon-color: #b3b3b3;\n  --player-right-icon-hover-color: #eeeeee;\n  --player-icon-hightlight-color: #eeeeee;\n\n  --dialog-highlight-color: #444444;\n  --dialog-background-color: #333;\n  --dialog-text-color: #ffffff;\n\n  --volume-icon-color: #b3b3b3;\n  --volume-bar-background-color: #333333;\n  --volume-bar-current-background-color: #e0e0e0;\n\n  --scroll-color: #444444;\n\n  --lyric-icon-background-color: #222222;\n  --lyric-font-size: 15px;\n  --lyric-line-margin: 20px;\n}\n"
  },
  {
    "path": "css/origin2.css",
    "content": "/* global settings (theme related) */\n:root {\n  --icon-default-color: #666666;\n\n  --text-default-color: #ffffff;\n  --text-subtitle-color: #7a7a7b;\n\n  --lyric-default-color: #666666;\n  --lyric-on-cover-color: #bbbbbb;\n  --lyric-important-on-cover-color: #ffffff;\n\n  --link-default-color: #999999;\n  --link-active-color: #ffffff;\n\n  --line-default-color: rgba(255, 255, 255, 0.08);\n\n  --sidebar-highlight-background-color: #4d4d4d;\n  --sidebar-hover-background-color: #3c3c3c;\n  --sidebar-hover-text-color: #ffffff;\n\n  --content-background-color: #222;\n\n  --footer-player-bar-background-color: #666666;\n  --footer-player-bar-cur-background-color: #e0e0e0;\n  --search-input-background-color: #323232;\n  --footer-player-bar-cur-button-color: #e0e0e0;\n\n  --window-control-border-color: #dddddd;\n\n  --important-color: #017afe;\n\n  --button-background-color: #323232;\n  --button-border-color: #323232;\n  --button-hover-background-color: #444444;\n\n  --now-playing-close-icon-color: #b3b3b3;\n\n  --disable-song-title-color: #b7b7b7;\n  --windows-border-color: #222;\n\n  --default-border-radius: 10px;\n  --text-default-size: 13px;\n  --h2-title-font-size: 24px;\n  --badge-font-size: 12px;\n  --songlist-odd-background-color: #2d2d2d;\n  --songlist-hover-background-color: #3e3e3e;\n\n  --player-icon-color: #b3b3b3;\n  --player-icon-hover-color: #eeeeee;\n  --player-right-icon-color: #b3b3b3;\n  --player-right-icon-hover-color: #eeeeee;\n\n  --dialog-highlight-color: #444444;\n  --dialog-background-color: #222;\n  --dialog-text-color: #ffffff;\n\n  --volume-icon-color: #b3b3b3;\n\n  --scroll-color: #444444;\n\n  --lyric-icon-background-color: #323232;\n\n  --nav-background-color: rgba(34, 34, 34, 0.86);\n  --color-body-bg: #222;\n  --white--black: #222;\n  --black--white: #fff;\n  --white--black-background: #fff;\n\n  --theme-color: #017afe;\n  --theme-color-ope: rgba(1, 122, 254, 0.4);\n  --theme-color-hover: #bbcdff;\n\n  --sidebar-background: #1D1D1F;\n  --sidebar-button-background: #323232;\n  --sidebar-splitter: #ebebec;\n\n  --shadow-mask: rgba(0, 0, 0, 0.5);\n}"
  },
  {
    "path": "css/player.css",
    "content": "a {\n  cursor: pointer;\n}\n\n.shadow {\n  position: fixed;\n  background: rgba(30, 30, 30, 0.5);\n  _position: absolute;\n  z-index: 9999;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n  background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);\n}\n\n.dialog {\n  position: absolute;\n  top: 120px;\n  width: 480px;\n  height: 420px;\n  z-index: 10000;\n  overflow: hidden;\n  border-radius: 4px 4px 4px 4px;\n  padding: 20px;\n  background-color: #333;\n}\n\n.dialog-header {\n  width: 100%;\n  height: 30px;\n  font-family: Arial, Helvetica, sans-serif;\n  font-size: 15px;\n  font-weight: bold;\n  text-align: left;\n  border-bottom: 1px solid;\n  margin-bottom: 20px;\n}\n\n.dialog-header .dialog-close {\n  float: right;\n  font-size: 33px;\n  cursor: pointer;\n  margin-top: -15px;\n}\n\n.dialog-body {\n  width: 100%;\n  height: 320px;\n  overflow-y: auto;\n  background-color: #333;\n}\n\n/*.masthead {\n    z-index: 999;\n}*/\n\n.masthead,\n.mastfoot {\n  margin: 0 auto;\n  left: 0;\n  right: 0;\n  z-index: 999;\n  background-color: #222222;\n}\n\n.masthead {\n  background-color: #333;\n  height: 90px;\n}\n\n.masthead .logo {\n  float: left;\n  height: 50px;\n  width: 50px;\n  margin-right: 20px;\n  cursor: pointer;\n}\n\n.masthead .masthead-brand {\n  color: rgba(255, 255, 255, 1);\n  cursor: pointer;\n}\n\n.cover-container {\n  position: relative;\n}\n\n.container-placeholder {\n  margin-top: 90px;\n}\n\n.site-wrapper {\n  width: 100%;\n  overflow: hidden;\n  position: absolute;\n  padding-left: 17px;\n}\n\n.site-wrapper-innerd {\n  overflow-y: scroll;\n  margin-top: 90px;\n  /* uncomment the line below will hide the scroll bar */\n  /*padding-right: 17px;*/\n  box-sizing: content-box;\n  width: 100%;\n  background-color: #333;\n}\n\n.searchbox {\n  /*    margin-top: 100px;*/\n}\n\n.searchbox .nav {\n  margin-top: 12px;\n}\n\n.searchbox .nav .searchspinner {\n  margin-top: 8px;\n  height: 25px;\n}\n\n.searchitem {\n  height: 92px;\n}\n\n.searchitem img {\n  float: left;\n  height: 90px;\n  width: 90px;\n}\n\n.searchitem div {\n  float: left;\n  margin-left: 48px;\n  margin-top: 38px;\n  width: 400px;\n}\n\n.playlist-covers {\n  margin-bottom: 0px;\n}\n\n.playlist-covers li {\n  float: left;\n  display: inline-block;\n  width: 140px;\n  height: 188px;\n  margin-right: 22px;\n}\n\n.playlist-covers .desc {\n  text-align: left;\n}\n\n.playlist-covers .u-cover {\n  position: relative;\n  display: block;\n  width: 140px;\n  height: 140px;\n}\n\n.playlist-covers .u-cover .bottom {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  width: 100%;\n  height: 27px;\n  color: #ccc;\n}\n\n.playlist-covers .loading_bottom {\n  clear: both;\n  text-align: center;\n}\n\n.playlist-covers .loading_bottom img {\n  height: 35px;\n}\n\n.u-cover .mask {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.u-cover img {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.u-cover .icon-play {\n  position: absolute;\n  right: 10px;\n  bottom: 5px;\n  width: 16px;\n  height: 17px;\n  background: url(../images/player_directplay.png);\n}\n\n.playlist-covers .u-cover .bottom .nb {\n  float: left;\n  margin: 7px 0 0 0;\n}\n\n.m-playbar {\n  position: absolute;\n  zoom: 1;\n  top: -90px;\n  left: 0;\n  width: 100%;\n  height: 90px;\n  margin: 0 auto;\n  background-color: #222222;\n}\n\n.m-playbar .btns {\n  width: 157px;\n  padding: 27px 0 0 19px;\n}\n\n.m-playbar .btns,\n.m-playbar .head,\n.m-playbar .play,\n.m-playbar .volum,\n.m-playbar .oper {\n  float: left;\n}\n\n.m-pbar .btn i {\n  visibility: hidden;\n  position: absolute;\n  left: 5px;\n  top: 5px;\n  width: 12px;\n  height: 12px;\n  background: url(../images/loading.gif);\n}\n\n.m-pbar .barbg,\n.m-pbar .cur,\n.m-pbar .left {\n  background: url(../images/statbar.png) no-repeat 0 9999px;\n  _background-image: url(../images/statbar.png);\n}\n\n.m-playbar .btns a {\n  background: url(../images/player_large.png) no-repeat 0 9999px;\n  _background-image: url(../images/player_large.png);\n  cursor: pointer;\n}\n\n.m-playbar .btns a {\n  display: block;\n  float: left;\n  width: 36px;\n  height: 36px;\n  margin-right: 8px;\n  margin-top: 0px;\n  text-indent: -9999px;\n}\n\n.m-playbar .btns .previous {\n  background-position: -72px 0px;\n}\n\n.m-playbar .btns .previous:hover {\n  background-position: -72px -36px;\n}\n\n.m-playbar .btns .play {\n  width: 36px;\n  height: 36px;\n  margin-top: 0;\n  background-position: 0px 0px;\n}\n\n.m-playbar .btns .play:hover {\n  background-position: 0px -36px;\n}\n\n.m-playbar .btns .pas {\n  background-position: -108px 0px;\n}\n\n.m-playbar .btns .pas:hover {\n  background-position: -108px -36px;\n}\n\n.m-playbar .btns .next {\n  /* pause icon distance adjust from 36 to 38 */\n  background-position: -38px 0px;\n}\n\n.m-playbar .btns .next:hover {\n  background-position: -38px -36px;\n}\n\n.m-playbar .head {\n  position: relative;\n  margin: 10px 15px 0 0;\n}\n\n.m-playbar .head,\n.m-playbar .head img {\n  width: 70px;\n  height: 70px;\n}\n\n.m-playbar .head .mask {\n  position: absolute;\n  top: 0px;\n  left: 0px;\n  display: block;\n  width: 70px;\n  height: 70px;\n}\n\n.m-playbar .maininfo {\n  float: none;\n  margin-left: 245px;\n  margin-right: 120px;\n}\n\n.m-playbar .words .notextdeco {\n  text-decoration: none;\n}\n\n.m-playbar .words {\n  margin-top: 14px;\n  height: 28px;\n  overflow: hidden;\n  color: #e8e8e8;\n  text-shadow: 0 1px 0 #171717;\n  line-height: 28px;\n}\n\n.m-playbar .words .name {\n  max-width: 300px;\n}\n\n.m-playbar .words .by {\n  max-width: 220px;\n  margin-left: 15px;\n  color: #9b9b9b;\n}\n\n.m-playbar .words .by a {\n  color: #9b9b9b;\n}\n\n.m-playbar .words .src {\n  cursor: pointer;\n  float: left;\n  width: 25px;\n  height: 25px;\n  margin: 2px 0 0 13px;\n  background: url(../images/player_small.png) no-repeat 0 9999px;\n  background-position: -100px 0px;\n}\n\n.m-playbar .words .src:hover {\n  background-position: -100px -25px;\n}\n\n.m-playbar .words .fc1 {\n  color: #e8e8e8;\n  margin-left: 3px;\n}\n\n.overflowhide {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.floatleft {\n  float: left;\n}\n\n.m-pbar {\n  position: relative;\n}\n\n.m-pbar.play {\n  width: 80%;\n  margin-top: 14px;\n}\n\n.m-pbar.volume {\n  position: absolute;\n  right: 13px;\n  bottom: 11px;\n  float: right;\n  width: 56%;\n  margin-top: 0px;\n}\n\n.m-pbar .barbg,\n.m-pbar .cur,\n.m-pbar .rdy {\n  height: 7px;\n}\n\n.m-pbar .barbg {\n  background-position: right 0;\n}\n\n.m-pbar .cur {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 1%;\n  background-position: left -9px;\n}\n\n.m-pbar .btn {\n  position: absolute;\n  top: -8px;\n  right: -13px;\n  width: 22px;\n  height: 24px;\n  margin-left: -11px;\n  background: url(../images/progress_indicator.png) no-repeat;\n}\n\n.m-playbar .time {\n  position: absolute;\n  right: -122px;\n  top: -6px;\n}\n\n.m-playbar .time {\n  float: right;\n  margin-right: 20px;\n  color: #797979;\n  text-shadow: 0 1px 0 #121212;\n}\n\n.m-playbar .time em {\n  color: #a1a1a1;\n}\n\nem,\ni {\n  font-style: normal;\n  text-align: left;\n  font-size: inherit;\n}\n\n.m-playbar a {\n  background: url(../images/player_small.png) no-repeat 0 9999px;\n}\n\n.m-playbar .ctrl {\n  position: absolute;\n  right: 0px;\n  bottom: 48px;\n  z-index: 10;\n  width: 103px;\n  padding-left: 13px;\n  float: none;\n}\n\n.m-playbar .icn-add {\n  background-position: -25px 0px;\n}\n\n.m-playbar .icn-add:hover {\n  background-position: -25px -25px;\n}\n\n.m-playbar .icn-list {\n  background-position: -125px 0px;\n}\n\n.m-playbar .icn-vol {\n  background-position: -175px 0px;\n}\n\n.m-playbar .icn-vol-mute {\n  background-position: -200px 0px;\n}\n\n.m-playbar .icn-list:hover {\n  background-position: -125px -25px;\n}\n\n.m-playbar .icn-loop {\n  background-position: -50px 0px;\n}\n\n.m-playbar .icn-loop:hover {\n  background-position: -50px -25px;\n}\n\n.m-playbar .icn-vol:hover {\n  background-position: -175px -25px;\n}\n\n.m-playbar .icn-vol-mute:hover {\n  background-position: -200px -25px;\n}\n\n.m-playbar .icn-shuffle {\n  background-position: -150px 0px;\n}\n\n.m-playbar .icn-shuffle:hover {\n  background-position: -150px -25px;\n}\n\n.m-playbar .icn-repeatone {\n  background-position: -225px 0px;\n}\n\n.m-playbar .icn-repeatone:hover {\n  background-position: -225px -25px;\n}\n\n.m-playbar .icn {\n  float: left;\n  width: 25px;\n  height: 25px;\n  margin: 11px 2px 0 0;\n  text-indent: -9999px;\n}\n\n.m-playbar .icn-add {\n  margin-right: 5px;\n}\n\n.m-playbar .menu {\n  position: absolute;\n  bottom: 90px;\n  _bottom: 90px;\n  right: 0px;\n  _right: 0px;\n  height: 349px;\n  width: 60%;\n  background-color: #121212;\n}\n\n.m-playbar .menu ul {\n  display: inline-block;\n  padding-left: 0px;\n  height: 308px;\n  overflow-y: scroll;\n  margin-bottom: 0px;\n}\n\n.m-playbar .menu li {\n  float: left;\n  width: 100%;\n  display: block;\n}\n\n.m-playbar .menu .lyric {\n  text-align: center;\n  width: 39%;\n  display: inline-block;\n  height: 308px;\n  overflow-y: scroll;\n  position: relative;\n}\n\n.m-playbar .menu .lyric p {\n  min-height: 20px;\n}\n\n.m-playbar .menu .lyric .placeholder {\n  height: 50px;\n}\n\n.m-playbar .menu .lyric .highlight {\n  font-size: 15px;\n  color: #ffffff;\n}\n\n.m-playbar .menu .playing {\n  background-color: #555555;\n}\n\n.m-playbar .menu li:hover,\n.m-playbar .menu li:focus {\n  background-color: #999999;\n}\n\n.m-playbar .menu .icn-remove {\n  height: 20px;\n  width: 20px;\n  background-position: -75px -25px;\n  display: inline-block;\n}\n\n.m-playbar .menu .icn-remove:hover {\n  background-position: -75px -25px;\n}\n\n.volume-ctrl {\n  position: absolute;\n  right: 0px;\n  bottom: 16px;\n  width: 110px;\n}\n\nli {\n  list-style: none;\n}\n\n.menu-header {\n  height: 40px;\n  background-color: #222222;\n  padding-top: 4px;\n  text-align: center;\n}\n\n.menu-header span {\n  position: absolute;\n  left: 19px;\n  top: 7px;\n  font-size: 18px;\n  color: #ffffff;\n}\n\n.menu-header small {\n  background-color: #333333;\n  color: #ffffff;\n  cursor: pointer;\n  vertical-align: middle;\n  display: inline-block;\n  width: 60px;\n  line-height: 20px;\n}\n\n.menu-header a:hover small {\n  background-color: #ffffff;\n  color: #333333;\n}\n.menu .add-all {\n  display: inline-block;\n  position: absolute;\n  left: 335px;\n  top: 9px;\n}\n\n.menu .remove-all {\n  display: inline-block;\n  position: absolute;\n  left: 410px;\n  top: 9px;\n}\n\n.menu .close-popup {\n  float: right;\n  margin-right: 14px;\n  font-size: 20px;\n  text-decoration: none;\n  color: #aaaaaa;\n}\n\n.menu .close-popup:hover {\n  color: #ffffff;\n}\n\n.menu .title {\n  width: 300px;\n  float: left;\n  height: 28px;\n  padding-top: 3px;\n  text-align: left;\n  padding-left: 20px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  cursor: pointer;\n}\n\n.menu .singer {\n  width: 180px;\n  float: right;\n  height: 28px;\n  padding-top: 3px;\n  text-align: left;\n  padding-left: 20px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  cursor: pointer;\n}\n\n.dbimport {\n  /*margin-top: 100px;*/\n}\n\n.form-signin {\n  width: 300px;\n  margin-left: auto;\n  margin-right: auto;\n  text-align: center;\n}\n\n.form-signin .form-control,\n.form-signin .valid-img,\n.form-signin .btn {\n  margin-top: 10px;\n}\n\n.form-signin .valid-img {\n  height: 40px;\n  width: 220px;\n}\n\n.form-signin .security-notice {\n  margin-top: 10px;\n}\n\n.playlist-detail {\n  position: absolute;\n  text-align: left;\n  background-color: #333;\n  width: 100%;\n}\n\n.playlist-detail .detail-head {\n  width: 200px;\n  position: fixed;\n  margin-bottom: 20px;\n}\n\n.playlist-detail .detail-head-cover {\n  height: 180px;\n  /*    width: 225px;*/\n  float: left;\n  margin: 10px;\n}\n\n.playlist-detail .detail-head-cover img {\n  max-width: 100%;\n  max-height: 100%;\n}\n\n.detail-head-title {\n  float: left;\n  width: 100%;\n  text-align: center;\n}\n\n.detail-head-title a {\n  display: inline-block;\n  text-indent: -9999px;\n  width: 36px;\n  height: 36px;\n  margin-top: 0;\n  background: url(../images/player_large.png) no-repeat 0 9999px;\n}\n\n.detail-head-title .play {\n  background-position: 0px 0px;\n}\n\n.detail-head-title .play:hover {\n  background-position: 0px -36px;\n}\n\n.detail-head-title .add {\n  background-position: -216px 0px;\n}\n\n.detail-head-title .add:hover {\n  background-position: -216px -36px;\n}\n\n.detail-head-title .link {\n  background-position: -250px 0px;\n}\n\n.detail-head-title .link:hover {\n  background-position: -250px -36px;\n}\n\n.detail-head-title .edit {\n  background-position: -288px 0px;\n}\n\n.detail-head-title .edit:hover {\n  background-position: -288px -36px;\n}\n\n.detail-head-title .clone {\n  background-position: -144px 0px;\n}\n\n.detail-head-title .clone:hover {\n  background-position: -144px -36px;\n}\n\n.detail-head-title .merge {\n  background-position: -324px 0px;\n}\n\n.detail-head-title .merge:hover {\n  background-position: -324px -36px;\n}\n\n.detail-head-title .ply:hover {\n  background-position: -40px -204px;\n}\n\n.playlist-detail .detail-head-title h2 {\n  font-size: 17px;\n  margin-bottom: 35px;\n}\n\n.playlist-detail .detail-songlist {\n  margin-left: 220px;\n  margin-top: 6px;\n  margin-right: 14px;\n}\n\n.playsong-detail .detail-head {\n  width: 390px;\n  position: fixed;\n  margin-bottom: 20px;\n}\n\n.playsong-detail .detail-songinfo {\n  padding-left: 440px;\n  padding-right: 55px;\n}\n\n.playsong-detail .detail-head .detail-head-cover {\n  margin: 0 auto;\n  width: 200px;\n}\n\n.playsong-detail .detail-head .detail-head-cover img {\n  width: 240px;\n  height: 240px;\n}\n\n.playsong-detail .detail-songinfo h2 {\n  font-size: 22px;\n}\n\n.playsong-detail .detail-songinfo .info {\n  border-bottom: solid #444 1px;\n  margin-bottom: 5px;\n  padding-bottom: 10px;\n}\n.playsong-detail .detail-songinfo .info span {\n  color: #9b9b9b;\n  margin-right: 10px;\n}\n.playsong-detail .detail-songinfo .info span.album {\n  margin-left: 30px;\n}\n.playsong-detail .lyric {\n  color: #999; /* IE8 proofing */\n  color: rgba(255, 255, 255, 0.5);\n  text-align: left;\n  width: 100%;\n  display: inline-block;\n  height: 410px;\n  overflow-y: scroll;\n  position: relative;\n  font-size: 15px;\n}\n\n.playsong-detail .lyric p {\n  min-height: 20px;\n}\n\n.playsong-detail .lyric .placeholder {\n  height: 50px;\n}\n\n.playsong-detail .lyric .highlight {\n  color: #ffffff;\n}\n\n.detail-songlist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.detail-songlist li {\n  float: left;\n  width: 100%;\n  display: block;\n  padding: 10px;\n}\n\n.detail-songlist .col2 {\n  float: left;\n  width: 28%;\n  margin-left: 2%;\n  font-size: 15px;\n}\n\n.detail-songlist .col1 {\n  float: left;\n  width: 19%;\n  margin-left: 2%;\n}\n\n.detail-songlist .disabled {\n  color: #777777;\n}\n\n.detail-songlist .col-add {\n  float: left;\n  width: 75px;\n  margin-left: 2%;\n}\n\n.detail-songlist .detail-tools {\n  float: right;\n  height: 21px;\n  position: relative;\n  width: 118px;\n}\n\n.detail-songlist .detail-tools a {\n  background: url(../images/player_small.png) no-repeat 0 9999px;\n  height: 25px;\n  width: 25px;\n  cursor: pointer;\n}\n\n.detail-songlist .detail-tools .detail-add-button {\n  background-position: 0px 0px;\n}\n\n.detail-songlist .detail-tools .detail-fav-button {\n  background-position: -25px 0px;\n}\n\n.detail-songlist .detail-tools .detail-delete-button {\n  background-position: -75px 0px;\n}\n\n.detail-songlist .detail-tools .source-button {\n  background-position: -100px 0px;\n}\n\n.detail-songlist .detail-tools .detail-add-button:hover {\n  background-position: 0px -25px;\n}\n\n.detail-songlist .detail-tools .detail-fav-button:hover {\n  background-position: -25px -25px;\n}\n\n.detail-songlist .detail-tools .detail-delete-button:hover {\n  background-position: -75px -25px;\n}\n\n.detail-songlist .detail-tools .source-button:hover {\n  background-position: -100px -25px;\n}\n\n.detail-songlist .detail-tools a {\n  text-decoration: none;\n  display: inline-block;\n}\n\n.detail-songlist .detail-artist a {\n  color: #777777;\n}\n\n.detail-songlist .odd {\n  background-color: #333;\n}\n\n.detail-songlist .even,\n.detail-songlist .detail-add {\n  background-color: #2d2d2d;\n}\n\n.dialog .detail-songlist li:hover {\n  background-color: #999999;\n  cursor: pointer;\n}\n\n/*.playlist-detail .detail-songlist li:hover {\n    background-color: #999999;\n}*/\n\n.playlist-detail .btn {\n  width: 88px;\n  margin-top: 0;\n  float: left;\n}\n\n.cover-container .detail-close {\n  position: absolute;\n  right: -32px;\n  top: 0px;\n}\n\n.cover-container .detail-close span {\n  font-size: 34px;\n  cursor: pointer;\n  color: #aaaaaa;\n}\n\n.cover-container .detail-close span:hover {\n  color: #ffffff;\n}\n\n.dialog-playlist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.dialog-playlist li {\n  cursor: pointer;\n  height: 112px;\n  padding: 6px;\n}\n\n.dialog-playlist li:hover {\n  background-color: #555555;\n}\n\n.dialog-playlist li img {\n  float: left;\n  height: 100px;\n  width: 100px;\n}\n\n.dialog-playlist li h2 {\n  margin-left: 125px;\n  font-size: 17px;\n}\n\n.dialog-backuplist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.dialog-backuplist li {\n  cursor: pointer;\n  height: 112px;\n  padding: 6px;\n}\n\n.dialog-backuplist li:hover {\n  background-color: #555555;\n}\n\n.dialog-backuplist li img {\n  float: left;\n  height: 100px;\n  width: 100px;\n}\n\n.dialog-backuplist li h2 {\n  margin-top: 10px;\n  margin-left: 125px;\n  font-size: 15px;\n}\n\n.dialog-merge-playlist {\n  padding-left: 0px;\n  text-align: left;\n}\n\n.dialog-merge-playlist li {\n  cursor: pointer;\n  height: 112px;\n  padding: 6px;\n}\n\n.dialog-merge-playlist li:hover {\n  background-color: #555555;\n}\n\n.dialog-merge-playlist li img {\n  float: left;\n  height: 100px;\n  width: 100px;\n}\n\n.dialog-merge-playlist li h2 {\n  margin-left: 125px;\n  font-size: 17px;\n}\n\n.dialog-newplaylist input {\n  margin-bottom: 22px;\n}\n\n.dialog-newplaylist .confirm-button {\n  margin-left: 76px;\n  margin-right: 96px;\n}\n\n.dialog-newbackup {\n  text-align: center;\n}\n\n.dialog-newbackup .confirm-button {\n  margin-right: 12px;\n}\n\n.dialog-editplaylist .dialog-footer {\n  position: absolute;\n  bottom: 20px;\n}\n\n.dialog-editplaylist .confirm-button,\n.dialog-open-url .confirm-button {\n  margin-right: 82px;\n  margin-left: 93px;\n}\n\n.dialog-connect-lastfm .buttons {\n  margin-top: 30px;\n}\n\n.dialog-connect-lastfm .confirm-button {\n  margin-left: 40px;\n  margin-right: 48px;\n}\n\n.source-list {\n  position: absolute;\n  right: -32px;\n  top: 0px;\n  z-index: 10;\n  text-align: center;\n}\n\n.source-list div {\n  background-color: #333333;\n  color: #ffffff;\n  height: 35px;\n  border: 1px solid #ffffff;\n  width: 75px;\n  cursor: pointer;\n  vertical-align: middle;\n  padding-top: 6px;\n}\n\n.source-list div:first-child:not(:last-child) {\n  border-top-left-radius: 4px;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.source-list div:last-child:not(:first-child) {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.source-list .active {\n  background-color: #e6e6e6;\n  color: #333333;\n}\n\n.source-list div:hover {\n  background-color: #ffffff;\n  color: #333333;\n}\n\n.source-list .open-url-button {\n  border-radius: 4px;\n}\n\n.settings-title {\n  font-size: 20px;\n  padding: 20px;\n  border-bottom: 2px solid #aaaaaa;\n}\n\n.settings-content {\n  padding: 20px;\n}\n\n.settings-content .btn {\n  margin-right: 10px;\n}\n\n.btn-group button,\n.btn-pagination,\n.btn-pagination:focus {\n  background-color: #333333;\n  color: #ffffff;\n  border-color: #333333;\n}\n\n.btn-group button:hover,\n.btn-pagination:hover {\n  background-color: #ffffff;\n  color: #333333;\n}\n\n.searchbox li > a:hover {\n  color: #333333;\n}\n\n.search-pagination {\n  text-align: center;\n  display: block;\n  vertical-align: middle;\n  line-height: 45px;\n}\n\n.search-pagination button:focus {\n  outline: 0;\n}\n\n.search-pagination label {\n  margin: 0 15px;\n}\n"
  },
  {
    "path": "css/reset.css",
    "content": "html,\nbody,\nspan,\napplet,\nobject,\niframe,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np,\nblockquote,\npre,\na,\nabbr,\nacronym,\naddress,\nbig,\ncite,\ncode,\ndel,\ndfn,\nem,\nfont,\nins,\nkbd,\nq,\ns,\nsamp,\nsmall,\nstrike,\nstrong,\nsub,\nsup,\ntt,\nvar,\ndl,\ndt,\ndd,\nol,\nul,\nli,\nfieldset,\nform,\nlabel,\nlegend {\n  margin: 0;\n  padding: 0;\n  border: 0;\n  outline: 0;\n  font-weight: normal;\n  font-style: normal;\n  font-size: 100%;\n  vertical-align: baseline;\n}\n\n:focus {\n  outline: 0;\n}\n\nbody {\n  line-height: 1.2;\n  color: black;\n  background: white;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-size: 100%;\n  font-weight: normal;\n}\n\nol,\nul {\n  list-style: none;\n}\n\n/* tables still need 'cellspacing=\"0\"' in the markup */\ntable {\n  border-collapse: separate;\n  border-spacing: 0;\n}\n\ncaption,\nth,\ntd {\n  text-align: left;\n  font-weight: normal;\n}\n\nblockquote:before,\nblockquote:after,\nq:before,\nq:after {\n  content: '';\n}\n\nblockquote,\nq {\n  quotes: '' '';\n}\n\nimg {\n  border: none;\n}\n"
  },
  {
    "path": "i18n/en-US.json",
    "content": "{\n \"HELLO\"        : \"hi\",\n \"_ALL_MUSIC\": \"All Music\",\n \"_NETEASE_MUSIC\": \"Netease\",\n \"_QQ_MUSIC\": \"QQ\",\n \"_XIAMI_MUSIC\": \"Xiami\",\n \"_KUGOU_MUSIC\": \"Kugou\",\n \"_KUWO_MUSIC\": \"Kuwo\",\n \"_BILIBILI_MUSIC\": \"Bilibili\",\n \"_MIGU_MUSIC\": \"Migu\",\n \"_TAIHE_MUSIC\": \"Qianqian\",\n \"_PLATFORM_UNION\": \"Platforms\",\n \"_PLAYLISTS\": \"PLAYLISTS\",\n \"_MY_MUSIC\": \"My Music\",\n \"_CREATED_PLAYLIST\": \"My Playlist\",\n \"_FAVORITED_PLAYLIST\": \"Favorite Playlist\",\n \"_REFRESH_PLAYLIST\": \"Refresh Playlist\",\n \"_FAVORITED\": \"Favorited\",\n \"_FAVORITE\": \"Favorite\",\n \"_PLAY_ALL\": \"Play All\",\n \"_ADD_TO_PLAYLIST\": \"Add to Playlist\",\n \"_EDIT\": \"Edit\",\n \"_IMPORT\": \"Import\",\n \"_IMPORT_PLAYLIST\": \"Import Playlist\",\n \"_ORIGIN_LINK\": \"Origin link\",\n \"_SONGS\": \"Songs\",\n \"_ARTISTS\": \"Artists\",\n \"_ALBUMS\": \"Albums\",\n \"_OPERATION\": \"Tools\",\n \"_CREATE_PLAYLIST\": \"Create Playlist\",\n \"_CANCEL\": \"Cancel\",\n \"_CREATE_AND_ADD_PLAYLIST\": \"Create and Add\",\n \"_REMOVE_FROM_PLAYLIST\": \"Remove\",\n \"_ADD_TO_QUEUE\": \"Add to Queue\",\n \"_ARTIST\": \"Artist\",\n \"_ALBUM\": \"Album\",\n \"_PLAYLIST_TITLE\": \"Playlist Title\",\n \"_PLAYLIST_AUTHOR\": \"Playlist Author\",\n \"_PLAYLIST_SONG_COUNT\": \"Playlist Song Count\",\n \"_PLAYLIST_COVER_IMAGE_URL\": \"Cover Image URL\",\n \"_INPUT_PLAYLIST_TITLE\": \"Input Playlist Title\",\n \"_INPUT_PLAYLIST_COVER_IMAGE_URL\":  \"Input Cover Image URL\",\n \"_EDIT_PLAYLIST\": \"Edit Playlist\",\n \"_REMOVE_PLAYLIST\": \"Remove Playlist\",\n \"_OPENING_LASTFM_PAGE\":\"Opening Last.fm页面...\",\n \"_CONFIRM_NOTICE_LASTFM\": \"Please click \\\"Yes, all access\\\" in new page, allow Listen 1 access your account.\",\n \"_AUTHORIZED_FINISHED\": \"Authorized Finished\",\n \"_AUTHORIZED_REOPEN\": \"I Have Problem, try again\",\n \"_PLAYLIST_LINK\": \"Playlist Link\",\n \"_OPEN_PLAYLIST\": \"Open Playlist\",\n \"_OPENING_GITHUB_PAGE\": \"Opening Github.com页面...\",\n \"_CONFIRM_NOTICE_GITHUB\": \"Please click \\\"Authencate\\\" in new page, allow Listen 1 access your account\",\n \"_CREATE_PLAYLIST_BACKUP\": \"Create Playlist Backup\",\n \"_CREATE_PUBLIC_BACKUP\": \"Create Public Backup\",\n \"_CREATE_PRIVATE_BACKUP\": \"Create Private Backup\",\n \"_BACKUP_PLAYLIST\": \"Backup Playlists\",\n \"_BACKUP_WARNING\": \"Reinstall or clear cache will lost all your playlists, backup is STRONG RECOMMENDED.\",\n \"_EXPORT_TO_LOCAL_FILE\": \"Export to Local File\",\n \"_EXPORT_TO_GITHUB_GIST\": \"Export to Github Gist\",\n \"_RECOVER_PLAYLIST\": \"Recover Playlists\",\n \"_RECOVER_WARNING\": \"Choose Backup File. Notice: It will overwrite current playlists.\",\n \"_RECOVER_FROM_LOCAL_FILE\": \"Recover from Local File\",\n \"_RECOVER_FROM_GITHUB_GIST\": \"Recover from Github Gist\",\n \"_CONNECT_TO_GITHUB\": \"Connect to Github.com\",\n \"_STATUS\": \"Status\",\n \"_RECONNECT\": \"Reconnect\",\n \"_CANCEL_CONNECT\": \"Cancel Connect\",\n \"_SHORTCUTS\": \"Shortcuts\",\n \"_VIEW_SHORTCUTS_LIST\": \"View Shortcuts List\",\n \"_GLOBAL_SHORTCUTS_NOTICE\": \"Enable Global Shortcuts\",\n \"_LYRIC_DISPLAY\": \"Lyric Display\",\n \"_SHOW_DESKTOP_LYRIC\": \"Show desktop lyric\",\n \"_SHOW_LYRIC_TRANSLATION\": \"Show lyric translation\",\n \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"Show lyric translation with desktop lyric\",\n \"_CONNECT_TO_LASTFM\": \"Connect to Last.fm\",\n \"_ABOUT\": \"About\",\n \"_HOMEPAGE\": \"Homepage\",\n \"_EMAIL\": \"Email\",\n \"_FEEDBACK\": \"Feedback\",\n \"_DESIGNER\": \"Designer\",\n \"_VERSION\": \"Version\",\n \"_LATEST_VERSION\": \"Latest Version\",\n \"_LICENSE_NOTICE\": \"(This software is free and open source under MIT license)\",\n \"_TOTAL_SONG_PREFIX\": \"Total \",\n \"_TOTAL_SONG_POSTFIX\": \" Songs\",\n \"_CLEAR_ALL\": \"Clear All\",\n \"_SEARCH_PLACEHOLDER\": \"Search\",\n \"_LANGUAGE\": \"Language\",\n \"_ADD_TO_PLAYLIST_SUCCESS\": \"Success: Add to My Playlist\",\n \"_FAVORITE_PLAYLIST_SUCCESS\": \"Success: Favorite Playlist\",\n \"_EDIT_PLAYLIST_SUCCESS\": \"Success: Edit Playlist\",\n \"_IMPORTING_PLAYLIST\": \"Importing playlists...\",\n \"_IMPORTING_PLAYLIST_SUCCESS\": \"Success: Import Playlists\",\n \"_REMOVE_PLAYLIST_SUCCESS\": \"Success: Remove Playlist\",\n \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"Success: UnFavorite Playlist\",\n \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"Success: Remove Song from Playlist\",\n \"_ADD_TO_QUEUE_SUCCESS\": \"Success: Add to Queue\",\n \"_COPYRIGHT_ISSUE\": \"Fail because copyright issue, Search other platform instead\",\n \"_INPUT_NEW_PLAYLIST_TITLE\": \"Input New Playlist Title\",\n \"_FAIL_OPEN_PLAYLIST_URL\": \"Fail to Open Playlist url\",\n \"_EXAMPLE\": \"Example:\",\n \"_CONFIRM\": \"Confirm\",\n \"_THEME\": \"Theme\",\n \"_THEME_WHITE\": \"White Theme\",\n \"_THEME_BLACK\": \"Black Theme\",\n \"_THEME_MODERN_WHITE\": \"Modern White Theme\",\n \"_THEME_MODERN_BLACK\": \"Modern Black Theme\",\n \"_AUTO_CHOOSE_SOURCE\": \"Auto Choose Source\",\n \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"Enable choose source from other music platform after fail.\",\n \"_AUTO_CHOOSE_SOURCE_LIST\": \"Music Platform to try after fail\",\n \"_NOWPLAYING_DISPLAY\": \"Now Playing Display\",\n \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"Show Cover Image for Now Playing\",\n \"_NOWPLAYING_BITRATE_NOTICE\": \"Show Bitrate\",\n \"_NOWPLAYING_PLATFORM_NOTICE\": \"Show Music Platform\",\n \"_LOCAL_MUSIC\": \"Local Music\",\n \"_ADD_LOCAL_SONGS\": \"Add Local Songs\",\n \"netease\": \"netease\",\n \"bilibili\": \"bili\",\n \"kugou\": \"kugou\",\n \"kuwo\": \"kuwo\",\n \"migu\": \"migu\",\n \"qq\": \"qq\",\n \"xiami\": \"xiami\",\n \"taihe\": \"qianqian\",\n \"localmusic\": \"local music\",\n \"_CLOSE_TAB_ACTION\": \"Close tab action\",\n \"_VALID_AFTER_RESTART\": \"Valid after restart\",\n \"_QUIT_APPLICATION\": \"Quit Application\",\n \"_MINIMIZE_TO_BACKGROUND\": \"Minimize to background\",\n \"_MY_CREATED_PLAYLIST\": \"My Created Playlist\",\n \"_MY_FAVORITE_PLAYLIST\": \"My Favorite Playlist\",\n \"_RECOMMEND_PLAYLIST\": \"Recommend Playlist\",\n \"_LISTEN1_LOGIN_NOTICE\": \"Listen1 WILL NOT transfer your account data to any server other than music platform server.\",\n \"_PASSWORD\": \"Password\",\n \"_LOGIN\": \"Login\",\n \"_LOGOUT\": \"Logout\",\n \"_LOGIN_ERROR\": \"Login error, please check user name and password\",\n \"_LOGIN_EMAIL_ERROR\": \"Please enter correct email address\",\n \"_LOGIN_COUNTRYCODE_ERROR\": \"Please enter correct country code\",\n \"_LOGIN_PHONE_ERROR\": \"Please enter correct phone number\",\n \"_LOGIN_PASSWORD_ERROR\": \"Password can't be empty\",\n \"_LOGIN_NETEASE\": \"Login Netease\",\n \"_LOGIN_BY_MOBILE_PHONE\": \"Login by mobile phone\",\n \"_LOGIN_BY_EMAIL\": \"Login by email\",\n \"_MOBILE_PHONE\": \"Mobile Phone\",\n \"_MY_NETEASE\": \"My Netease\",\n \"_MY_QQ\": \"My QQ Music\",\n \"_FAIL_ALL_NOTICE\": \"No available track in current playlist\",\n \"_SHORTCUTS_FUNCTION\": \"Shortcuts Function\",\n \"_GLOBAL_SHORTCUTS\": \"Global Shortcuts\",\n \"_PLAY_OR_PAUSE\": \"Play/Pause\",\n \"_PREVIOUS_TRACK\": \"Previous Track\",\n \"_NEXT_TRACK\": \"Next Track\",\n \"_VOLUME_UP\": \"Volume Up\",\n \"_VOLUME_DOWN\": \"Volume Down\",\n \"_SHORTCUTS_NOT_SET\": \"N/A\",\n \"_QUICK_SEARCH\": \"Quick Search\",\n \"_SEARCH_PLAYLIST\": \"Search Playlist\",\n \"_KEYBOARD_SPACE\": \"Space\",\n \"_NOT_LOGIN_NICKNAME\": \"Anonymous\",\n \"_LOGIN_DIALOG_NOTICE\": \"Opening Login page, please login in that page and click finish login\",\n \"_LOGIN_SUCCESS\": \"Finish Login\",\n \"_LOGIN_FAIL_RETRY\": \"Something wrong. Reopen Login page\",\n \"_PROXY_SYSTEM\": \"Use System Proxy\",\n \"_PROXY_DIRECT\": \"No Proxy\",\n \"_PROXY_CUSTOM\": \"Custom Proxy\",\n \"_PROXY_CONFIG\": \"Proxy Config\",\n \"_PROXY_NOT_SET\": \"Proxy Not Set\",\n \"_MODIFY\": \"Modify\",\n \"_PROTOCOL\": \"Protocol\",\n \"_HOST\": \"Host\",\n \"_PORT\": \"Port\",\n \"ZOOM_IN_OUT\": \"Zoom In/Out\"\n}\n"
  },
  {
    "path": "i18n/fr-FR.json",
    "content": "{\n \"HELLO\"        : \"Bonjour\",\n \"_ALL_MUSIC\": \"All Music\",\n \"_NETEASE_MUSIC\": \"Netease\",\n \"_QQ_MUSIC\": \"QQ\",\n \"_XIAMI_MUSIC\": \"Xiami\",\n \"_KUGOU_MUSIC\": \"Kugou\",\n \"_KUWO_MUSIC\": \"Kuwo\",\n \"_BILIBILI_MUSIC\": \"Bilibili\",\n \"_MIGU_MUSIC\": \"Migu\",\n \"_TAIHE_MUSIC\": \"Qianqian\",\n \"_PLATFORM_UNION\": \"Platforms\",\n \"_PLAYLISTS\": \"PLAYLISTS\",\n \"_MY_MUSIC\": \"Ma Musique\",\n \"_CREATED_PLAYLIST\": \"Ma Playlist\",\n \"_FAVORITED_PLAYLIST\": \"Favorite Playlist\",\n \"_REFRESH_PLAYLIST\": \"Refresh Playlist\",\n \"_FAVORITED\": \"Favorited\",\n \"_FAVORITE\": \"Favorite\",\n \"_PLAY_ALL\": \"Lire tout\",\n \"_ADD_TO_PLAYLIST\": \"Ajouter à la Playlist\",\n \"_EDIT\": \"Editer\",\n \"_IMPORT\": \"Importer\",\n \"_IMPORT_PLAYLIST\": \"Importer la Playlist\",\n \"_ORIGIN_LINK\": \"Lien original\",\n \"_SONGS\": \"Musiques\",\n \"_ARTISTS\": \"Artistes\",\n \"_ALBUMS\": \"Albums\",\n \"_OPERATION\": \"Outils\",\n \"_CREATE_PLAYLIST\": \"Créer une Playlist\",\n \"_CANCEL\": \"Annuler\",\n \"_CREATE_AND_ADD_PLAYLIST\": \"Créer et ajouter\",\n \"_REMOVE_FROM_PLAYLIST\": \"Retirer\",\n \"_ADD_TO_QUEUE\": \"Ajouter à la liste de lecture\",\n \"_ARTIST\": \"Artiste\",\n \"_ALBUM\": \"Album\",\n \"_PLAYLIST_TITLE\": \"Titre de la Playlist\",\n \"_PLAYLIST_AUTHOR\": \"Playlist Author\",\n \"_PLAYLIST_SONG_COUNT\": \"Playlist Song Count\",\n \"_PLAYLIST_COVER_IMAGE_URL\": \"URL de la pochette\",\n \"_INPUT_PLAYLIST_TITLE\": \"Inserez le titre de la playlist\",\n \"_INPUT_PLAYLIST_COVER_IMAGE_URL\":  \"Entrez l'URL de la pochette\",\n \"_EDIT_PLAYLIST\": \"Editer la playlist\",\n \"_REMOVE_PLAYLIST\": \"Supprimer la Playlist\",\n \"_OPENING_LASTFM_PAGE\":\"Ouverture de Last.fm页面...\",\n \"_CONFIRM_NOTICE_LASTFM\": \"Cliquez sur \\\"Oui, tout autoriser\\\" dans la nouvelle page, autoriser l'accès à votre compte.\",\n \"_AUTHORIZED_FINISHED\": \"Autorisation finie\",\n \"_AUTHORIZED_REOPEN\": \"J'ai un problème, veuillez réessayer\",\n \"_PLAYLIST_LINK\": \"Lien de la playslit\",\n \"_OPEN_PLAYLIST\": \"Ouvrir la laylist\",\n \"_OPENING_GITHUB_PAGE\": \"Ouverture de Github.com页面...\",\n \"_CONFIRM_NOTICE_GITHUB\": \"Cliquez sur  \\\"S'identifier\\\" dans la nouvelle page, autoriser l'accès à votre compte.\",\n \"_CREATE_PLAYLIST_BACKUP\": \"Créer une sauvegarde de playslist\",\n \"_CREATE_PUBLIC_BACKUP\": \"Créer une sauvegarde public de playslist\",\n \"_CREATE_PRIVATE_BACKUP\": \"Créer une sauvegarde privée de playslist\",\n \"_BACKUP_PLAYLIST\": \"Sauvegardes de playslite\",\n \"_BACKUP_WARNING\": \"Réinstallez ou effacez votre cache vous fera perdre toutes vos playlists, les sauvegardes sont recommandés.\",\n \"_EXPORT_TO_LOCAL_FILE\": \"Exporter vers un fichier local\",\n \"_EXPORT_TO_GITHUB_GIST\": \"Exporter vers un Github Gist\",\n \"_RECOVER_PLAYLIST\": \"Récuperer une playlist\",\n \"_RECOVER_WARNING\": \"Séléctionner un fichier de sauvegarde. Notice: Elle remplacera la playliste actuelle.\",\n \"_RECOVER_FROM_LOCAL_FILE\": \"Récuperer par un fichier local\",\n \"_RECOVER_FROM_GITHUB_GIST\": \"Récuperer par un Github Gist\",\n \"_CONNECT_TO_GITHUB\": \"Connecter à Github.com\",\n \"_STATUS\": \"Statut\",\n \"_RECONNECT\": \"Reconnecter\",\n \"_CANCEL_CONNECT\": \"Annuler la connexion\",\n \"_SHORTCUTS\": \"Raccourcis\",\n \"_VIEW_SHORTCUTS_LIST\": \"Voir la liste des raccourcis\",\n \"_GLOBAL_SHORTCUTS_NOTICE\": \"Activer les raccourcis généraux\",\n \"_LYRIC_DISPLAY\": \"Affichage des parole\",\n \"_SHOW_DESKTOP_LYRIC\": \"Afficher les parole du bureau\",\n \"_SHOW_LYRIC_TRANSLATION\": \"Affiche la traduction des paroles\",\n \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"Afficher les traductions de paroles du bureau\",\n \"_CONNECT_TO_LASTFM\": \"Ouverture de Last.fm\",\n \"_ABOUT\": \"A Propos\",\n \"_HOMEPAGE\": \"Accueil\",\n \"_EMAIL\": \"Email\",\n \"_FEEDBACK\": \"Feedback\",\n \"_DESIGNER\": \"Designer\",\n \"_VERSION\": \"Version\",\n \"_LATEST_VERSION\": \"Latest Version\",\n \"_LICENSE_NOTICE\": \"(Ce programme est gratuit et tout license MIT)\",\n \"_TOTAL_SONG_PREFIX\": \"Total \",\n \"_TOTAL_SONG_POSTFIX\": \" Morceaux\",\n \"_CLEAR_ALL\": \"Tout effacer\",\n \"_SEARCH_PLACEHOLDER\": \"Recherche\",\n \"_LANGUAGE\": \"Langage\",\n \"_ADD_TO_PLAYLIST_SUCCESS\": \"Succès: Ajouter à la Playlist\",\n \"_FAVORITE_PLAYLIST_SUCCESS\": \"Success: Favorite Playlist\",\n \"_EDIT_PLAYLIST_SUCCESS\": \"Succès: Editer la Playlist\",\n \"_IMPORTING_PLAYLIST\": \"Importation des playlists...\",\n \"_IMPORTING_PLAYLIST_SUCCESS\": \"Succès: Importer les Playlists\",\n \"_REMOVE_PLAYLIST_SUCCESS\": \"Succès: Effacer Playlist\",\n \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"Succès: UnFavorite Playlist\",\n \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"Succès: Retirer la morceau de la Playlist\",\n \"_ADD_TO_QUEUE_SUCCESS\": \"Succès: Ajouter à la file de lecture\",\n \"_COPYRIGHT_ISSUE\": \"Errur due au copyright, Cherchez sur une autre plateforme\",\n \"_INPUT_NEW_PLAYLIST_TITLE\": \"Entrez un nouveau titre de Playlist\",\n \"_FAIL_OPEN_PLAYLIST_URL\": \"Erreur ouverture de l'url\",\n \"_EXAMPLE\": \"Exemple:\",\n \"_CONFIRM\": \"Confirmer\",\n \"_THEME\": \"Thème\",\n \"_THEME_WHITE\": \"Thème blanc\",\n \"_THEME_BLACK\": \"Thème foncé\",\n \"_THEME_MODERN_WHITE\": \"Modern White Theme\",\n \"_THEME_MODERN_BLACK\": \"Modern Black Theme\",\n \"_AUTO_CHOOSE_SOURCE\": \"Auto Choose Source\",\n \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"If play fail, auto choose source from other music platform.\",\n \"_AUTO_CHOOSE_SOURCE_LIST\": \"Music Platform to try after fail\",\n \"_NOWPLAYING_DISPLAY\": \"Now Playing Display\",\n \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"Show Cover Image for Now Playing\",\n \"_NOWPLAYING_BITRATE_NOTICE\": \"Show Bitrate\",\n \"_NOWPLAYING_PLATFORM_NOTICE\": \"Show Music Platform\",\n \"_LOCAL_MUSIC\": \"Local Music\",\n \"_ADD_LOCAL_SONGS\": \"Add Local Songs\",\n \"netease\": \"netease\",\n \"bilibili\": \"bili\",\n \"kugou\": \"kugou\",\n \"kuwo\": \"kuwo\",\n \"migu\": \"migu\",\n \"qq\": \"qq\",\n \"xiami\": \"xiami\",\n \"taihe\": \"qianqian\",\n \"localmusic\": \"local music\",\n \"_CLOSE_TAB_ACTION\": \"Close tab action\",\n \"_VALID_AFTER_RESTART\": \"Valid after restart\",\n \"_QUIT_APPLICATION\": \"Quit Application\",\n \"_MINIMIZE_TO_BACKGROUND\": \"Minimize to background\",\n \"_MY_CREATED_PLAYLIST\": \"My Created Playlist\",\n \"_MY_FAVORITE_PLAYLIST\": \"My Favorite Playlist\",\n \"_RECOMMEND_PLAYLIST\": \"Recommend Playlist\",\n \"_LISTEN1_LOGIN_NOTICE\": \"Listen1 NE transférera PAS les données de votre compte vers un serveur autre que le serveur de la plate-forme musicale.\",\n \"_PASSWORD\": \"Password\",\n \"_LOGIN\": \"Login\",\n \"_LOGOUT\": \"Logout\",\n \"_LOGIN_ERROR\": \"Login error, please check user name and password\",\n \"_LOGIN_EMAIL_ERROR\": \"Please enter correct email address\",\n \"_LOGIN_COUNTRYCODE_ERROR\": \"Please enter correct country code\",\n \"_LOGIN_PHONE_ERROR\": \"Please enter correct phone number\",\n \"_LOGIN_PASSWORD_ERROR\": \"Password can't be empty\",\n \"_LOGIN_NETEASE\": \"Login Netease\",\n \"_LOGIN_BY_MOBILE_PHONE\": \"Login by mobile phone\",\n \"_LOGIN_BY_EMAIL\": \"Login by email\",\n \"_MOBILE_PHONE\": \"Mobile Phone\",\n \"_MY_NETEASE\": \"My Netease\",\n \"_MY_QQ\": \"My QQ Music\",\n \"_FAIL_ALL_NOTICE\": \"No available track in current playlist\",\n \"_SHORTCUTS_FUNCTION\": \"Shortcuts Function\",\n \"_GLOBAL_SHORTCUTS\": \"Global Raccourcis\",\n \"_PLAY_OR_PAUSE\": \"Play/Pause\",\n \"_PREVIOUS_TRACK\": \"Previous Track\",\n \"_NEXT_TRACK\": \"Next Track\",\n \"_VOLUME_UP\": \"Volume Up\",\n \"_VOLUME_DOWN\": \"Volume Down\",\n \"_SHORTCUTS_NOT_SET\": \"N/A\",\n \"_QUICK_SEARCH\": \"Quick Search\",\n \"_SEARCH_PLAYLIST\": \"Search Playlist\",\n \"_KEYBOARD_SPACE\": \"Space\",\n \"_NOT_LOGIN_NICKNAME\": \"Anonymous\",\n \"_LOGIN_DIALOG_NOTICE\": \"Opening Login page, please login in that page and click finish login\",\n \"_LOGIN_SUCCESS\": \"Finish Login\",\n \"_LOGIN_FAIL_RETRY\": \"Something wrong. Reopen Login page\",\n \"_PROXY_SYSTEM\": \"Use System Proxy\",\n \"_PROXY_DIRECT\": \"No Proxy\",\n \"_PROXY_CUSTOM\": \"Custom Proxy\",\n \"_PROXY_CONFIG\": \"Proxy Config\",\n \"_PROXY_NOT_SET\": \"Proxy Not Set\",\n \"_MODIFY\": \"Modify\",\n \"_PROTOCOL\": \"Protocol\",\n \"_HOST\": \"Host\",\n \"_PORT\": \"Port\",\n \"ZOOM_IN_OUT\": \"Zoom In/Out\"\n}\n"
  },
  {
    "path": "i18n/ko-KR.json",
    "content": "{\n  \"HELLO\": \"안녕하세요\",\n  \"_ALL_MUSIC\": \"모든 음악\",\n  \"_NETEASE_MUSIC\": \"Netease\",\n  \"_QQ_MUSIC\": \"QQ\",\n  \"_XIAMI_MUSIC\": \"Xiami\",\n  \"_KUGOU_MUSIC\": \"Kugou\",\n  \"_KUWO_MUSIC\": \"Kuwo\",\n  \"_BILIBILI_MUSIC\": \"Bilibili\",\n  \"_MIGU_MUSIC\": \"Migu\",\n  \"_TAIHE_MUSIC\": \"Qianqian\",\n  \"_PLATFORM_UNION\": \"전체 사이트\",\n  \"_PLAYLISTS\": \"재생목록\",\n  \"_MY_MUSIC\": \"내 음악\",\n  \"_CREATED_PLAYLIST\": \"내 재생목록\",\n  \"_FAVORITED_PLAYLIST\": \"좋아하는 재생목록\",\n  \"_REFRESH_PLAYLIST\": \"갱신 재생목록\",\n  \"_FAVORITED\": \"즐겨찾음\",\n  \"_FAVORITE\": \"즐겨찾기\",\n  \"_PLAY_ALL\": \"전체 재생\",\n  \"_ADD_TO_PLAYLIST\": \"재생목록 추가\",\n  \"_EDIT\": \"수정\",\n  \"_IMPORT\": \"추가\",\n  \"_IMPORT_PLAYLIST\": \"추가 재생목록\",\n  \"_ORIGIN_LINK\": \"원본 링크\",\n  \"_SONGS\": \"음악\",\n  \"_ARTISTS\": \"가수\",\n  \"_ALBUMS\": \"앨범\",\n  \"_OPERATION\": \"도구\",\n  \"_CREATE_PLAYLIST\": \"재생목록 생성\",\n  \"_CANCEL\": \"취소\",\n  \"_CREATE_AND_ADD_PLAYLIST\": \"생성 및 추가\",\n  \"_REMOVE_FROM_PLAYLIST\": \"제거\",\n  \"_ADD_TO_QUEUE\": \"목록 추가\",\n  \"_ARTIST\": \"가수\",\n  \"_ALBUM\": \"앨범\",\n  \"_PLAYLIST_TITLE\": \"재생목록 이름\",\n  \"_PLAYLIST_AUTHOR\": \"재생목록 생성자\",\n  \"_PLAYLIST_SONG_COUNT\": \"재생목록 음악 수\",\n  \"_PLAYLIST_COVER_IMAGE_URL\": \"커버 사진 URL\",\n  \"_INPUT_PLAYLIST_TITLE\": \"재생목록 이름 입력\",\n  \"_INPUT_PLAYLIST_COVER_IMAGE_URL\": \"커버 사진 URL 입력\",\n  \"_EDIT_PLAYLIST\": \"재생목록 수정\",\n  \"_REMOVE_PLAYLIST\": \"재생목록 제거\",\n  \"_OPENING_LASTFM_PAGE\": \"Last.fm 여는중...\",\n  \"_CONFIRM_NOTICE_LASTFM\": \"\\\"Yes, all access\\\" 을 클릭하여 Listen 1 이 계정에 접근 할 수 있도록 허용 해주세요.\",\n  \"_AUTHORIZED_FINISHED\": \"인증 완료\",\n  \"_AUTHORIZED_REOPEN\": \"문제 발생, 다시 시도 하세요\",\n  \"_PLAYLIST_LINK\": \"재생목록 링크\",\n  \"_OPEN_PLAYLIST\": \"재생목록 열기\",\n  \"_OPENING_GITHUB_PAGE\": \"Github.com 여는중...\",\n  \"_CONFIRM_NOTICE_GITHUB\": \"\\\"Authencate\\\" 을 클릭하여 Listen 1 이 계정에 접근 할 수 있도록 허용 해주세요\",\n  \"_CREATE_PLAYLIST_BACKUP\": \"재생목록 백업 생성\",\n  \"_CREATE_PUBLIC_BACKUP\": \"공개 백업 생성\",\n  \"_CREATE_PRIVATE_BACKUP\": \"비공개 백업 생성\",\n  \"_BACKUP_PLAYLIST\": \"백업 재생목록\",\n  \"_BACKUP_WARNING\": \"캐시를 다시 설치하거나 지우면 모든 재생 목록이 손실됩니다. 백업을 권장 합니다\",\n  \"_EXPORT_TO_LOCAL_FILE\": \"로컬 파일 내보내기\",\n  \"_EXPORT_TO_GITHUB_GIST\": \"Github Gist 내보내기\",\n  \"_RECOVER_PLAYLIST\": \"재생 목록 복구\",\n  \"_RECOVER_WARNING\": \"백업 파일을 선택합니다. 알림: 현재 재생 목록을 덮어씁니다.\",\n  \"_RECOVER_FROM_LOCAL_FILE\": \"로컬 파일 복구\",\n  \"_RECOVER_FROM_GITHUB_GIST\": \"Github Gist 복구\",\n  \"_CONNECT_TO_GITHUB\": \"Github.com 연결\",\n  \"_STATUS\": \"상태\",\n  \"_RECONNECT\": \"재접속\",\n  \"_CANCEL_CONNECT\": \"접속 취소\",\n  \"_SHORTCUTS\": \"단축키\",\n  \"_VIEW_SHORTCUTS_LIST\": \"단축키 리스트 보기\",\n  \"_GLOBAL_SHORTCUTS_NOTICE\": \"전역 단축키 활성\",\n  \"_LYRIC_DISPLAY\": \"가사 표시\",\n  \"_SHOW_DESKTOP_LYRIC\": \"바탕화면 가사 표시\",\n  \"_SHOW_LYRIC_TRANSLATION\": \"외국어 가사 번역 표시\",\n  \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"외국어 가사 번역 및 바탕화면 표시\",\n  \"_CONNECT_TO_LASTFM\": \"Last.fm 연결\",\n  \"_ABOUT\": \"소개\",\n  \"_HOMEPAGE\": \"홈페이지\",\n  \"_EMAIL\": \"Email\",\n  \"_FEEDBACK\": \"Feedback\",\n  \"_DESIGNER\": \"디자이너\",\n  \"_VERSION\": \"버전\",\n  \"_LATEST_VERSION\": \"마지막 버전\",\n  \"_LICENSE_NOTICE\": \"(This software is free and open source under MIT license)\",\n  \"_TOTAL_SONG_PREFIX\": \"전체 \",\n  \"_TOTAL_SONG_POSTFIX\": \" 음악\",\n  \"_CLEAR_ALL\": \"모두 지우기\",\n  \"_SEARCH_PLACEHOLDER\": \"검색\",\n  \"_LANGUAGE\": \"언어\",\n  \"_ADD_TO_PLAYLIST_SUCCESS\": \"성공: 내 재생 목록에 추가\",\n  \"_FAVORITE_PLAYLIST_SUCCESS\": \"성공: 즐겨찾기 재생 목록\",\n  \"_EDIT_PLAYLIST_SUCCESS\": \"성공: 재생 목록 편집\",\n  \"_IMPORTING_PLAYLIST\": \"재생 목록 가져오기...\",\n  \"_IMPORTING_PLAYLIST_SUCCESS\": \"성공: 재생 목록 가져오기\",\n  \"_REMOVE_PLAYLIST_SUCCESS\": \"성공: 재생 목록 제거\",\n  \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"성공: 재생 목록 즐겨찾기 취소\",\n  \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"성공: 재생 목록에서 음악 제거\",\n  \"_ADD_TO_QUEUE_SUCCESS\": \"성공: 재생 목록에 추가\",\n  \"_COPYRIGHT_ISSUE\": \"저작권 문제로 인해 실패, 대신 다른 플랫폼 검색\",\n  \"_INPUT_NEW_PLAYLIST_TITLE\": \"새 재생 목록 제목 입력\",\n  \"_FAIL_OPEN_PLAYLIST_URL\": \"재생 목록 URL을 열지 못함\",\n  \"_EXAMPLE\": \"예제:\",\n  \"_CONFIRM\": \"확인\",\n  \"_THEME\": \"테마\",\n  \"_THEME_WHITE\": \"하얀색 테마\",\n  \"_THEME_BLACK\": \"검색은 테마\",\n  \"_THEME_MODERN_WHITE\": \"Modern White Theme\",\n  \"_THEME_MODERN_BLACK\": \"Modern Black Theme\",\n  \"_AUTO_CHOOSE_SOURCE\": \"소스 자동 선택\",\n  \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"재생 소스 자동 전환 여부 (음악 재생 실패 후에만 전환)\",\n  \"_AUTO_CHOOSE_SOURCE_LIST\": \"실패 후 시도할 음악 플랫폼\",\n  \"_NOWPLAYING_DISPLAY\": \"재생 중 표시\",\n  \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"지금 재생 중인 커버 이미지 표시\",\n  \"_NOWPLAYING_BITRATE_NOTICE\": \"비트 레이트 보이기\",\n  \"_NOWPLAYING_PLATFORM_NOTICE\": \"음악 플랫폼 표시\",\n  \"_LOCAL_MUSIC\": \"로컬 음악\",\n  \"_ADD_LOCAL_SONGS\": \"로컬 음악 추가\",\n  \"netease\": \"netease\",\n  \"bilibili\": \"bili\",\n  \"kugou\": \"kugou\",\n  \"kuwo\": \"kuwo\",\n  \"migu\": \"migu\",\n  \"qq\": \"qq\",\n  \"xiami\": \"xiami\",\n  \"taihe\": \"qianqian\",\n  \"localmusic\": \"로컬 음악\",\n  \"_CLOSE_TAB_ACTION\": \"탭 닫기 동작\",\n  \"_VALID_AFTER_RESTART\": \"다시 시작 후에 동작\",\n  \"_QUIT_APPLICATION\": \"응용 프로그램 종료\",\n  \"_MINIMIZE_TO_BACKGROUND\": \"배경으로 최소화\",\n  \"_MY_CREATED_PLAYLIST\": \"내가 만든 재생 목록\",\n  \"_MY_FAVORITE_PLAYLIST\": \"내가 소장 재생 목록\",\n  \"_RECOMMEND_PLAYLIST\": \"추천 재생 목록\",\n  \"_LISTEN1_LOGIN_NOTICE\": \"Listen1은 음악 플랫폼 서버 이외의 서버로 계정 데이터를 전송하지 않습니다.\",\n  \"_PASSWORD\": \"패스워드\",\n  \"_LOGIN\": \"로그인\",\n  \"_LOGOUT\": \"로그아웃\",\n  \"_LOGIN_ERROR\": \"로그인 오류. 아이디, 패스워드를 확인 하세요.\",\n  \"_LOGIN_EMAIL_ERROR\": \"올바른 이메일 주소를 입력하십시오.\",\n  \"_LOGIN_COUNTRYCODE_ERROR\": \"올바른 국가 코드를 입력하십시오.\",\n  \"_LOGIN_PHONE_ERROR\": \"올바른 전화번호를 입력하십시오.\",\n  \"_LOGIN_PASSWORD_ERROR\": \"암호는 비워 둘 수 없습니다.\",\n  \"_LOGIN_NETEASE\": \"Netease 로그인\",\n  \"_LOGIN_BY_MOBILE_PHONE\": \"휴대폰으로 로그인\",\n  \"_LOGIN_BY_EMAIL\": \"이메일로 로그인\",\n  \"_MOBILE_PHONE\": \"휴대폰\",\n  \"_MY_NETEASE\": \"내 Netease\",\n  \"_MY_QQ\": \"내 QQ 음악\",\n  \"_FAIL_ALL_NOTICE\": \"현재 재생 목록에 재생 가능한 곡이 없습니다.\",\n  \"_SHORTCUTS_FUNCTION\": \"단축키 기능\",\n  \"_GLOBAL_SHORTCUTS\": \"전역 단축키\",\n  \"_PLAY_OR_PAUSE\": \"재생/일시 중지\",\n  \"_PREVIOUS_TRACK\": \"이전 음악\",\n  \"_NEXT_TRACK\": \"다음 음악\",\n  \"_VOLUME_UP\": \"사운드 크게\",\n  \"_VOLUME_DOWN\": \"사운드 작게\",\n  \"_SHORTCUTS_NOT_SET\": \"N/A\",\n  \"_QUICK_SEARCH\": \"빠른 검색\",\n  \"_SEARCH_PLAYLIST\": \"재생 목록 검색\",\n  \"_KEYBOARD_SPACE\": \"스페이스\",\n  \"_NOT_LOGIN_NICKNAME\": \"익명\",\n  \"_LOGIN_DIALOG_NOTICE\": \"그인 페이지를 여는 중입니다. 해당 페이지에 로그인하고 로그인 완료를 클릭하십시오.\",\n  \"_LOGIN_SUCCESS\": \"로그인 완료\",\n  \"_LOGIN_FAIL_RETRY\": \"로그인에 문제가 생기면 로그인 페이지를 다시 엽니다.\",\n  \"_PROXY_SYSTEM\": \"시스템 Proxy 사용\",\n  \"_PROXY_DIRECT\": \"Proxy 미사용\",\n  \"_PROXY_CUSTOM\": \"사용자 Proxy\",\n  \"_PROXY_CONFIG\": \"Proxy 설정\",\n  \"_PROXY_NOT_SET\": \"Proxy가 설정되지 않음\",\n  \"_MODIFY\": \"수정\",\n  \"_PROTOCOL\": \"Protocol\",\n  \"_HOST\": \"Host\",\n  \"_PORT\": \"Port\",\n  \"ZOOM_IN_OUT\": \"확대/축소\"\n}\n"
  },
  {
    "path": "i18n/pt-BR.json",
    "content": "{\n \"HELLO\"        : \"Olá\",\n \"_ALL_MUSIC\": \"Todas as músicas\",\n \"_NETEASE_MUSIC\": \"Netease\",\n \"_QQ_MUSIC\": \"QQ\",\n \"_XIAMI_MUSIC\": \"Xiami\",\n \"_KUGOU_MUSIC\": \"Kugou\",\n \"_KUWO_MUSIC\": \"Kuwo\",\n \"_BILIBILI_MUSIC\": \"Bilibili\",\n \"_MIGU_MUSIC\": \"Migu\",\n \"_TAIHE_MUSIC\": \"Qianqian\",\n \"_PLATFORM_UNION\": \"Plataformas\",\n \"_PLAYLISTS\": \"PLAYLISTS\",\n \"_MY_MUSIC\": \"Minha música\",\n \"_CREATED_PLAYLIST\": \"Minha Playlist\",\n \"_FAVORITED_PLAYLIST\": \"Playlist Favorita\",\n \"_REFRESH_PLAYLIST\": \"Playlist Recarregar\",\n \"_FAVORITED\": \"Favoritado\",\n \"_FAVORITE\": \"Favorito\",\n \"_PLAY_ALL\": \"Tocar todas\",\n \"_ADD_TO_PLAYLIST\": \"Adicionar pra Playlist\",\n \"_EDIT\": \"Editar\",\n \"_IMPORT\": \"Importar\",\n \"_IMPORT_PLAYLIST\": \"Importar Playlist\",\n \"_ORIGIN_LINK\": \"Link de Origem\",\n \"_SONGS\": \"Músicas\",\n \"_ARTISTS\": \"Artistas\",\n \"_ALBUMS\": \"Álbuns\",\n \"_OPERATION\": \"Ferramentas\",\n \"_CREATE_PLAYLIST\": \"Criar Playlist\",\n \"_CANCEL\": \"Cancelar\",\n \"_CREATE_AND_ADD_PLAYLIST\": \"Criar e adicionar\",\n \"_REMOVE_FROM_PLAYLIST\": \"Remover\",\n \"_ADD_TO_QUEUE\": \"Adicionar à fila\",\n \"_ARTIST\": \"Artista\",\n \"_ALBUM\": \"Álbum\",\n \"_PLAYLIST_TITLE\": \"Título da Playlist\",\n \"_PLAYLIST_AUTHOR\": \"Autor da Playlist\",\n \"_PLAYLIST_SONG_COUNT\": \"Quantidade de Músicas na Playlist\",\n \"_PLAYLIST_COVER_IMAGE_URL\": \"URL da Imagem da Capa\",\n \"_INPUT_PLAYLIST_TITLE\": \"Insira o Título da Lista de Reprodução\",\n \"_INPUT_PLAYLIST_COVER_IMAGE_URL\":  \"Insira a URL da Imagem da Capa\",\n \"_EDIT_PLAYLIST\": \"Editar Playlist\",\n \"_REMOVE_PLAYLIST\": \"Remover Playlist\",\n \"_OPENING_LASTFM_PAGE\":\"Abrindo Last.fm...\",\n \"_CONFIRM_NOTICE_LASTFM\": \"Por favor, clique em \\\"Sim, todo acesso\\\" na nova página, permite que o Listen 1 acesse sua conta.\",\n \"_AUTHORIZED_FINISHED\": \"Autorização finalizada\",\n \"_AUTHORIZED_REOPEN\": \"Estou com um problema, tente novamente\",\n \"_PLAYLIST_LINK\": \"Link da Playlist\",\n \"_OPEN_PLAYLIST\": \"Abrir Playlist\",\n \"_OPENING_GITHUB_PAGE\": \"Abrindo Github.com...\",\n \"_CONFIRM_NOTICE_GITHUB\": \"Por favor, clique em \\\"Autenticar\\\" na nova página, permite que o Listen 1 acesse sua conta.\",\n \"_CREATE_PLAYLIST_BACKUP\": \"Criar Backup da Playlist \",\n \"_CREATE_PUBLIC_BACKUP\": \"Criar Backup Público\",\n \"_CREATE_PRIVATE_BACKUP\": \"Criar Backup Privado\",\n \"_BACKUP_PLAYLIST\": \"Backup Playlists\",\n \"_BACKUP_WARNING\": \"Reinstalar ou limpar o cache perderá todas as suas listas de reprodução, o backup é FORTEMENTE RECOMENDADO.\",\n \"_EXPORT_TO_LOCAL_FILE\": \"Exportar para um arquivo local\",\n \"_EXPORT_TO_GITHUB_GIST\": \"Exportar para Github Gist\",\n \"_RECOVER_PLAYLIST\": \"Recuperar Playlists\",\n \"_RECOVER_WARNING\": \"Escolha o arquivo de backup. Aviso: Ele substituirá as listas de reprodução atuais.\",\n \"_RECOVER_FROM_LOCAL_FILE\": \"Recuperar de um arquivo local\",\n \"_RECOVER_FROM_GITHUB_GIST\": \"Recuperar do Github Gist\",\n \"_CONNECT_TO_GITHUB\": \"Conectar com Github.com\",\n \"_STATUS\": \"Status\",\n \"_RECONNECT\": \"Reconectar\",\n \"_CANCEL_CONNECT\": \"Cancelar conexão\",\n \"_SHORTCUTS\": \"Atalhos\",\n \"_VIEW_SHORTCUTS_LIST\": \"Ver Lista de Atalhos\",\n \"_GLOBAL_SHORTCUTS_NOTICE\": \"Habilitar Atalhos Globais\",\n \"_LYRIC_DISPLAY\": \"Exibição de letra\",\n \"_SHOW_DESKTOP_LYRIC\": \"Mostrar letra da área de trabalho\",\n \"_SHOW_LYRIC_TRANSLATION\": \"Mostrar tradução da letra\",\n \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"Mostrar a tradução da letra com a letra da área de trabalho\",\n \"_CONNECT_TO_LASTFM\": \"Conectar à Last.fm\",\n \"_ABOUT\": \"Sobre\",\n \"_HOMEPAGE\": \"Página Inicial\",\n \"_EMAIL\": \"Email\",\n \"_FEEDBACK\": \"Feedback\",\n \"_DESIGNER\": \"Designer\",\n \"_VERSION\": \"Versão\",\n \"_LATEST_VERSION\": \"Última versão\",\n \"_LICENSE_NOTICE\": \"(Este software é gratuito e de código aberto sob licença do MIT)\",\n \"_TOTAL_SONG_PREFIX\": \"Total \",\n \"_TOTAL_SONG_POSTFIX\": \" Músicas\",\n \"_CLEAR_ALL\": \"Limpar Tudo\",\n \"_SEARCH_PLACEHOLDER\": \"Pesquisar\",\n \"_LANGUAGE\": \"Língua\",\n \"_ADD_TO_PLAYLIST_SUCCESS\": \"Sucesso: Adicionar em minha Playlist\",\n \"_FAVORITE_PLAYLIST_SUCCESS\": \"Sucesso: Favoritar Playlist\",\n \"_EDIT_PLAYLIST_SUCCESS\": \"Sucesso: Editar Playlist\",\n \"_IMPORTING_PLAYLIST\": \"Importando playlists...\",\n \"_IMPORTING_PLAYLIST_SUCCESS\": \"Sucesso: Importar Playlists\",\n \"_REMOVE_PLAYLIST_SUCCESS\": \"Sucesso: Remover playlist\",\n \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"Sucesso: Desfavoritar playlist\",\n \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"Sucesso: Remover música da playlist\",\n \"_ADD_TO_QUEUE_SUCCESS\": \"Sucesso: Adicionar à fila\",\n \"_COPYRIGHT_ISSUE\": \"Falha devido ao problema de direitos autorais. Pesquise em outra plataforma\",\n \"_INPUT_NEW_PLAYLIST_TITLE\": \"Insira o novo título Playlist\",\n \"_FAIL_OPEN_PLAYLIST_URL\": \"Falha em abrir o link da playlist\",\n \"_EXAMPLE\": \"Exemplo:\",\n \"_CONFIRM\": \"Confirmar\",\n \"_THEME\": \"Tema\",\n \"_THEME_WHITE\": \"Tema Claro\",\n \"_THEME_BLACK\": \"Tema Escuro\",\n \"_THEME_MODERN_WHITE\": \"Tema Modern White\",\n \"_THEME_MODERN_BLACK\": \"Tema Modern Black\",\n \"_AUTO_CHOOSE_SOURCE\": \"Escolha automática da fonte\",\n \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"Ative a escolha da fonte de outra plataforma de música após a falha.\",\n \"_AUTO_CHOOSE_SOURCE_LIST\": \"Plataforma de música para tentar depois de falhar\",\n \"_NOWPLAYING_DISPLAY\": \"Tocando agora\",\n \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"Mostrar a imagem da capa para Tocando agora\",\n \"_NOWPLAYING_BITRATE_NOTICE\": \"Mostrar Bitrate\",\n \"_NOWPLAYING_PLATFORM_NOTICE\": \"Mostrar Plataforma da Música \",\n \"_LOCAL_MUSIC\": \"Música Locas\",\n \"_ADD_LOCAL_SONGS\": \"Adicionar músicas locais\",\n \"netease\": \"netease\",\n \"bilibili\": \"bili\",\n \"kugou\": \"kugou\",\n \"kuwo\": \"kuwo\",\n \"migu\": \"migu\",\n \"qq\": \"qq\",\n \"xiami\": \"xiami\",\n \"taihe\": \"qianqian\",\n \"localmusic\": \"música local\",\n \"_CLOSE_TAB_ACTION\": \"Fechar guia\",\n \"_VALID_AFTER_RESTART\": \"Válidar após reiniciar\",\n \"_QUIT_APPLICATION\": \"Sair da aplicação\",\n \"_MINIMIZE_TO_BACKGROUND\": \"Minimizar para segundo plano\",\n \"_MY_CREATED_PLAYLIST\": \"Minhas Playlist Criadas\",\n \"_MY_FAVORITE_PLAYLIST\": \"Minhas Playlist Favoritas\",\n \"_RECOMMEND_PLAYLIST\": \"Playlists Recomendadas\",\n \"_LISTEN1_LOGIN_NOTICE\": \"Listen1 NÃO transferirá os dados da sua conta para nenhum servidor que não seja o servidor da plataforma de música.\",\n \"_PASSWORD\": \"Senha\",\n \"_LOGIN\": \"Login\",\n \"_LOGOUT\": \"Sair\",\n \"_LOGIN_ERROR\": \"Erro no login, por favor confira se as informações estão corretas\",\n \"_LOGIN_EMAIL_ERROR\": \"Por favor insira um email válido\",\n \"_LOGIN_COUNTRYCODE_ERROR\": \"Por favor insira o código de país correto\",\n \"_LOGIN_PHONE_ERROR\": \"Por favor insira um número de celular válido\",\n \"_LOGIN_PASSWORD_ERROR\": \"A senha não pode estar vazia\",\n \"_LOGIN_NETEASE\": \"Login Netease\",\n \"_LOGIN_BY_MOBILE_PHONE\": \"Login por Celular\",\n \"_LOGIN_BY_EMAIL\": \"Login por Email\",\n \"_MOBILE_PHONE\": \"Celular\",\n \"_MY_NETEASE\": \"Meu Netease\",\n \"_MY_QQ\": \"Meu QQ Music\",\n \"_FAIL_ALL_NOTICE\": \"Nenhuma faixa disponível na playlist atual\",\n \"_SHORTCUTS_FUNCTION\": \"Função dos atalhos\",\n \"_GLOBAL_SHORTCUTS\": \"Atalhos globais\",\n \"_PLAY_OR_PAUSE\": \"Play/Pause\",\n \"_PREVIOUS_TRACK\": \"Faixa anterior\",\n \"_NEXT_TRACK\": \"Próxima faixa\",\n \"_VOLUME_UP\": \"Volume Up\",\n \"_VOLUME_DOWN\": \"Volume Down\",\n \"_SHORTCUTS_NOT_SET\": \"N/A\",\n \"_QUICK_SEARCH\": \"Pesquisa Rápida\",\n \"_SEARCH_PLAYLIST\": \"Procurar Playlist\",\n \"_KEYBOARD_SPACE\": \"Espaço\",\n \"_NOT_LOGIN_NICKNAME\": \"Anônimo\",\n \"_LOGIN_DIALOG_NOTICE\": \"Abrindo a página de login, faça o login nessa página e clique em concluir o login\",\n \"_LOGIN_SUCCESS\": \"Concluir o login\",\n \"_LOGIN_FAIL_RETRY\": \"Algo deu errado. Reabra a página de login\",\n \"_PROXY_SYSTEM\": \"Usar Proxy do Sistema\",\n \"_PROXY_DIRECT\": \"Sem proxy\",\n \"_PROXY_CUSTOM\": \"Proxy personalizado\",\n \"_PROXY_CONFIG\": \"Configuração de proxy\",\n \"_PROXY_NOT_SET\": \"Proxy não definido\",\n \"_MODIFY\": \"Modificar\",\n \"_PROTOCOL\": \"Protocolo\",\n \"_HOST\": \"Host\",\n \"_PORT\": \"Porta\",\n \"ZOOM_IN_OUT\": \"Ampliar/Reduzir\"\n}\n"
  },
  {
    "path": "i18n/zh-CN.json",
    "content": "{\n \"HELLO\"        : \"你好\",\n \"_ALL_MUSIC\": \"所有音乐\",\n \"_NETEASE_MUSIC\": \"网易云音乐\",\n \"_QQ_MUSIC\": \"QQ音乐\",\n \"_XIAMI_MUSIC\": \"虾米音乐\",\n \"_KUGOU_MUSIC\": \"酷狗音乐\",\n \"_KUWO_MUSIC\": \"酷我音乐\",\n \"_BILIBILI_MUSIC\": \"哔哩哔哩\",\n \"_MIGU_MUSIC\": \"咪咕音乐\",\n \"_TAIHE_MUSIC\": \"千千音乐\",\n \"_PLATFORM_UNION\": \"平台聚合\",\n \"_PLAYLISTS\": \"精选歌单\",\n \"_MY_MUSIC\": \"我的音乐\",\n \"_CREATED_PLAYLIST\": \"创建的歌单\",\n \"_FAVORITED_PLAYLIST\": \"收藏的歌单\",\n \"_REFRESH_PLAYLIST\": \"刷新\",\n \"_FAVORITED\": \"已收藏\",\n \"_FAVORITE\": \"收藏\",\n \"_PLAY_ALL\": \"播放全部\",\n \"_ADD_TO_PLAYLIST\": \"添加到我的歌单\",\n \"_EDIT\": \"编辑\",\n \"_IMPORT\": \"导入\",\n \"_IMPORT_PLAYLIST\": \"导入歌单\",\n \"_ORIGIN_LINK\": \"链接\",\n \"_SONGS\": \"歌曲名\",\n \"_ARTISTS\": \"歌手\",\n \"_ALBUMS\": \"专辑名\",\n \"_OPERATION\": \"操作\",\n \"_CREATE_PLAYLIST\": \"新建歌单\",\n \"_CANCEL\": \"取消\",\n \"_CREATE_AND_ADD_PLAYLIST\": \"创建并添加\",\n \"_REMOVE_FROM_PLAYLIST\": \"从歌单删除\",\n \"_ADD_TO_QUEUE\": \"添加到当前播放\",\n \"_ARTIST\": \"歌手\",\n \"_ALBUM\": \"专辑名\",\n \"_PLAYLIST_TITLE\": \"歌单名称\",\n \"_PLAYLIST_AUTHOR\": \"歌单作者\",\n \"_PLAYLIST_SONG_COUNT\": \"歌曲数\",\n \"_PLAYLIST_COVER_IMAGE_URL\": \"封面图片url\",\n \"_INPUT_PLAYLIST_TITLE\": \"输入歌单名称\",\n \"_INPUT_PLAYLIST_COVER_IMAGE_URL\":  \"输入封面URL\",\n \"_EDIT_PLAYLIST\": \"编辑歌单\",\n \"_REMOVE_PLAYLIST\": \"删除歌单\",\n \"_OPENING_LASTFM_PAGE\":\"正在打开Last.fm页面...\",\n \"_CONFIRM_NOTICE_LASTFM\": \"请在打开的页面点击\\\"Yes, all access\\\", 允许Listen 1访问你的账户。\",\n \"_AUTHORIZED_FINISHED\": \"已经完成授权\",\n \"_AUTHORIZED_REOPEN\": \"遇到问题，再次打开授权页\",\n \"_PLAYLIST_LINK\": \"歌单链接\",\n \"_OPEN_PLAYLIST\": \"打开歌单\",\n \"_OPENING_GITHUB_PAGE\": \"正在打开Github.com页面...\",\n \"_CONFIRM_NOTICE_GITHUB\": \"请在打开的页面点击\\\"Authencate\\\", 允许Listen 1访问你的账户。\",\n \"_CREATE_PLAYLIST_BACKUP\": \"创建歌单备份\",\n \"_CREATE_PUBLIC_BACKUP\": \"创建公开备份\",\n \"_CREATE_PRIVATE_BACKUP\": \"创建私有备份\",\n \"_BACKUP_PLAYLIST\": \"备份歌单\",\n \"_BACKUP_WARNING\": \"重装插件或清除缓存数据会导致我的歌单数据丢失，强烈建议在这些操作前，备份我的歌单。\",\n \"_EXPORT_TO_LOCAL_FILE\": \"导出到本地文件\",\n \"_EXPORT_TO_GITHUB_GIST\": \"导出到Github Gist\",\n \"_RECOVER_PLAYLIST\": \"从备份恢复\",\n \"_RECOVER_WARNING\": \"选择备份文件，恢复我的歌单。注意：恢复我的歌单会覆盖现有的歌单。\",\n \"_RECOVER_FROM_LOCAL_FILE\": \"从本地文件导入\",\n \"_RECOVER_FROM_GITHUB_GIST\": \"从Github Gist导入\",\n \"_CONNECT_TO_GITHUB\": \"连接到Github.com\",\n \"_STATUS\": \"状态\",\n \"_RECONNECT\": \"重新连接\",\n \"_CANCEL_CONNECT\": \"取消连接\",\n \"_SHORTCUTS\": \"快捷键\",\n \"_VIEW_SHORTCUTS_LIST\": \"查看快捷键列表\",\n \"_GLOBAL_SHORTCUTS_NOTICE\": \"启用全局快捷键\",\n \"_LYRIC_DISPLAY\": \"歌词显示\",\n \"_SHOW_DESKTOP_LYRIC\": \"启用桌面歌词\",\n \"_SHOW_LYRIC_TRANSLATION\": \"外文歌词显示翻译（播放页）\",\n \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"外文歌词显示翻译（桌面歌词）\",\n \"_CONNECT_TO_LASTFM\": \"连接到last.fm\",\n \"_ABOUT\": \"关于\",\n \"_HOMEPAGE\": \"主页\",\n \"_EMAIL\": \"邮箱\",\n \"_FEEDBACK\": \"反馈问题\",\n \"_DESIGNER\": \"主题设计\",\n \"_VERSION\": \"当前版本\",\n \"_LATEST_VERSION\": \"最新版本\",\n \"_LICENSE_NOTICE\": \"(本软件基于MIT协议开源免费)\",\n \"_TOTAL_SONG_PREFIX\": \"共\",\n \"_TOTAL_SONG_POSTFIX\": \"首\",\n \"_CLEAR_ALL\": \"清空\",\n \"_SEARCH_PLACEHOLDER\": \"搜索\",\n \"_LANGUAGE\": \"语言\",\n \"_ADD_TO_PLAYLIST_SUCCESS\": \"成功添加到我创建的歌单\",\n \"_FAVORITE_PLAYLIST_SUCCESS\": \"收藏成功\",\n \"_EDIT_PLAYLIST_SUCCESS\": \"编辑歌单成功\",\n \"_IMPORTING_PLAYLIST\": \"正在导入歌单...\",\n \"_IMPORTING_PLAYLIST_SUCCESS\": \"导入歌单成功\",\n \"_REMOVE_PLAYLIST_SUCCESS\": \"删除歌单成功\",\n \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"取消收藏成功\",\n \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"删除歌曲成功\",\n \"_ADD_TO_QUEUE_SUCCESS\": \"添加到当前播放成功\",\n \"_COPYRIGHT_ISSUE\": \"版权原因无法播放，请搜索其他平台\",\n \"_INPUT_NEW_PLAYLIST_TITLE\": \"输入新歌单名称\",\n \"_FAIL_OPEN_PLAYLIST_URL\": \"未能打开输入的歌单地址\",\n \"_EXAMPLE\": \"例如\",\n \"_CONFIRM\": \"确认\",\n \"_THEME\": \"主题\",\n \"_THEME_WHITE\": \"简约白\",\n \"_THEME_BLACK\": \"深空灰\",\n \"_THEME_MODERN_WHITE\": \"现代白\",\n \"_THEME_MODERN_BLACK\": \"现代黑\",\n \"_AUTO_CHOOSE_SOURCE\": \"自动切换源\",\n \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"是否自动切换播放源（仅在播放音乐失败后切换）\",\n \"_AUTO_CHOOSE_SOURCE_LIST\": \"从以下平台搜索可用源\",\n \"_NOWPLAYING_DISPLAY\": \"正在播放显示\",\n \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"显示专辑封面作为背景\",\n \"_NOWPLAYING_BITRATE_NOTICE\": \"显示比特率\",\n \"_NOWPLAYING_PLATFORM_NOTICE\": \"显示音乐平台\",\n \"_LOCAL_MUSIC\": \"本地音乐\",\n \"_ADD_LOCAL_SONGS\": \"添加歌曲\",\n \"netease\": \"网易\",\n \"bilibili\": \"哔哩\",\n \"kugou\": \"酷狗\",\n \"kuwo\": \"酷我\",\n \"migu\": \"咪咕\",\n \"qq\": \"QQ\",\n \"xiami\": \"虾米\",\n \"taihe\": \"千千\",\n \"localmusic\": \"本地\",\n \"_CLOSE_TAB_ACTION\": \"关闭标签页时行为\",\n \"_VALID_AFTER_RESTART\": \"需重启生效\",\n \"_QUIT_APPLICATION\": \"退出应用\",\n \"_MINIMIZE_TO_BACKGROUND\": \"最小化到后台\",\n \"_MY_CREATED_PLAYLIST\": \"我创建的歌单\",\n \"_MY_FAVORITE_PLAYLIST\": \"我收藏的歌单\",\n \"_RECOMMEND_PLAYLIST\": \"推荐歌单\",\n \"_LISTEN1_LOGIN_NOTICE\": \"Listen1不会传输你的账号数据到任何第三方服务器。\",\n \"_PASSWORD\": \"密码\",\n \"_LOGIN\": \"登录\",\n \"_LOGOUT\": \"退出登录\",\n \"_LOGIN_ERROR\": \"登录失败，请检查用户名和密码\",\n \"_LOGIN_EMAIL_ERROR\": \"请输入正确的邮箱地址\",\n \"_LOGIN_COUNTRYCODE_ERROR\": \"请输入正确的国家或地区代码\",\n \"_LOGIN_PHONE_ERROR\": \"请输入正确的手机号\",\n \"_LOGIN_PASSWORD_ERROR\": \"密码不能为空\",\n \"_LOGIN_NETEASE\": \"登录网易云音乐\",\n \"_LOGIN_BY_MOBILE_PHONE\": \"手机号登录\",\n \"_LOGIN_BY_EMAIL\": \"邮箱登录\",\n \"_MOBILE_PHONE\": \"手机号\",\n \"_MY_NETEASE\": \"我的网易云音乐\",\n \"_MY_QQ\": \"我的QQ音乐\",\n \"_FAIL_ALL_NOTICE\": \"当前播放列表没有可播放的歌曲\",\n \"_SHORTCUTS_FUNCTION\": \"功能说明\",\n \"_GLOBAL_SHORTCUTS\": \"全局快捷键\",\n \"_PLAY_OR_PAUSE\": \"播放/暂停\",\n \"_PREVIOUS_TRACK\": \"上一首\",\n \"_NEXT_TRACK\": \"下一首\",\n \"_VOLUME_UP\": \"音量加\",\n \"_VOLUME_DOWN\": \"音量减\",\n \"_SHORTCUTS_NOT_SET\": \"空\",\n \"_QUICK_SEARCH\": \"快速搜索\",\n \"_SEARCH_PLAYLIST\": \"搜索歌单\",\n \"_KEYBOARD_SPACE\": \"空格\",\n \"_NOT_LOGIN_NICKNAME\": \"未登录\",\n \"_LOGIN_DIALOG_NOTICE\": \"正在打开音乐平台的登录页，请在打开网页中完成登录流程，然后点击登录完成\",\n \"_LOGIN_SUCCESS\": \"登录完成\",\n \"_LOGIN_FAIL_RETRY\": \"登录遇到问题，再打开登录页面\",\n \"_PROXY_SYSTEM\": \"使用系统代理\",\n \"_PROXY_DIRECT\": \"不使用代理\",\n \"_PROXY_CUSTOM\": \"自定义代理\",\n \"_PROXY_CONFIG\": \"代理设置\",\n \"_PROXY_NOT_SET\": \"未设置\",\n \"_MODIFY\": \"修改\",\n \"_PROTOCOL\": \"代理协议\",\n \"_HOST\": \"主机地址\",\n \"_PORT\": \"端口\",\n \"ZOOM_IN_OUT\": \"放大/缩小\"\n}\n"
  },
  {
    "path": "i18n/zh-TC.json",
    "content": "﻿{\r\n \"HELLO\"        : \"你好\",\r\n \"_ALL_MUSIC\": \"所有音樂\",\r\n \"_NETEASE_MUSIC\": \"網易雲音樂\",\r\n \"_QQ_MUSIC\": \"QQ音樂\",\r\n \"_XIAMI_MUSIC\": \"蝦米音樂\",\r\n \"_KUGOU_MUSIC\": \"酷狗音樂\",\r\n \"_KUWO_MUSIC\": \"酷我音樂\",\r\n \"_BILIBILI_MUSIC\": \"嗶哩嗶哩\",\r\n \"_MIGU_MUSIC\": \"咪咕音樂\",\r\n \"_TAIHE_MUSIC\": \"千千音樂\",\r\n \"_PLATFORM_UNION\": \"平臺聚合\",\r\n \"_PLAYLISTS\": \"精選歌單\",\r\n \"_MY_MUSIC\": \"我的音樂\",\r\n \"_CREATED_PLAYLIST\": \"創建的歌單\",\r\n \"_FAVORITED_PLAYLIST\": \"收藏的歌單\",\r\n \"_REFRESH_PLAYLIST\": \"刷新\",\r\n \"_FAVORITED\": \"已收藏\",\r\n \"_FAVORITE\": \"收藏\",\r\n \"_PLAY_ALL\": \"播放全部\",\r\n \"_ADD_TO_PLAYLIST\": \"添加到我的歌單\",\r\n \"_EDIT\": \"編輯\",\r\n \"_IMPORT\": \"導入\",\r\n \"_IMPORT_PLAYLIST\": \"導入歌單\",\r\n \"_ORIGIN_LINK\": \"連結\",\r\n \"_SONGS\": \"曲目\",\r\n \"_ARTISTS\": \"歌手\",\r\n \"_ALBUMS\": \"專輯名\",\r\n \"_OPERATION\": \"操作\",\r\n \"_CREATE_PLAYLIST\": \"創建歌單\",\r\n \"_CANCEL\": \"取消\",\r\n \"_CREATE_AND_ADD_PLAYLIST\": \"創建並添加\",\r\n \"_REMOVE_FROM_PLAYLIST\": \"從歌單裡移除\",\r\n \"_ADD_TO_QUEUE\": \"加入到當前播放\",\r\n \"_ARTIST\": \"歌手\",\r\n \"_ALBUM\": \"專輯名\",\r\n \"_PLAYLIST_TITLE\": \"歌單名稱\",\r\n \"_PLAYLIST_AUTHOR\": \"歌單作者\",\r\n \"_PLAYLIST_SONG_COUNT\": \"曲目數\",\r\n \"_PLAYLIST_COVER_IMAGE_URL\": \"封面圖片url\",\r\n \"_INPUT_PLAYLIST_TITLE\": \"鍵入歌單名稱\",\r\n \"_INPUT_PLAYLIST_COVER_IMAGE_URL\":  \"鍵入封面圖片URL\",\r\n \"_EDIT_PLAYLIST\": \"編輯歌單\",\r\n \"_REMOVE_PLAYLIST\": \"移除歌單\",\r\n \"_OPENING_LASTFM_PAGE\":\"正在打開Last.fm頁面...\",\r\n \"_CONFIRM_NOTICE_LASTFM\": \"請在打開的頁面點擊\\\"Yes, all access\\\", 允許Listen 1訪問你的帳戶。\",\r\n \"_AUTHORIZED_FINISHED\": \"已經完成授權\",\r\n \"_AUTHORIZED_REOPEN\": \"遇到問題，再次打開授權頁\",\r\n \"_PLAYLIST_LINK\": \"歌單連結\",\r\n \"_OPEN_PLAYLIST\": \"打開歌單\",\r\n \"_OPENING_GITHUB_PAGE\": \"正在打開Github.com頁面...\",\r\n \"_CONFIRM_NOTICE_GITHUB\": \"請在打開的頁面點擊\\\"Authencate\\\", 允許Listen 1訪問你的帳戶。\",\r\n \"_CREATE_PLAYLIST_BACKUP\": \"創建歌單備份\",\r\n \"_CREATE_PUBLIC_BACKUP\": \"創建公開備份\",\r\n \"_CREATE_PRIVATE_BACKUP\": \"創建私有備份\",\r\n \"_BACKUP_PLAYLIST\": \"備份歌單\",\r\n \"_BACKUP_WARNING\": \"重建應用程式或清除緩存檔案會導致我的歌單數據丟失，強烈建議在做這件事，備份我的歌單。\",\r\n \"_EXPORT_TO_LOCAL_FILE\": \"匯出到本地檔案\",\r\n \"_EXPORT_TO_GITHUB_GIST\": \"匯出到Github Gist\",\r\n \"_RECOVER_PLAYLIST\": \"從備份恢復\",\r\n \"_RECOVER_WARNING\": \"選擇備份檔案，恢復我的歌單。危險：恢復我的歌單會覆蓋現有的歌單。\",\r\n \"_RECOVER_FROM_LOCAL_FILE\": \"從本地檔案導入\",\r\n \"_RECOVER_FROM_GITHUB_GIST\": \"從Github Gist導入\",\r\n \"_CONNECT_TO_GITHUB\": \"連結到Github.com\",\r\n \"_STATUS\": \"狀態\",\r\n \"_RECONNECT\": \"重新連結\",\r\n \"_CANCEL_CONNECT\": \"取消連結\",\r\n \"_SHORTCUTS\": \"快速鍵\",\r\n \"_VIEW_SHORTCUTS_LIST\": \"查看快速鍵列表\",\r\n \"_GLOBAL_SHORTCUTS_NOTICE\": \"啟用全域快速鍵\",\r\n \"_LYRIC_DISPLAY\": \"歌詞顯示\",\r\n \"_SHOW_DESKTOP_LYRIC\": \"啟用桌面歌詞\",\r\n \"_SHOW_LYRIC_TRANSLATION\": \"外文歌詞顯示翻譯（播放頁）\",\r\n \"_SHOW_DESKTOP_LYRIC_TRANSLATION\": \"外文歌詞顯示翻譯（桌面歌詞）\",\r\n \"_CONNECT_TO_LASTFM\": \"連結到last.fm\",\r\n \"_ABOUT\": \"關於\",\r\n \"_HOMEPAGE\": \"主頁\",\r\n \"_EMAIL\": \"電郵\",\r\n \"_FEEDBACK\": \"回饋問題\",\r\n \"_DESIGNER\": \"主題設計\",\r\n \"_VERSION\": \"當前版本\",\r\n \"_LATEST_VERSION\": \"最新版本\",\r\n \"_LICENSE_NOTICE\": \"(本軟體基於MIT協定開源免費)\",\r\n \"_TOTAL_SONG_PREFIX\": \"總 \",\r\n \"_TOTAL_SONG_POSTFIX\": \" 首\",\r\n \"_CLEAR_ALL\": \"清空\",\r\n \"_SEARCH_PLACEHOLDER\": \"搜索\",\r\n \"_LANGUAGE\": \"語言\",\r\n \"_ADD_TO_PLAYLIST_SUCCESS\": \"成功添加到我創建的歌單\",\r\n \"_FAVORITE_PLAYLIST_SUCCESS\": \"收藏成功\",\r\n \"_EDIT_PLAYLIST_SUCCESS\": \"編輯歌單成功\",\r\n \"_IMPORTING_PLAYLIST\": \"正在導入歌單...\",\r\n \"_IMPORTING_PLAYLIST_SUCCESS\": \"導入歌單成功\",\r\n \"_REMOVE_PLAYLIST_SUCCESS\": \"移除歌單成功\",\r\n \"_UNFAVORITE_PLAYLIST_SUCCESS\": \"移除收藏成功\",\r\n \"_REMOVE_SONG_FROM_PLAYLIST_SUCCESS\": \"移除曲目成功\",\r\n \"_ADD_TO_QUEUE_SUCCESS\": \"添加到當前播放成功\",\r\n \"_COPYRIGHT_ISSUE\": \"版權限定無法播放，請搜索其他平臺\",\r\n \"_INPUT_NEW_PLAYLIST_TITLE\": \"鍵入新歌單名稱\",\r\n \"_FAIL_OPEN_PLAYLIST_URL\": \"未能打開鍵入的歌單位址\",\r\n \"_EXAMPLE\": \"例如 \",\r\n \"_CONFIRM\": \"確認\",\r\n \"_THEME\": \"樣式\",\r\n \"_THEME_WHITE\": \"簡約白\",\r\n \"_THEME_BLACK\": \"深空灰\",\r\n \"_THEME_MODERN_WHITE\": \"现代白\",\r\n \"_THEME_MODERN_BLACK\": \"现代黑\",\r\n \"_AUTO_CHOOSE_SOURCE\": \"自動切換源\",\r\n \"_AUTO_CHOOSE_SOURCE_NOTICE\": \"是否自動切換播放源（僅在播放音樂失敗後切換）\",\r\n \"_AUTO_CHOOSE_SOURCE_LIST\": \"從以下平臺搜索可用源\",\r\n \"_NOWPLAYING_DISPLAY\": \"正在播放顯示\",\r\n \"_NOWPLAYING_COVER_BACKGROUND_NOTICE\": \"顯示專輯封面作為背景\",\r\n \"_NOWPLAYING_BITRATE_NOTICE\": \"顯示比特率\",\r\n \"_NOWPLAYING_PLATFORM_NOTICE\": \"顯示音樂平臺\",\r\n \"_LOCAL_MUSIC\": \"本機音樂\",\r\n \"_ADD_LOCAL_SONGS\": \"添加本機音樂\",\r\n \"netease\": \"網易\",\r\n \"bilibili\": \"嗶哩嗶哩\",\r\n \"kugou\": \"酷狗\",\r\n \"kuwo\": \"酷我\",\r\n \"migu\": \"咪咕\",\r\n \"qq\": \"QQ\",\r\n \"xiami\": \"蝦米\",\r\n \"taihe\": \"千千\",\r\n \"localmusic\": \"本地\",\r\n \"_CLOSE_TAB_ACTION\": \"關閉標籤頁時行為\",\r\n \"_VALID_AFTER_RESTART\": \"需重載生效\",\r\n \"_QUIT_APPLICATION\": \"退出軟體\",\r\n \"_MINIMIZE_TO_BACKGROUND\": \"最小化到後臺\",\r\n \"_MY_CREATED_PLAYLIST\": \"我創建的歌單\",\r\n \"_MY_FAVORITE_PLAYLIST\": \"我收藏的歌單\",\r\n \"_RECOMMEND_PLAYLIST\": \"推薦歌單\",\r\n \"_LISTEN1_LOGIN_NOTICE\": \"Listen1不會傳輸你的賬號數據到任何第三方伺服器。\",\r\n \"_PASSWORD\": \"密碼\",\r\n \"_LOGIN\": \"登錄\",\r\n \"_LOGOUT\": \"退出登錄\",\r\n \"_LOGIN_ERROR\": \"登錄失敗，請檢查用戶名和密碼\",\r\n \"_LOGIN_EMAIL_ERROR\": \"請輸入正確的郵箱地址\",\r\n \"_LOGIN_COUNTRYCODE_ERROR\": \"請輸入正確的國家或地區代碼\",\r\n \"_LOGIN_PHONE_ERROR\": \"請輸入正確的手機號\",\r\n \"_LOGIN_PASSWORD_ERROR\": \"密碼不能為空\",\r\n \"_LOGIN_NETEASE\": \"登錄網易雲音樂\",\r\n \"_LOGIN_BY_MOBILE_PHONE\": \"手機號登錄\",\r\n \"_LOGIN_BY_EMAIL\": \"郵箱登錄\",\r\n \"_MOBILE_PHONE\": \"手機號\",\r\n \"_MY_NETEASE\": \"我的網易雲音樂\",\r\n \"_MY_QQ\": \"我的QQ音樂\",\r\n \"_FAIL_ALL_NOTICE\": \"當前播放列表沒有可播放的歌曲\",\r\n \"_SHORTCUTS_FUNCTION\": \"功能說明\",\r\n \"_GLOBAL_SHORTCUTS\": \"全局快捷鍵\",\r\n \"_PLAY_OR_PAUSE\": \"播放/暫停\",\r\n \"_PREVIOUS_TRACK\": \"上一首\",\r\n \"_NEXT_TRACK\": \"下一首\",\r\n \"_VOLUME_UP\": \"音量加\",\r\n \"_VOLUME_DOWN\": \"音量減\",\r\n \"_SHORTCUTS_NOT_SET\": \"空\",\r\n \"_QUICK_SEARCH\": \"快速搜索\",\r\n \"_SEARCH_PLAYLIST\": \"搜索歌單\",\r\n \"_KEYBOARD_SPACE\": \"空格\",\r\n \"_NOT_LOGIN_NICKNAME\": \"未登錄\",\r\n \"_LOGIN_DIALOG_NOTICE\": \"正在打開音樂平臺的登錄頁，請在打開網頁中完成登錄流程，然後點擊登錄完成\",\r\n \"_LOGIN_SUCCESS\": \"登錄完成\",\r\n \"_LOGIN_FAIL_RETRY\": \"登錄遇到問題，再打開登錄頁面\",\r\n \"_PROXY_SYSTEM\": \"使用系統代理\",\r\n \"_PROXY_DIRECT\": \"不使用代理\",\r\n \"_PROXY_CUSTOM\": \"自定義代理\",\r\n \"_PROXY_CONFIG\": \"代理設置\",\r\n \"_PROXY_NOT_SET\": \"未設置\",\r\n \"_MODIFY\": \"修改\",\r\n \"_PROTOCOL\": \"代理協議\",\r\n \"_HOST\": \"主機地址\",\r\n \"_PORT\": \"端口\",\r\n \"ZOOM_IN_OUT\": \"放大/縮小\"\r\n}\r\n"
  },
  {
    "path": "js/app.js",
    "content": "/* eslint-disable no-shadow */\n/* global l1Player require */\n/* global angular isElectron i18next i18nextHttpBackend Notyf notyf */\n/* global setPrototypeOfLocalStorage  */\n/* eslint-disable global-require */\n/* eslint-disable no-unused-vars */\n/* eslint-disable no-param-reassign */\n/* eslint-disable import/no-unresolved */\n\nconst sourceList = [\n  {\n    name: 'netease',\n    displayId: '_NETEASE_MUSIC',\n  },\n  {\n    name: 'qq',\n    displayId: '_QQ_MUSIC',\n  },\n  {\n    name: 'kugou',\n    displayId: '_KUGOU_MUSIC',\n  },\n  {\n    name: 'kuwo',\n    displayId: '_KUWO_MUSIC',\n  },\n  {\n    name: 'bilibili',\n    displayId: '_BILIBILI_MUSIC',\n  },\n  {\n    name: 'migu',\n    displayId: '_MIGU_MUSIC',\n  },\n  {\n    name: 'taihe',\n    displayId: '_TAIHE_MUSIC',\n  },\n];\n\nconst main = () => {\n  const app = angular.module('listenone', []);\n  setPrototypeOfLocalStorage();\n  app.config([\n    '$compileProvider',\n    ($compileProvider) => {\n      $compileProvider.imgSrcSanitizationWhitelist(\n        /^\\s*(https?|ftp|mailto|chrome-extension|moz-extension|file):/\n      );\n    },\n  ]);\n\n  app.run([\n    '$q',\n    ($q) => {\n      axios.Axios.prototype.request_original = axios.Axios.prototype.request;\n      axios.Axios.prototype.request = function new_req(config) {\n        return $q.when(this.request_original(config));\n      };\n      window.notyf = new Notyf({\n        duration: 5000,\n        ripple: true,\n        position: { x: 'center', y: 'top' },\n        types: [\n          {\n            type: 'warning',\n            background: 'darkorange',\n            icon: false,\n          },\n          {\n            type: 'info',\n            background: 'deepskyblue',\n            icon: false,\n          },\n          {\n            type: 'success',\n            className: 'notyf__toast--success',\n            backgroundColor: '#335eea',\n            icon: { className: 'notyf__icon--success', tagName: 'i' },\n          },\n          {\n            type: 'error',\n            className: 'notyf__toast--error',\n            backgroundColor: '#b90b2c',\n            icon: { className: 'notyf__icon--error', tagName: 'i' },\n          },\n        ],\n      });\n      window.notyf.warning = (msg, replace) => {\n        if (replace) {\n          notyf.dismissAll();\n        }\n        window.notyf.open({\n          type: 'warning',\n          message: msg,\n        });\n      };\n      window.notyf.info = (msg, replace) => {\n        if (replace) {\n          notyf.dismissAll();\n        }\n        window.notyf.open({\n          type: 'info',\n          message: msg,\n        });\n      };\n    },\n  ]);\n\n  l1Player.injectDirectives(app);\n\n  app.filter('playmode_title', () => (input) => {\n    switch (input) {\n      case 0:\n        return '顺序';\n      case 1:\n        return '随机';\n      case 2:\n        return '单曲循环';\n      default:\n        return '';\n    }\n  });\n\n  app.directive('customOnChange', () => {\n    const ret = {\n      restrict: 'A',\n      link: (scope, element, attrs) => {\n        const onChangeHandler = scope.$eval(attrs.customOnChange);\n        element.bind('change', onChangeHandler);\n      },\n    };\n    return ret;\n  });\n\n  app.directive('volumeWheel', () => (scope, element, attrs) => {\n    element.bind('mousewheel', () => {\n      l1Player.adjustVolume(window.event.wheelDelta > 0);\n    });\n  });\n\n  app.directive('pagination', () => ({\n    restrict: 'EA',\n    replace: false,\n    template: ` <button class=\"btn btn-sm btn-pagination\" ng-click=\"previousPage()\" ng-disabled=\"curpage==1\"> 上一页</button>\n    <label> {{curpage}}/{{totalpage}} 页 </label>\n    <button class=\"btn btn-sm btn-pagination\" ng-click=\"nextPage()\" ng-disabled=\"curpage==totalpage\"> 下一页</button>`,\n  }));\n\n  app.directive('errSrc', () => ({\n    // https://stackoverflow.com/questions/16310298/if-a-ngsrc-path-resolves-to-a-404-is-there-a-way-to-fallback-to-a-default\n    link: (scope, element, attrs) => {\n      element.bind('error', () => {\n        if (attrs.src !== attrs.errSrc) {\n          attrs.$set('src', attrs.errSrc);\n        }\n      });\n      attrs.$observe('ngSrc', (value) => {\n        if (!value && attrs.errSrc) {\n          attrs.$set('src', attrs.errSrc);\n        }\n      });\n    },\n  }));\n\n  app.directive('resize', ($window) => (scope, element) => {\n    const w = angular.element($window);\n    const changeHeight = () => {\n      const headerHeight = 90;\n      const footerHeight = 90;\n      element.css('height', `${w.height() - headerHeight - footerHeight}px`);\n    };\n    w.bind('resize', () => {\n      changeHeight(); // when window size gets changed\n    });\n    changeHeight(); // when page loads\n  });\n\n  app.directive('addAndPlay', [\n    () => ({\n      restrict: 'EA',\n      scope: {\n        song: '=addAndPlay',\n      },\n      link(scope, element, attrs) {\n        element.bind('click', (event) => {\n          l1Player.addTrack(scope.song);\n          l1Player.playById(scope.song.id);\n        });\n      },\n    }),\n  ]);\n\n  app.directive('addWithoutPlay', [\n    () => ({\n      restrict: 'EA',\n      scope: {\n        song: '=addWithoutPlay',\n      },\n      link(scope, element, attrs) {\n        element.bind('click', (event) => {\n          l1Player.addTrack(scope.song);\n          notyf.success(i18next.t('_ADD_TO_QUEUE_SUCCESS'));\n        });\n      },\n    }),\n  ]);\n\n  app.directive('openUrl', [\n    '$window',\n    ($window) => ({\n      restrict: 'EA',\n      scope: {\n        url: '=openUrl',\n      },\n      link(scope, element, attrs) {\n        element.bind('click', (event) => {\n          if (isElectron()) {\n            const { shell } = require('electron');\n            shell.openExternal(scope.url);\n          } else {\n            $window.open(scope.url, '_blank');\n          }\n        });\n      },\n    }),\n  ]);\n\n  app.directive('windowControl', [\n    '$window',\n    ($window) => ({\n      restrict: 'EA',\n      scope: {\n        action: '@windowControl',\n      },\n      link(scope, element, attrs) {\n        element.bind('click', (event) => {\n          if (isElectron()) {\n            const { ipcRenderer } = require('electron');\n            ipcRenderer.send('control', scope.action);\n          }\n        });\n      },\n    }),\n  ]);\n\n  app.directive('infiniteScroll', [\n    '$window',\n    '$rootScope',\n    ($window, $rootScope) => ({\n      restrict: 'EA',\n      scope: {\n        infiniteScroll: '&',\n        contentSelector: '=contentSelector',\n      },\n      link(scope, elements, attrs) {\n        elements.bind('scroll', (event) => {\n          if (scope.loading) {\n            return;\n          }\n          const containerElement = elements[0];\n          const contentElement = document.querySelector(scope.contentSelector);\n\n          const baseTop = containerElement.getBoundingClientRect().top;\n          const currentTop = contentElement.getBoundingClientRect().top;\n          const baseHeight = containerElement.offsetHeight;\n          const offset = baseTop - currentTop;\n\n          const bottom = offset + baseHeight;\n          const height = contentElement.offsetHeight;\n\n          const remain = height - bottom;\n          if (remain < 0) {\n            // page not shown\n            return;\n          }\n          const offsetToload = 10;\n          if (remain <= offsetToload) {\n            $rootScope.$broadcast('infinite_scroll:hit_bottom', '');\n          }\n        });\n      },\n    }),\n  ]);\n\n  /* drag drop support */\n  app.directive('dragDropZone', [\n    '$window',\n    ($window) => ({\n      restrict: 'A',\n      scope: {\n        dragobject: '=dragZoneObject',\n        dragtitle: '=dragZoneTitle',\n        dragtype: '=dragZoneType',\n        ondrop: '&dropZoneOndrop',\n        ondragleave: '&dropZoneOndragleave',\n        sortable: '=',\n      },\n      link(scope, element, attrs) {\n        // https://stackoverflow.com/questions/34200023/drag-drop-set-custom-html-as-drag-image\n        element.on('dragstart', (ev) => {\n          if (scope.dragobject === undefined) {\n            return;\n          }\n          if (scope.dragtype === undefined) {\n            return;\n          }\n          ev.dataTransfer.setData(\n            scope.dragtype,\n            JSON.stringify(scope.dragobject)\n          );\n          const elem = document.createElement('div');\n          elem.id = 'drag-ghost';\n          elem.innerHTML = scope.dragtitle;\n          elem.style.position = 'absolute';\n          elem.style.top = '-1000px';\n          elem.style.padding = '3px';\n          elem.style.background = '#eeeeee';\n          elem.style.color = '#333';\n          elem.style['border-radius'] = '3px';\n\n          document.body.appendChild(elem);\n          ev.dataTransfer.setDragImage(elem, 0, 40);\n        });\n        element.on('dragend', () => {\n          const ghost = document.getElementById('drag-ghost');\n          if (ghost.parentNode) {\n            ghost.parentNode.removeChild(ghost);\n          }\n        });\n        element.on('dragenter', (event) => {\n          let dragType = '';\n          if (event.dataTransfer.types.length > 0) {\n            [dragType] = event.dataTransfer.types;\n          }\n          if (\n            scope.dragtype === 'application/listen1-myplaylist' &&\n            dragType === 'application/listen1-song'\n          ) {\n            element[0].classList.add('dragover');\n          }\n        });\n        element.on('dragleave', (event) => {\n          element[0].classList.remove('dragover');\n          if (scope.ondragleave !== undefined) {\n            scope.ondragleave();\n          }\n          if (scope.sortable) {\n            const target = element[0];\n            target.style['z-index'] = '0';\n            target.style['border-bottom'] = 'solid 2px transparent';\n            target.style['border-top'] = 'solid 2px transparent';\n          }\n        });\n\n        element.on('dragover', (event) => {\n          event.preventDefault();\n          const dragLineColor = '#FF4444';\n          let dragType = '';\n          if (event.dataTransfer.types.length > 0) {\n            [dragType] = event.dataTransfer.types;\n          }\n\n          if (scope.dragtype === dragType && scope.sortable) {\n            event.dataTransfer.dropEffect = 'move';\n            const bounding = event.target.getBoundingClientRect();\n            const offset = bounding.y + bounding.height / 2;\n\n            const direction = event.clientY - offset > 0 ? 'bottom' : 'top';\n            const target = element[0];\n            if (direction === 'bottom') {\n              target.style['border-bottom'] = `solid 2px ${dragLineColor}`;\n              target.style['border-top'] = 'solid 2px transparent';\n              target.style['z-index'] = '9';\n            } else if (direction === 'top') {\n              target.style['border-top'] = `solid 2px ${dragLineColor}`;\n              target.style['border-bottom'] = 'solid 2px transparent';\n              target.style['z-index'] = '9';\n            }\n          } else if (\n            scope.dragtype === 'application/listen1-myplaylist' &&\n            dragType === 'application/listen1-song'\n          ) {\n            event.dataTransfer.dropEffect = 'copy';\n          }\n        });\n\n        element.on('drop', (event) => {\n          if (scope.ondrop === undefined) {\n            return;\n          }\n          const [dragType] = event.dataTransfer.types;\n          const jsonString = event.dataTransfer.getData(dragType);\n          const data = JSON.parse(jsonString);\n          let direction = '';\n          const bounding = event.target.getBoundingClientRect();\n          const offset = bounding.y + bounding.height / 2;\n          direction = event.clientY - offset > 0 ? 'bottom' : 'top';\n          // https://stackoverflow.com/questions/19889615/can-an-angular-directive-pass-arguments-to-functions-in-expressions-specified-in\n          scope.ondrop({ arg1: data, arg2: dragType, arg3: direction });\n\n          element[0].classList.remove('dragover');\n          if (scope.sortable) {\n            const target = element[0];\n            target.style['border-top'] = 'solid 2px transparent';\n            target.style['border-bottom'] = 'solid 2px transparent';\n          }\n        });\n      },\n    }),\n  ]);\n\n  app.directive('draggableBar', [\n    '$document',\n    '$rootScope',\n    ($document, $rootScope) => (scope, element, attrs) => {\n      let x;\n      let container;\n      const { mode } = attrs;\n\n      function onMyMousedown() {\n        if (mode === 'play') {\n          $rootScope.$broadcast('dragbar:changing_progress', true);\n        }\n      }\n\n      function onMyMouseup() {\n        if (mode === 'play') {\n          $rootScope.$broadcast('dragbar:changing_progress', false);\n        }\n      }\n\n      function onMyUpdateProgress(progress) {\n        if (mode === 'play') {\n          $rootScope.$broadcast('dragbar:myprogress', progress * 100);\n        }\n        if (mode === 'volume') {\n          l1Player.setVolume(progress * 100);\n          l1Player.unmute();\n        }\n      }\n\n      function onMyCommitProgress(progress) {\n        if (mode === 'play') {\n          l1Player.seek(progress);\n        }\n        if (mode === 'volume') {\n          const current = localStorage.getObject('player-settings');\n          current.volume = progress * 100;\n          localStorage.setObject('player-settings', current);\n        }\n      }\n\n      function commitProgress(progress) {\n        onMyCommitProgress(progress);\n      }\n\n      function updateProgress() {\n        if (container) {\n          if (x < 0) {\n            x = 0;\n          } else if (x > container.right - container.left) {\n            x = container.right - container.left;\n          }\n        }\n        const progress = x / (container.right - container.left);\n        onMyUpdateProgress(progress);\n      }\n\n      function mousemove(event) {\n        x = event.clientX - container.left;\n        updateProgress();\n      }\n\n      function mouseup() {\n        const progress = x / (container.right - container.left);\n        commitProgress(progress);\n        $document.off('mousemove', mousemove);\n        $document.off('mouseup', mouseup);\n        onMyMouseup();\n      }\n\n      element.on('mousedown', (event) => {\n        onMyMousedown();\n        container = document.getElementById(attrs.id).getBoundingClientRect();\n        // Prevent default dragging of selected content\n        event.preventDefault();\n        x = event.clientX - container.left;\n        updateProgress();\n        $document.on('mousemove', mousemove);\n        $document.on('mouseup', mouseup);\n      });\n    },\n  ]);\n};\n\ni18next.use(i18nextHttpBackend).init({\n  lng: 'zh-CN',\n  fallbackLng: 'zh-CN',\n  supportedLngs: ['zh-CN', 'zh-TC', 'en-US', 'fr-FR', 'ko-KR', 'pt-BR'],\n  preload: ['zh-CN', 'zh-TC', 'en-US', 'fr-FR', 'ko-KR', 'pt-BR'],\n  debug: false,\n  backend: {\n    loadPath: 'i18n/{{lng}}.json',\n  },\n});\n\nmain();\n"
  },
  {
    "path": "js/background.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global GithubClient */\nchrome.action.onClicked.addListener((tab) => {\n  chrome.tabs.create(\n    {\n      url: chrome.runtime.getURL('listen1.html'),\n    },\n    (new_tab) => {\n      // Tab opened.\n    }\n  );\n});\n\n// const MOBILE_UA =\n//   'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30';\n\n// function hack_referer_header(details) {\n//   const replace_referer = true;\n//   let replace_origin = true;\n//   let add_referer = true;\n//   let add_origin = true;\n\n//   let referer_value = '';\n//   let origin_value = '';\n//   let ua_value = '';\n\n//   if (details.url.includes('://music.163.com/')) {\n//     referer_value = 'https://music.163.com/';\n//   }\n//   if (details.url.includes('://interface3.music.163.com/')) {\n//     referer_value = 'https://music.163.com/';\n//   }\n//   if (details.url.includes('://gist.githubusercontent.com/')) {\n//     referer_value = 'https://gist.githubusercontent.com/';\n//   }\n\n//   if (details.url.includes('.xiami.com/')) {\n//     add_origin = false;\n//     add_referer = false;\n//     // referer_value = \"https://www.xiami.com\";\n//   }\n\n//   if (details.url.includes('c.y.qq.com/')) {\n//     referer_value = 'https://y.qq.com/';\n//     origin_value = 'https://y.qq.com';\n//   }\n//   if (\n//     details.url.includes('i.y.qq.com/') ||\n//     details.url.includes('qqmusic.qq.com/') ||\n//     details.url.includes('music.qq.com/') ||\n//     details.url.includes('imgcache.qq.com/')\n//   ) {\n//     referer_value = 'https://y.qq.com/';\n//   }\n\n//   if (details.url.includes('.kugou.com/')) {\n//     referer_value = 'https://www.kugou.com/';\n//     ua_value = MOBILE_UA;\n//   }\n//   if (details.url.includes('m.kugou.com/')) {\n//     ua_value = MOBILE_UA;\n//   }\n//   if (details.url.includes('.kuwo.cn/')) {\n//     referer_value = 'https://www.kuwo.cn/';\n//   }\n\n//   if (\n//     details.url.includes('.bilibili.com/') ||\n//     details.url.includes('.bilivideo.com/')\n//   ) {\n//     referer_value = 'https://www.bilibili.com/';\n//     replace_origin = false;\n//     add_origin = false;\n//   }\n\n//   if (details.url.includes('.bilivideo.cn')) {\n//     referer_value = 'https://www.bilibili.com/';\n//     origin_value = 'https://www.bilibili.com/';\n//     add_referer = true;\n//     add_origin = true;\n//   }\n\n//   if (\n//     details.url.includes('.taihe.com/') ||\n//     details.url.includes('music.91q.com')\n//   ) {\n//     referer_value = 'https://music.taihe.com/';\n//   }\n\n//   if (details.url.includes('.migu.cn')) {\n//     referer_value = 'https://music.migu.cn/v3/music/player/audio?from=migu';\n//   }\n\n//   if (details.url.includes('m.music.migu.cn')) {\n//     referer_value = 'https://m.music.migu.cn/';\n//   }\n\n//   if (\n//     details.url.includes('app.c.nf.migu.cn') ||\n//     details.url.includes('d.musicapp.migu.cn')\n//   ) {\n//     ua_value = MOBILE_UA;\n//     add_origin = false;\n//     add_referer = false;\n//   }\n\n//   if (details.url.includes('jadeite.migu.cn')) {\n//     ua_value = 'okhttp/3.12.12';\n//     add_origin = false;\n//     add_referer = false;\n//   }\n\n//   if (origin_value === '') {\n//     origin_value = referer_value;\n//   }\n\n//   let isRefererSet = false;\n//   let isOriginSet = false;\n//   let isUASet = false;\n//   const headers = details.requestHeaders;\n//   const blockingResponse = {};\n\n//   for (let i = 0, l = headers.length; i < l; i += 1) {\n//     if (\n//       replace_referer &&\n//       headers[i].name === 'Referer' &&\n//       referer_value !== ''\n//     ) {\n//       headers[i].value = referer_value;\n//       isRefererSet = true;\n//     }\n//     if (replace_origin && headers[i].name === 'Origin' && origin_value !== '') {\n//       headers[i].value = origin_value;\n//       isOriginSet = true;\n//     }\n//     if (headers[i].name === 'User-Agent' && ua_value !== '') {\n//       headers[i].value = ua_value;\n//       isUASet = true;\n//     }\n//   }\n\n//   if (add_referer && !isRefererSet && referer_value !== '') {\n//     headers.push({\n//       name: 'Referer',\n//       value: referer_value,\n//     });\n//   }\n\n//   if (add_origin && !isOriginSet && origin_value !== '') {\n//     headers.push({\n//       name: 'Origin',\n//       value: origin_value,\n//     });\n//   }\n\n//   if (!isUASet && ua_value !== '') {\n//     headers.push({\n//       name: 'User-Agent',\n//       value: ua_value,\n//     });\n//   }\n\n//   blockingResponse.requestHeaders = headers;\n//   return blockingResponse;\n// }\n\n// const urls = [\n//   '*://*.music.163.com/*',\n//   '*://music.163.com/*',\n//   '*://*.xiami.com/*',\n//   '*://i.y.qq.com/*',\n//   '*://c.y.qq.com/*',\n//   '*://*.kugou.com/*',\n//   '*://*.kuwo.cn/*',\n//   '*://*.bilibili.com/*',\n//   '*://*.bilivideo.com/*',\n//   '*://*.bilivideo.cn/*',\n//   '*://*.migu.cn/*',\n//   '*://*.githubusercontent.com/*',\n// ];\n\n// try {\n//   chrome.webRequest.onBeforeSendHeaders.addListener(\n//     hack_referer_header,\n//     {\n//       urls,\n//     },\n//     ['requestHeaders', 'blocking', 'extraHeaders']\n//   );\n// } catch (err) {\n//   // before chrome v72, extraHeader is not supported\n//   chrome.webRequest.onBeforeSendHeaders.addListener(\n//     hack_referer_header,\n//     {\n//       urls,\n//     },\n//     ['requestHeaders', 'blocking']\n//   );\n// }\n\n\n/**\n * Get tokens.\n */\n\nchrome.runtime.onMessage.addListener((request, sender, sendResponse) => {\n  if (request.type !== 'code') {\n    return;\n  }\n\n  GithubClient.github.handleCallback(request.code);\n  sendResponse();\n});\n"
  },
  {
    "path": "js/bridge.js",
    "content": "/* eslint-disable no-unused-vars */\n/*\nbuild a bridge between UI and audio player\n\naudio player has 2 modes, but share same protocol: front and background.\n\n* front: audio player and UI are in same environment\n* background: audio player is in background page.\n\n*/\n\nfunction getFrontPlayer() {\n  return window.threadPlayer;\n}\n\nfunction getBackgroundPlayer() {\n  return chrome.extension.getBackgroundPage().threadPlayer;\n}\n\nfunction getBackgroundPlayerAsync(callback) {\n  (chrome || browser).runtime.getBackgroundPage((w) => {\n    callback(w.threadPlayer);\n  });\n}\n\nfunction getPlayer(mode) {\n  if (mode === 'front') {\n    return getFrontPlayer();\n  }\n  if (mode === 'background') {\n    return getBackgroundPlayer();\n  }\n  return undefined;\n}\n\nfunction getPlayerAsync(mode, callback) {\n  if (mode === 'front') {\n    const player = getFrontPlayer();\n    return callback(player);\n  }\n  if (mode === 'background') {\n    return getBackgroundPlayerAsync(callback);\n  }\n  return undefined;\n}\nconst frontPlayerListener = [];\nfunction addFrontPlayerListener(listener) {\n  frontPlayerListener.push(listener);\n}\n\nfunction addBackgroundPlayerListener(listener) {\n  return (chrome || browser).runtime.onMessage.addListener(\n    (msg, sender, res) => {\n      if (!msg.type.startsWith('BG_PLAYER:')) {\n        return null;\n      }\n      return listener(msg, sender, res);\n    }\n  );\n}\n\nfunction addPlayerListener(mode, listener) {\n  if (mode === 'front') {\n    return addFrontPlayerListener(listener);\n  }\n  if (mode === 'background') {\n    return addBackgroundPlayerListener(listener);\n  }\n  return null;\n}\n\nfunction frontPlayerSendMessage(message) {\n  if (frontPlayerListener !== []) {\n    frontPlayerListener.forEach((listener) => {\n      listener(message);\n    });\n  }\n}\n\nfunction backgroundPlayerSendMessage(message) {\n  (chrome || browser).runtime.sendMessage(message);\n}\n\nfunction playerSendMessage(mode, message) {\n  if (mode === 'front') {\n    frontPlayerSendMessage(message);\n  }\n  if (mode === 'background') {\n    backgroundPlayerSendMessage(message);\n  }\n}\n"
  },
  {
    "path": "js/controller/auth.js",
    "content": "/* eslint-disable import/no-unresolved */\n/* eslint-disable global-require */\n/* global angular MediaService isElectron require */\nangular.module('listenone').controller('AuthController', [\n  '$scope',\n  ($scope) => {\n    $scope.loginProgress = false;\n    $scope.loginType = 'email';\n    $scope.loginSourceList = MediaService.getLoginProviders().map(\n      (i) => i.name\n    );\n    $scope.refreshAuthStatus = () => {\n      $scope.loginSourceList.map((source) =>\n        MediaService.getUser(source).success((data) => {\n          if (data.status === 'success') {\n            $scope.setMusicAuth(source, data.data);\n          } else {\n            $scope.setMusicAuth(source, {});\n          }\n        })\n      );\n    };\n\n    $scope.logout = (source) => {\n      $scope.setMusicAuth(source, {});\n      MediaService.logout(source);\n    };\n\n    $scope.is_login = (source) =>\n      $scope.musicAuth[source] && $scope.musicAuth[source].is_login;\n\n    $scope.musicAuth = {};\n\n    $scope.setMusicAuth = (source, data) => {\n      $scope.musicAuth[source] = data;\n    };\n\n    $scope.getLoginUrl = (source) => MediaService.getLoginUrl(source);\n\n    $scope.openLogin = (source) => {\n      const url = $scope.getLoginUrl(source);\n      if (isElectron()) {\n        const { ipcRenderer } = require('electron');\n        return ipcRenderer.send('openUrl', url);\n      }\n      return window.open(url, '_blank');\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/instant_search.js",
    "content": "/* eslint-disable no-param-reassign */\n/* global angular i18next MediaService sourceList */\nangular.module('listenone').controller('InstantSearchController', [\n  '$scope',\n  '$timeout',\n  '$rootScope',\n  ($scope, $timeout, $rootScope) => {\n    $scope.originpagelog = { allmusic: 1 };\n    sourceList.forEach((i) => {\n      $scope.originpagelog[i.name] = 1;\n    });\n    $scope.sourceList = sourceList.filter((i) => i.searchable !== false);\n    $scope.tab = sourceList[0].name;\n    $scope.keywords = '';\n    $scope.loading = false;\n    $scope.curpagelog = { ...$scope.originpagelog };\n    $scope.totalpagelog = { ...$scope.originpagelog };\n    $scope.curpage = 1;\n    $scope.totalpage = 1;\n    $scope.searchType = 0;\n\n    function updateCurrentPage(cp) {\n      if (cp === -1) {\n        // when search words changes,pagenums should be reset.\n        $scope.curpagelog = { ...$scope.originpagelog };\n        $scope.curpage = 1;\n      } else if (cp >= 0) {\n        $scope.curpagelog[$scope.tab] = cp;\n        $scope.curpage = $scope.curpagelog[$scope.tab];\n      } else {\n        // only tab changed\n        $scope.curpage = $scope.curpagelog[$scope.tab];\n      }\n    }\n\n    function updateTotalPage(totalItem) {\n      if (totalItem === -1) {\n        $scope.totalpagelog = { ...$scope.originpagelog };\n        $scope.totalpage = 1;\n      } else if (totalItem >= 0) {\n        $scope.totalpage = Math.ceil(totalItem / 20);\n        $scope.totalpagelog[$scope.tab] = $scope.totalpage;\n      } else {\n        // just switch tab\n        $scope.totalpage = $scope.totalpagelog[$scope.tab];\n      }\n    }\n\n    function performSearch() {\n      $rootScope.$broadcast('search:keyword_change', $scope.keywords);\n      MediaService.search($scope.tab, {\n        keywords: $scope.keywords,\n        curpage: $scope.curpage,\n        type: $scope.searchType,\n      }).success((data) => {\n        // update the textarea\n        data.result.forEach((r) => {\n          r.sourceName = i18next.t(r.source);\n        });\n        $scope.result = data.result;\n        updateTotalPage(data.total);\n        $scope.loading = false;\n        // scroll back to top when finish searching\n        document.querySelector('.site-wrapper-innerd').scrollTo({ top: 0 });\n      });\n    }\n\n    $scope.changeSourceTab = (newTab) => {\n      $scope.loading = true;\n      $scope.tab = newTab;\n      $scope.result = [];\n      updateCurrentPage();\n      updateTotalPage();\n\n      if ($scope.keywords === '') {\n        $scope.loading = false;\n      } else {\n        performSearch();\n      }\n    };\n\n    $scope.changeSearchType = (newSearchType) => {\n      $scope.loading = true;\n      $scope.searchType = newSearchType;\n      $scope.result = [];\n      updateCurrentPage();\n      updateTotalPage();\n\n      if ($scope.keywords === '') {\n        $scope.loading = false;\n      } else {\n        performSearch();\n      }\n    };\n    $scope.isActiveTab = (tab) => $scope.tab === tab;\n\n    $scope.isSearchType = (searchType) => $scope.searchType === searchType;\n\n    // eslint-disable-next-line consistent-return\n    function renderSearchPage() {\n      updateCurrentPage(-1);\n      updateTotalPage(-1);\n      if (!$scope.keywords || $scope.keywords.length === 0) {\n        $scope.result = [];\n        return 0;\n      }\n\n      performSearch();\n    }\n\n    $scope.$watch('keywords', (tmpStr) => {\n      if (tmpStr === $scope.keywords) {\n        // if searchStr is still the same..\n        // go ahead and retrieve the data\n        renderSearchPage();\n      }\n    });\n\n    $scope.enterEvent = (e) => {\n      const keycode = window.event ? e.keyCode : e.which;\n      if (keycode === 13) {\n        // enter key\n        renderSearchPage();\n      }\n    };\n\n    $scope.nextPage = () => {\n      $scope.curpagelog[$scope.tab] += 1;\n      $scope.curpage = $scope.curpagelog[$scope.tab];\n      performSearch();\n    };\n\n    $scope.previousPage = () => {\n      $scope.curpagelog[$scope.tab] -= 1;\n      $scope.curpage = $scope.curpagelog[$scope.tab];\n      performSearch();\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/my_playlist.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global angular MediaService */\n\nangular.module('listenone').controller('MyPlayListController', [\n  '$scope',\n  '$timeout',\n  ($scope, $timeout) => {\n    $scope.myplaylists = [];\n    $scope.favoriteplaylists = [];\n\n    $scope.loadMyPlaylist = () => {\n      MediaService.showMyPlaylist().success((data) => {\n        $scope.$evalAsync(() => {\n          $scope.myplaylists = data.result;\n        });\n      });\n    };\n\n    $scope.loadFavoritePlaylist = () => {\n      MediaService.showFavPlaylist().success((data) => {\n        $scope.$evalAsync(() => {\n          $scope.favoriteplaylists = data.result;\n        });\n      });\n    };\n\n    $scope.$watch('current_tag', (newValue, oldValue) => {\n      if (newValue !== oldValue) {\n        if (newValue === '1') {\n          $scope.myplaylists = [];\n          $scope.loadMyPlaylist();\n        }\n      }\n    });\n    $scope.$on('myplaylist:update', (event, data) => {\n      $scope.loadMyPlaylist();\n    });\n\n    $scope.$on('favoriteplaylist:update', (event, data) => {\n      $scope.loadFavoritePlaylist();\n    });\n  },\n]);\n"
  },
  {
    "path": "js/controller/navigation.js",
    "content": "/* eslint-disable import/no-unresolved */\n/* eslint-disable global-require */\n/* eslint-disable no-shadow */\n/* eslint-disable no-unused-vars */\n/* eslint-disable no-param-reassign */\n/* global angular notyf i18next MediaService l1Player hotkeys isElectron require GithubClient lastfm */\n// control main view of page, it can be called any place\nangular.module('listenone').controller('NavigationController', [\n  '$scope',\n  '$timeout',\n  '$rootScope',\n  ($scope, $timeout, $rootScope) => {\n    $rootScope.page_title = { title: 'Listen 1', artist: '', status: '' }; // eslint-disable-line no-param-reassign\n    $scope.window_url_stack = [];\n    $scope.window_poped_url_stack = [];\n    $scope.current_tag = 2;\n    $scope.is_window_hidden = 1;\n    $scope.is_dialog_hidden = 1;\n    $scope.tag_params = {};\n\n    $scope.songs = [];\n    $scope.current_list_id = -1;\n\n    $scope.dialog_song = '';\n    $scope.dialog_type = 0;\n    $scope.dialog_title = '';\n\n    $scope.isDoubanLogin = false;\n\n    $scope.lastfm = lastfm;\n\n    $scope.isOpenSidebar = true;\n\n    $scope.$on('isdoubanlogin:update', (event, data) => {\n      $scope.isDoubanLogin = data;\n    });\n\n    // isOpenSidebar\n\n    if (localStorage.getObject('openSidebar') !== null) {\n      $scope.isOpenSidebar = localStorage.getObject('openSidebar');\n    }\n    $scope.openSidebar = () => {\n      $scope.isOpenSidebar = !$scope.isOpenSidebar;\n      localStorage.setObject('openSidebar', $scope.isOpenSidebar);\n    };\n    // tag\n    $scope.showTag = (tag_id, tag_params) => {\n      $scope.current_tag = tag_id;\n      $scope.is_window_hidden = 1;\n      $scope.window_url_stack = [];\n      $scope.window_poped_url_stack = [];\n      $scope.tag_params = tag_params;\n      if (tag_id === 6) {\n        $rootScope.$broadcast('myplatform:update', tag_params.user);\n      }\n      $scope.closeWindow();\n    };\n\n    $scope.$on('search:keyword_change', (event, data) => {\n      $scope.showTag(3);\n    });\n\n    // playlist window\n    $scope.resetWindow = (offset) => {\n      if (offset === undefined) {\n        offset = 0;\n      }\n      $scope.cover_img_url = 'images/loading.svg';\n      $scope.playlist_title = '';\n      $scope.playlist_source_url = '';\n      $scope.songs = [];\n      $scope.window_type = 'list';\n      $timeout(() => {\n        document.getElementsByClassName('browser')[0].scrollTop = offset;\n      }, 0);\n    };\n\n    $scope.closeWindow = (offset) => {\n      if (offset === undefined) {\n        offset = 0;\n      }\n      $scope.is_window_hidden = 1;\n      $scope.resetWindow(offset);\n      $scope.window_url_stack = [];\n      $scope.window_poped_url_stack = [];\n    };\n\n    function refreshWindow(url, offset = 0) {\n      if (url === '/now_playing') {\n        $scope.window_type = 'track';\n        return;\n      }\n      const listId = new URL(url, window.location).searchParams.get('list_id');\n      MediaService.getPlaylist(listId).success((data) => {\n        $scope.songs = data.tracks;\n        $scope.list_id = data.info.id;\n        $scope.cover_img_url = data.info.cover_img_url;\n        $scope.playlist_title = data.info.title;\n        $scope.playlist_source_url = data.info.source_url;\n        $scope.is_mine = data.info.id.slice(0, 2) === 'my';\n        $scope.is_local = data.info.id.slice(0, 2) === 'lm';\n        $timeout(() => {\n          document.getElementsByClassName('browser')[0].scrollTop = offset;\n        }, 0);\n      });\n    }\n    $scope.popWindow = () => {\n      if ($scope.window_url_stack.length === 0) {\n        return;\n      }\n      let poped = $scope.window_url_stack.pop();\n      if ($scope.getCurrentUrl() === '/now_playing') {\n        poped = $scope.window_url_stack.pop();\n      }\n      $scope.window_poped_url_stack.push(poped.url);\n      if ($scope.window_url_stack.length === 0) {\n        $scope.closeWindow(poped.offset);\n      } else {\n        $scope.resetWindow(poped.offset);\n        const lastWindow = $scope.window_url_stack.slice(-1)[0];\n        refreshWindow(lastWindow.url, poped.offset);\n      }\n    };\n\n    $scope.toggleNowPlaying = () => {\n      if ($scope.getCurrentUrl() === '/now_playing') {\n        $scope.popWindow();\n        return;\n      }\n      if (!$scope.menuHidden) {\n        $scope.togglePlaylist();\n      }\n      // save current scrolltop\n      $scope.is_window_hidden = 0;\n      $scope.resetWindow();\n\n      $scope.window_url_stack.push({\n        url: '/now_playing',\n        offset: document.getElementsByClassName('browser')[0].scrollTop,\n      });\n      $scope.window_poped_url_stack = [];\n\n      $scope.window_type = 'track';\n    };\n\n    $scope.forwardWindow = () => {\n      if ($scope.window_poped_url_stack.length === 0) {\n        return;\n      }\n\n      $scope.resetWindow();\n      const url = $scope.window_poped_url_stack.pop();\n      $scope.window_url_stack.push({\n        url,\n        offset: 0,\n      });\n      refreshWindow(url);\n    };\n\n    $scope.getCurrentUrl = () =>\n      ($scope.window_url_stack.slice(-1)[0] || {}).url;\n\n    $scope.showPlaylist = (list_id, useCache) => {\n      $scope.clearFilter();\n      const url = `/playlist?list_id=${list_id}`;\n      // save current scrolltop\n      const offset = document.getElementsByClassName('browser')[0].scrollTop;\n      if ($scope.getCurrentUrl() === url) {\n        return;\n      }\n      $scope.is_window_hidden = 0;\n      $scope.resetWindow();\n\n      if ($scope.getCurrentUrl() === '/now_playing') {\n        // if now playing is top, pop it\n        $scope.window_url_stack.pop();\n      }\n      $scope.window_url_stack.push({ url, offset });\n      $scope.window_poped_url_stack = [];\n\n      const listId = new URL(url, window.location).searchParams.get('list_id');\n      MediaService.getPlaylist(listId, useCache).success((data) => {\n        if (data.status === '0') {\n          notyf.info(data.reason);\n          $scope.popWindow();\n          return;\n        }\n        $scope.songs = data.tracks;\n        $scope.cover_img_url = data.info.cover_img_url;\n        $scope.playlist_title = data.info.title;\n        $scope.playlist_source_url = data.info.source_url;\n        $scope.list_id = data.info.id;\n        $scope.is_mine = data.info.id.slice(0, 2) === 'my';\n        $scope.is_local = data.info.id.slice(0, 2) === 'lm';\n\n        MediaService.queryPlaylist(data.info.id, 'favorite').success((res) => {\n          // success 函数可能在异步回调中执行，需要手动触发脏检查\n          $timeout(() => {\n            $scope.is_favorite = res.result;\n          }, 0);\n        });\n\n        $scope.window_type = 'list';\n      });\n    };\n\n    $scope.directplaylist = (list_id) => {\n      MediaService.getPlaylist(list_id).success((data) => {\n        $scope.songs = data.tracks;\n        $scope.current_list_id = list_id;\n        l1Player.setNewPlaylist($scope.songs);\n        l1Player.play();\n      });\n    };\n\n    $scope.showDialog = (dialog_type, data) => {\n      $scope.is_dialog_hidden = 0;\n      $scope.dialog_data = data;\n      const dialogWidth = 400;\n      const dialogHeight = 430;\n      const left = window.innerWidth / 2 - dialogWidth / 2;\n      const top = window.innerHeight / 2 - dialogHeight / 2;\n\n      $scope.myStyle = {\n        left: `${left}px`,\n        top: `${top}px`,\n      };\n      $scope.dialog_type = dialog_type;\n      if (dialog_type === 0) {\n        $scope.dialog_title = i18next.t('_ADD_TO_PLAYLIST');\n        $scope.dialog_song = data;\n        MediaService.showMyPlaylist().success((res) => {\n          $scope.myplaylist = res.result;\n        });\n      }\n\n      // if (dialog_type === 2) {\n      //   $scope.dialog_title = '登录豆瓣';\n      //   $scope.dialog_type = 2;\n      // }\n\n      if (dialog_type === 3) {\n        $scope.dialog_title = i18next.t('_EDIT_PLAYLIST');\n        $scope.dialog_cover_img_url = data.cover_img_url;\n        $scope.dialog_playlist_title = data.playlist_title;\n      }\n      if (dialog_type === 4) {\n        $scope.dialog_title = i18next.t('_CONNECT_TO_LASTFM');\n      }\n      if (dialog_type === 5) {\n        $scope.dialog_title = i18next.t('_OPEN_PLAYLIST');\n      }\n      if (dialog_type === 6) {\n        $scope.dialog_title = i18next.t('_IMPORT_PLAYLIST');\n        MediaService.showMyPlaylist().success((res) => {\n          $scope.myplaylist = res.result;\n        });\n      }\n      if (dialog_type === 7) {\n        $scope.dialog_title = i18next.t('_CONNECT_TO_GITHUB');\n      }\n      if (dialog_type === 8) {\n        $scope.dialog_title = i18next.t('_EXPORT_TO_GITHUB_GIST');\n        GithubClient.gist.listExistBackup().then(\n          (res) => {\n            $scope.myBackup = res;\n          },\n          (err) => {\n            $scope.myBackup = [];\n          }\n        );\n      }\n      if (dialog_type === 10) {\n        $scope.dialog_title = i18next.t('_RECOVER_FROM_GITHUB_GIST');\n        GithubClient.gist.listExistBackup().then(\n          (res) => {\n            $scope.myBackup = res;\n          },\n          (err) => {\n            $scope.myBackup = [];\n          }\n        );\n      }\n      if (dialog_type === 11) {\n        $scope.dialog_title = i18next.t('_LOGIN');\n      }\n      if (dialog_type === 12) {\n        $scope.dialog_title = i18next.t('_PROXY_CONFIG');\n      }\n    };\n\n    $scope.onSidebarPlaylistDrop = (\n      playlistType,\n      list_id,\n      data,\n      dataType,\n      direction\n    ) => {\n      if (playlistType === 'my' && dataType === 'application/listen1-song') {\n        $scope.addMyPlaylist(list_id, data);\n      } else if (\n        (playlistType === 'my' &&\n          dataType === 'application/listen1-myplaylist') ||\n        (playlistType === 'favorite' &&\n          dataType === 'application/listen1-favoriteplaylist')\n      ) {\n        MediaService.insertMyplaylistToMyplaylists(\n          playlistType,\n          data.info.id,\n          list_id,\n          direction\n        ).success(() => {\n          if (playlistType === 'my') {\n            $rootScope.$broadcast('myplaylist:update');\n          }\n          if (playlistType === 'favorite') {\n            $rootScope.$broadcast('favoriteplaylist:update');\n          }\n        });\n      }\n    };\n    $scope.playlistFilter = { key: '' };\n\n    $scope.clearFilter = () => {\n      $scope.playlistFilter.key = '';\n    };\n    $scope.fieldFilter = (song) => {\n      if ($scope.playlistFilter.key === '') {\n        return true;\n      }\n      return (\n        song.title.includes($scope.playlistFilter.key) ||\n        song.artist.includes($scope.playlistFilter.key) ||\n        (song.album && song.album.includes($scope.playlistFilter.key))\n      );\n    };\n    $scope.onPlaylistSongDrop = (list_id, song, data, dataType, direction) => {\n      if (dataType === 'application/listen1-song') {\n        // insert song\n        MediaService.insertTrackToMyPlaylist(\n          list_id,\n          data,\n          song,\n          direction\n        ).success((playlist) => {\n          $scope.closeDialog();\n          if (list_id === $scope.list_id) {\n            $scope.$evalAsync(() => {\n              $scope.songs = playlist.tracks;\n            });\n          }\n        });\n      }\n    };\n\n    $scope.onCurrentPlayingSongDrop = (song, data, dataType, direction) => {\n      if (dataType === 'application/listen1-song') {\n        l1Player.insertTrack(data, song, direction);\n      }\n    };\n\n    $scope.playById = (id) => {\n      l1Player.playById(id);\n    };\n\n    $scope.addAndPlay = (song) => {\n      l1Player.addTrack(song);\n      l1Player.playById(song.id);\n    };\n\n    $scope.addMyPlaylist = (option_id, song) => {\n      MediaService.addMyPlaylist(option_id, song).success((playlist) => {\n        notyf.success(i18next.t('_ADD_TO_PLAYLIST_SUCCESS'));\n        $scope.closeDialog();\n        // add to current playing list\n        if (option_id === $scope.current_list_id) {\n          l1Player.addTrack($scope.dialog_song);\n        }\n        if (option_id === $scope.list_id) {\n          $scope.songs = playlist.tracks;\n        }\n      });\n    };\n\n    $scope.chooseDialogOption = (option_id) => {\n      $scope.addMyPlaylist(option_id, $scope.dialog_song);\n    };\n\n    $scope.newDialogOption = (option) => {\n      $scope.dialog_type = option;\n    };\n\n    $scope.cancelNewDialog = (option) => {\n      $scope.dialog_type = option;\n    };\n\n    $scope.createAndAddPlaylist = () => {\n      MediaService.createMyPlaylist(\n        $scope.newlist_title,\n        $scope.dialog_song\n      ).success(() => {\n        $rootScope.$broadcast('myplaylist:update');\n        notyf.success(i18next.t('_ADD_TO_PLAYLIST_SUCCESS'));\n        $scope.closeDialog();\n      });\n    };\n\n    $scope.editMyPlaylist = () => {\n      MediaService.editMyPlaylist(\n        $scope.list_id,\n        $scope.dialog_playlist_title,\n        $scope.dialog_cover_img_url\n      ).success(() => {\n        $rootScope.$broadcast('myplaylist:update');\n        $scope.playlist_title = $scope.dialog_playlist_title;\n        $scope.cover_img_url = $scope.dialog_cover_img_url;\n        notyf.success(i18next.t('_EDIT_PLAYLIST_SUCCESS'));\n        $scope.closeDialog();\n      });\n    };\n\n    $scope.mergePlaylist = (target_list_id) => {\n      notyf.info(i18next.t('_IMPORTING_PLAYLIST'));\n      MediaService.mergePlaylist($scope.list_id, target_list_id).success(() => {\n        notyf.success(i18next.t('_IMPORTING_PLAYLIST_SUCCESS'));\n        $scope.closeDialog();\n        $scope.popWindow();\n        $scope.showPlaylist($scope.list_id);\n      });\n    };\n\n    $scope.removeSongFromPlaylist = (song, list_id) => {\n      let removeFunc = null;\n      if (list_id.slice(0, 2) === 'my') {\n        removeFunc = MediaService.removeTrackFromMyPlaylist;\n      } else if (list_id.slice(0, 2) === 'lm') {\n        removeFunc = MediaService.removeTrackFromPlaylist;\n      }\n\n      removeFunc(list_id, song.id).success(() => {\n        // remove song from songs\n        const index = $scope.songs.indexOf(song);\n        if (index > -1) {\n          $scope.songs.splice(index, 1);\n        }\n        notyf.success(i18next.t('_REMOVE_SONG_FROM_PLAYLIST_SUCCESS'));\n      });\n    };\n\n    $scope.closeDialog = () => {\n      $scope.is_dialog_hidden = 1;\n      $scope.dialog_type = 0;\n      // update lastfm status if not authorized\n      if (lastfm.isAuthRequested()) {\n        lastfm.updateStatus();\n      }\n    };\n\n    $scope.setCurrentList = (list_id) => {\n      $scope.current_list_id = list_id;\n    };\n\n    $scope.playMylist = (list_id) => {\n      l1Player.setNewPlaylist($scope.songs);\n      l1Player.play();\n      $scope.setCurrentList(list_id);\n    };\n\n    $scope.addMylist = (list_id) => {\n      $timeout(() => {\n        // add songs to playlist\n        l1Player.addTracks($scope.songs);\n        notyf.success(i18next.t('_ADD_TO_QUEUE_SUCCESS'));\n      }, 0);\n    };\n\n    $scope.clonePlaylist = (list_id) => {\n      MediaService.clonePlaylist(list_id, 'my').success(() => {\n        $rootScope.$broadcast('myplaylist:update');\n        $scope.closeWindow();\n        notyf.success(i18next.t('_ADD_TO_PLAYLIST_SUCCESS'));\n      });\n    };\n\n    $scope.removeMyPlaylist = (list_id) => {\n      MediaService.removeMyPlaylist(list_id, 'my').success(() => {\n        $rootScope.$broadcast('myplaylist:update');\n        $scope.closeDialog();\n        $scope.closeWindow();\n        notyf.success(i18next.t('_REMOVE_PLAYLIST_SUCCESS'));\n      });\n    };\n\n    $scope.downloadFile = (fileName, fileType, content) => {\n      window.URL = window.URL || window.webkitURL;\n      const blob = new Blob([content], {\n        type: fileType,\n      });\n      const link = document.createElement('a');\n      link.download = fileName;\n      link.href = window.URL.createObjectURL(blob);\n      link.style.display = 'none';\n      document.body.appendChild(link);\n      link.click();\n      link.remove();\n    };\n\n    $scope.backupMySettings = () => {\n      const items = {};\n      Object.keys(localStorage).forEach((key) => {\n        items[key] = localStorage.getObject(key);\n      });\n\n      const content = JSON.stringify(items);\n      $scope.downloadFile('listen1_backup.json', 'application/json', content);\n    };\n\n    $scope.importMySettings = (event) => {\n      const fileObject = event.target.files[0];\n      if (fileObject === null) {\n        notyf.warning('请选择备份文件');\n        return;\n      }\n      const reader = new FileReader();\n      reader.onloadend = (readerEvent) => {\n        if (readerEvent.target.readyState === FileReader.DONE) {\n          const data_json = readerEvent.target.result;\n          // parse json\n          let data = null;\n          try {\n            data = JSON.parse(data_json);\n          } catch (e) {\n            notyf.warning('备份文件格式错误，请重新选择');\n            return;\n          }\n\n          Object.keys(data).forEach((item) =>\n            localStorage.setObject(item, data[item])\n          );\n          $rootScope.$broadcast('myplaylist:update');\n          notyf.success('成功导入我的歌单');\n        }\n      };\n      reader.readAsText(fileObject);\n    };\n\n    $scope.gistBackupLoading = false;\n    $scope.backupMySettings2Gist = (gistId, isPublic) => {\n      const items = {};\n      Object.keys(localStorage).forEach((key) => {\n        if (key !== 'gistid' && key !== 'githubOauthAccessKey') {\n          // avoid token leak\n          items[key] = localStorage.getObject(key);\n        }\n      });\n      const gistFiles = GithubClient.gist.json2gist(items);\n      $scope.gistBackupLoading = true;\n      GithubClient.gist.backupMySettings2Gist(gistFiles, gistId, isPublic).then(\n        () => {\n          notyf.dismissAll();\n          notyf.success('成功导出我的歌单到Gist');\n          $scope.gistBackupLoading = false;\n        },\n        (err) => {\n          notyf.dismissAll();\n          notyf.warning('导出我的歌单失败，检查后重试');\n          $scope.gistBackupLoading = false;\n        }\n      );\n      notyf.info('正在导出我的歌单到Gist...');\n    };\n\n    $scope.gistRestoreLoading = false;\n    $scope.importMySettingsFromGist = (gistId) => {\n      $scope.gistRestoreLoading = true;\n      GithubClient.gist.importMySettingsFromGist(gistId).then(\n        (raw) => {\n          GithubClient.gist.gist2json(raw, (data) => {\n            Object.keys(data).forEach((item) =>\n              localStorage.setObject(item, data[item])\n            );\n            notyf.dismissAll();\n            notyf.success('导入我的歌单成功');\n            $scope.gistRestoreLoading = false;\n            $rootScope.$broadcast('myplaylist:update');\n          });\n        },\n        (err) => {\n          notyf.dismissAll();\n          if (err === 404) {\n            notyf.warning('未找到备份歌单，请先备份');\n          } else {\n            notyf.warning('导入我的歌单失败，检查后重试');\n          }\n          $scope.gistRestoreLoading = false;\n        }\n      );\n      notyf.info('正在从Gist导入我的歌单...');\n    };\n\n    $scope.showShortcuts = () => {};\n\n    // description: '快速搜索',\n    hotkeys('f', () => {\n      $scope.showTag(3);\n      $timeout(() => {\n        document.getElementById('search-input').focus();\n      }, 0);\n    });\n\n    $scope.openUrl = (url) => {\n      MediaService.parseURL(url).success((data) => {\n        const { result } = data;\n        if (result !== undefined) {\n          $scope.showPlaylist(result.id);\n        } else {\n          notyf.info(i18next.t('_FAIL_OPEN_PLAYLIST_URL'));\n        }\n      });\n    };\n\n    $scope.favoritePlaylist = (list_id) => {\n      if ($scope.is_favorite) {\n        $scope.removeFavoritePlaylist(list_id);\n        $scope.is_favorite = 0;\n      } else {\n        $scope.addFavoritePlaylist(list_id);\n        $scope.is_favorite = 1;\n      }\n    };\n    $scope.addFavoritePlaylist = (list_id) => {\n      MediaService.clonePlaylist(list_id, 'favorite').success((addResult) => {\n        $rootScope.$broadcast('favoriteplaylist:update');\n        notyf.success(i18next.t('_FAVORITE_PLAYLIST_SUCCESS'));\n      });\n    };\n\n    $scope.removeFavoritePlaylist = (list_id) => {\n      MediaService.removeMyPlaylist(list_id, 'favorite').success(() => {\n        $rootScope.$broadcast('favoriteplaylist:update');\n        // $scope.closeWindow();\n        notyf.success(i18next.t('_UNFAVORITE_PLAYLIST_SUCCESS'));\n      });\n    };\n\n    $scope.addLocalMusic = (list_id) => {\n      if (isElectron()) {\n        const remote = require('@electron/remote');\n        const remoteFunctions = remote.require('./functions.js');\n        remote.dialog\n          .showOpenDialog({\n            title: '添加歌曲',\n            properties: ['openFile', 'multiSelections'],\n            filters: [\n              {\n                name: 'Music Files',\n                extensions: ['flac', 'mp3', 'mp4', 'ogg', 'wav', 'webm'],\n              },\n            ],\n          })\n          .then((result) => {\n            if (result.canceled) {\n              return;\n            }\n\n            result.filePaths.forEach((fp) => {\n              remoteFunctions.readAudioTags(fp).then((md) => {\n                const track = {\n                  id: `lmtrack_${fp}`,\n                  title: md.common.title,\n                  artist: md.common.artist,\n                  artist_id: `lmartist_${md.common.artist}`,\n                  album: md.common.album,\n                  album_id: `lmalbum_${md.common.album}`,\n                  source: 'localmusic',\n                  source_url: '',\n                  img_url: '',\n                  lyrics: md.common.lyrics,\n                  // url: \"lmtrack_\"+fp,\n                  sound_url: `file://${fp}`,\n                };\n\n                const list_id = 'lmplaylist_reserve';\n                MediaService.addPlaylist(list_id, [track]).success((res) => {\n                  const { playlist } = res;\n                  $scope.songs = playlist.tracks;\n                  $scope.list_id = playlist.info.id;\n                  $scope.cover_img_url = playlist.info.cover_img_url;\n                  $scope.playlist_title = playlist.info.title;\n                  $scope.playlist_source_url = playlist.info.source_url;\n                  $scope.is_mine = playlist.info.id.slice(0, 2) === 'my';\n                  $scope.is_local = playlist.info.id.slice(0, 2) === 'lm';\n                  $scope.$evalAsync();\n                });\n              });\n            });\n          })\n          .catch((err) => {\n            // console.log(err);\n          });\n      }\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/platform.js",
    "content": "/* global angular MediaService */\nconst platformSourceList = [\n  {\n    name: 'my_created_playlist',\n    displayId: '_MY_CREATED_PLAYLIST',\n  },\n  {\n    name: 'my_favorite_playlist',\n    displayId: '_MY_FAVORITE_PLAYLIST',\n  },\n  {\n    name: 'recommend_playlist',\n    displayId: '_RECOMMEND_PLAYLIST',\n  },\n];\nangular.module('listenone').controller('PlatformController', [\n  '$scope',\n  ($scope) => {\n    $scope.myPlatformPlaylists = [];\n    $scope.myPlatformUser = {};\n    $scope.platformSourceList = platformSourceList;\n    $scope.tab = platformSourceList[0].name;\n\n    $scope.loadPlatformPlaylists = () => {\n      if ($scope.myPlatformUser.platform === undefined) {\n        return;\n      }\n      let getPlaylistFn = MediaService.getUserCreatedPlaylist;\n      if ($scope.tab === 'recommend_playlist') {\n        getPlaylistFn = MediaService.getRecommendPlaylist;\n      } else if ($scope.tab === 'my_favorite_playlist') {\n        getPlaylistFn = MediaService.getUserFavoritePlaylist;\n      }\n      const user = $scope.myPlatformUser;\n      getPlaylistFn(user.platform, {\n        user_id: user.user_id,\n      }).success((response) => {\n        const { data } = response;\n        $scope.myPlatformPlaylists = data.playlists;\n      });\n    };\n\n    $scope.initPlatformController = (user) => {\n      $scope.tab = platformSourceList[0].name;\n      $scope.myPlatformUser = user;\n      $scope.loadPlatformPlaylists();\n    };\n\n    $scope.$on('myplatform:update', (event, user) => {\n      $scope.initPlatformController(user);\n    });\n\n    $scope.changeTab = (name) => {\n      $scope.tab = name;\n      $scope.loadPlatformPlaylists();\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/play.js",
    "content": "/* eslint-disable no-param-reassign */\n/* eslint-disable no-shadow */\n/* eslint-disable import/no-unresolved */\n/* eslint-disable global-require */\n/* global angular notyf i18next MediaService l1Player hotkeys GithubClient isElectron require getLocalStorageValue getPlayer getPlayerAsync addPlayerListener smoothScrollTo lastfm */\n\nfunction getCSSStringFromSetting(setting) {\n  let { backgroundAlpha } = setting;\n  if (backgroundAlpha === 0) {\n    // NOTE: background alpha 0 results total transparent\n    // which will cause mouse leave event not trigger\n    // correct in windows platform for lyic window if disable\n    // hardware accelerate\n    backgroundAlpha = 0.01;\n  }\n  return `div.content.lyric-content{\n      font-size: ${setting.fontSize}px;\n      color: ${setting.color};\n      background: rgba(36, 36, 36, ${backgroundAlpha});\n    }\n    div.content.lyric-content span.contentTrans {\n      font-size: ${setting.fontSize - 4}px;\n    }\n    `;\n}\n\nfunction useModernTheme() {\n  const defaultTheme = localStorage.getObject('theme');\n  return defaultTheme === 'white2' || defaultTheme === 'black2';\n}\n\nfunction getSafeIndex(index, length) {\n  if (index < 0) {\n    const r = index % length;\n    if (r < 0) {\n      return length + (index % length);\n    }\n    return r;\n  }\n  if (index > length - 1) {\n    return index % length;\n  }\n  return index;\n}\n\nfunction formatSecond(posSec) {\n  return `${Math.floor(posSec / 60)}:${`0${posSec % 60}`.slice(-2)}`;\n}\n\nangular.module('listenone').controller('PlayController', [\n  '$scope',\n  '$timeout',\n  '$log',\n  '$anchorScroll',\n  '$location',\n  '$rootScope',\n  ($scope, $timeout, $log, $anchorScroll, $location, $rootScope) => {\n    $scope.menuHidden = true;\n    $scope.volume = l1Player.status.volume;\n    $scope.mute = l1Player.status.muted;\n    $scope.settings = {\n      playmode: 0,\n      nowplaying_track_id: -1,\n    };\n    $scope.lyricArray = [];\n    $scope.lyricLineNumber = -1;\n    $scope.lastTrackId = null;\n\n    $scope.enableGloablShortcut = false;\n    $scope.isChrome = !isElectron();\n    $scope.isMac = false;\n\n    $scope.currentDuration = '0:00';\n    $scope.currentDurationSeconds = 0;\n    $scope.currentPosition = '0:00';\n\n    $scope.currentIndex = 0;\n    $scope.staged_playlist = [];\n    $scope.getSongIdByIndex = (index) => {\n      const songId =\n        $scope.playlist[getSafeIndex(index, $scope.playlist.length)].id;\n      return `${songId}_${index}`;\n    };\n\n    $scope.refreshStage = () => {\n      if ($scope.playlist === undefined) {\n        return;\n      }\n      const STAGED_LENGTH = 5;\n      let i = $scope.currentIndex - 2;\n      $scope.staged_playlist = [];\n      while ($scope.staged_playlist.length < STAGED_LENGTH) {\n        const song = $scope.playlist[getSafeIndex(i, $scope.playlist.length)];\n        if (!song) {\n          break;\n        }\n        $scope.staged_playlist.push({ ...song, stageId: `${song.id}_${i}` });\n        i += 1;\n      }\n    };\n\n    if (!$scope.isChrome) {\n      // eslint-disable-next-line no-undef\n      $scope.isMac = process.platform === 'darwin';\n    }\n\n    function switchMode(mode) {\n      // playmode 0:loop 1:shuffle 2:repeat one\n      switch (mode) {\n        case 0:\n          l1Player.setLoopMode('all');\n          break;\n        case 1:\n          l1Player.setLoopMode('shuffle');\n          break;\n        case 2:\n          l1Player.setLoopMode('one');\n          break;\n        default:\n      }\n    }\n\n    $scope.loadLocalSettings = () => {\n      const defaultSettings = {\n        playmode: 0,\n        nowplaying_track_id: -1,\n        volume: 90,\n      };\n      const localSettings = localStorage.getObject('player-settings');\n      if (localSettings === null) {\n        $scope.settings = defaultSettings;\n        $scope.saveLocalSettings();\n      } else {\n        $scope.settings = localSettings;\n      }\n      // apply settings\n      switchMode($scope.settings.playmode);\n\n      $scope.volume = $scope.settings.volume;\n      if ($scope.volume === null) {\n        $scope.volume = 90;\n        $scope.saveLocalSettings();\n      } else {\n        l1Player.setVolume($scope.volume);\n      }\n      $scope.enableGlobalShortCut = localStorage.getObject(\n        'enable_global_shortcut'\n      );\n      $scope.enableLyricFloatingWindow = localStorage.getObject(\n        'enable_lyric_floating_window'\n      );\n      $scope.enableLyricTranslation = localStorage.getObject(\n        'enable_lyric_translation'\n      );\n      $scope.enableLyricFloatingWindowTranslation = localStorage.getObject(\n        'enable_lyric_floating_window_translation'\n      );\n      $scope.enableAutoChooseSource = getLocalStorageValue(\n        'enable_auto_choose_source',\n        true\n      );\n      $scope.autoChooseSourceList = getLocalStorageValue(\n        'auto_choose_source_list',\n        ['kuwo', 'qq', 'migu']\n      );\n      $scope.enableStopWhenClose =\n        isElectron() || getLocalStorageValue('enable_stop_when_close', true);\n      $scope.enableNowplayingCoverBackground = getLocalStorageValue(\n        'enable_nowplaying_cover_background',\n        false\n      );\n      $scope.enableNowplayingBitrate = getLocalStorageValue(\n        'enable_nowplaying_bitrate',\n        false\n      );\n      $scope.enableNowplayingPlatform = getLocalStorageValue(\n        'enable_nowplaying_platform',\n        false\n      );\n\n      const defaultFloatWindowSetting = {\n        fontSize: 20,\n        color: '#ffffff',\n        backgroundAlpha: 0.2,\n      };\n\n      $scope.floatWindowSetting = getLocalStorageValue(\n        'float_window_setting',\n        defaultFloatWindowSetting\n      );\n\n      $scope.applyGlobalShortcut();\n      $scope.openLyricFloatingWindow();\n    };\n\n    // electron global shortcuts\n    $scope.applyGlobalShortcut = (toggle) => {\n      if (!isElectron()) {\n        return;\n      }\n      let message = '';\n      if (toggle === true) {\n        $scope.enableGlobalShortCut = !$scope.enableGlobalShortCut;\n      }\n      if ($scope.enableGlobalShortCut === true) {\n        message = 'enable_global_shortcut';\n      } else {\n        message = 'disable_global_shortcut';\n      }\n\n      // check if globalShortcuts is allowed\n      localStorage.setObject(\n        'enable_global_shortcut',\n        $scope.enableGlobalShortCut\n      );\n\n      const { ipcRenderer } = require('electron');\n      ipcRenderer.send('control', message);\n    };\n\n    $scope.openLyricFloatingWindow = (toggle) => {\n      if (!isElectron()) {\n        return;\n      }\n      let message = '';\n      if (toggle === true) {\n        $scope.enableLyricFloatingWindow = !$scope.enableLyricFloatingWindow;\n      }\n      if ($scope.enableLyricFloatingWindow === true) {\n        message = 'enable_lyric_floating_window';\n      } else {\n        message = 'disable_lyric_floating_window';\n      }\n      localStorage.setObject(\n        'enable_lyric_floating_window',\n        $scope.enableLyricFloatingWindow\n      );\n      const { ipcRenderer } = require('electron');\n      ipcRenderer.send(\n        'control',\n        message,\n        getCSSStringFromSetting($scope.floatWindowSetting)\n      );\n    };\n\n    if (isElectron()) {\n      const { webFrame, ipcRenderer } = require('electron');\n      // webFrame.setVisualZoomLevelLimits(1, 3);\n      ipcRenderer.on('setZoomLevel', (event, level) => {\n        webFrame.setZoomLevel(level);\n      });\n      ipcRenderer.on('lyricWindow', (event, arg) => {\n        if (arg === 'float_window_close') {\n          $scope.openLyricFloatingWindow(true);\n        } else if (\n          arg === 'float_window_font_small' ||\n          arg === 'float_window_font_large'\n        ) {\n          const MIN_FONT_SIZE = 12;\n          const MAX_FONT_SIZE = 50;\n          const offset = arg === 'float_window_font_small' ? -1 : 1;\n          $scope.floatWindowSetting.fontSize += offset;\n          if ($scope.floatWindowSetting.fontSize < MIN_FONT_SIZE) {\n            $scope.floatWindowSetting.fontSize = MIN_FONT_SIZE;\n          } else if ($scope.floatWindowSetting.fontSize > MAX_FONT_SIZE) {\n            $scope.floatWindowSetting.fontSize = MAX_FONT_SIZE;\n          }\n        } else if (\n          arg === 'float_window_background_light' ||\n          arg === 'float_window_background_dark'\n        ) {\n          const MIN_BACKGROUND_ALPHA = 0;\n          const MAX_BACKGROUND_ALPHA = 1;\n          const offset = arg === 'float_window_background_light' ? -0.1 : 0.1;\n          $scope.floatWindowSetting.backgroundAlpha += offset;\n          if (\n            $scope.floatWindowSetting.backgroundAlpha < MIN_BACKGROUND_ALPHA\n          ) {\n            $scope.floatWindowSetting.backgroundAlpha = MIN_BACKGROUND_ALPHA;\n          } else if (\n            $scope.floatWindowSetting.backgroundAlpha > MAX_BACKGROUND_ALPHA\n          ) {\n            $scope.floatWindowSetting.backgroundAlpha = MAX_BACKGROUND_ALPHA;\n          }\n        } else if (arg === 'float_window_font_change_color') {\n          const floatWindowlyricColors = [\n            '#ffffff',\n            '#65d29f',\n            '#3c87eb',\n            '#ec63af',\n            '#4f5455',\n            '#eb605b',\n          ];\n          const currentIndex = floatWindowlyricColors.indexOf(\n            $scope.floatWindowSetting.color\n          );\n          const nextIndex = (currentIndex + 1) % floatWindowlyricColors.length;\n          $scope.floatWindowSetting.color = floatWindowlyricColors[nextIndex];\n        }\n        localStorage.setObject(\n          'float_window_setting',\n          $scope.floatWindowSetting\n        );\n        const { ipcRenderer } = require('electron');\n        const message = 'update_lyric_floating_window_css';\n        ipcRenderer.send(\n          'control',\n          message,\n          getCSSStringFromSetting($scope.floatWindowSetting)\n        );\n      });\n    }\n\n    $scope.saveLocalSettings = () => {\n      localStorage.setObject('player-settings', $scope.settings);\n    };\n\n    $scope.changePlaymode = () => {\n      const playmodeCount = 3;\n      $scope.settings.playmode = ($scope.settings.playmode + 1) % playmodeCount;\n      switchMode($scope.settings.playmode);\n      $scope.saveLocalSettings();\n    };\n\n    $rootScope.openGithubAuth = GithubClient.github.openAuthUrl;\n    $rootScope.GithubLogout = () => {\n      GithubClient.github.logout();\n      $scope.$evalAsync(() => {\n        $scope.githubStatus = 0;\n        $scope.githubStatusText = GithubClient.github.getStatusText();\n      });\n    };\n    $rootScope.updateGithubStatus = () => {\n      GithubClient.github.updateStatus((data) => {\n        $scope.$evalAsync(() => {\n          $scope.githubStatus = data;\n          $scope.githubStatusText = GithubClient.github.getStatusText();\n        });\n      });\n    };\n\n    $scope.togglePlaylist = () => {\n      const anchor = `song${l1Player.status.playing.id}`;\n      $scope.menuHidden = !$scope.menuHidden;\n      if (!$scope.menuHidden) {\n        $anchorScroll(anchor);\n      }\n    };\n\n    $scope.toggleMuteStatus = () => {\n      // mute function is indeed toggle mute status.\n      l1Player.toggleMute();\n    };\n\n    $scope.myProgress = 0;\n    $scope.changingProgress = false;\n\n    $scope.copyrightNotice = () => {\n      notyf.info(i18next.t('_COPYRIGHT_ISSUE'), true);\n    };\n    $scope.failAllNotice = () => {\n      notyf.warning(i18next.t('_FAIL_ALL_NOTICE'), true);\n    };\n\n    $rootScope.$on('dragbar:myprogress', (event, data) => {\n      $scope.$evalAsync(() => {\n        // should use apply to force refresh ui\n        $scope.myProgress = data;\n\n        const posSec = Math.floor(\n          ($scope.currentDurationSeconds * $scope.myProgress) / 100\n        );\n        const posStr = formatSecond(posSec);\n\n        $scope.currentPosition = posStr;\n      });\n    });\n\n    $rootScope.$on('dragbar:changing_progress', (event, data) => {\n      $scope.$evalAsync(() => {\n        // should use apply to force refresh ui\n        $scope.changingProgress = data;\n      });\n    });\n\n    function parseLyric(lyric, tlyric) {\n      const lines = lyric.split('\\n');\n      let result = [];\n      const timeResult = [];\n\n      if (typeof tlyric !== 'string') {\n        tlyric = '';\n      }\n      const linesTrans = tlyric.split('\\n');\n      const resultTrans = [];\n      const timeResultTrans = [];\n      if (tlyric === '') {\n        linesTrans.splice(0);\n      }\n\n      function rightPadding(str, length, padChar) {\n        const newstr = str + new Array(length - str.length + 1).join(padChar);\n        return newstr;\n      }\n\n      const process =\n        (result, timeResult, translationFlag) => (line, index) => {\n          const tagReg = /\\[\\D*:([^\\]]+)\\]/g;\n          const tagRegResult = tagReg.exec(line);\n          if (tagRegResult) {\n            const lyricObject = {};\n            lyricObject.seconds = 0;\n            [lyricObject.content] = tagRegResult;\n            result.push(lyricObject);\n            return;\n          }\n\n          const timeReg = /\\[(\\d{2,})\\:(\\d{2})(?:\\.(\\d{1,3}))?\\]/g; // eslint-disable-line no-useless-escape\n\n          let timeRegResult = null;\n          // eslint-disable-next-line no-cond-assign\n          while ((timeRegResult = timeReg.exec(line)) !== null) {\n            const htmlUnescapes = {\n              '&amp;': '&',\n              '&lt;': '<',\n              '&gt;': '>',\n              '&quot;': '\"',\n              '&#39;': \"'\",\n              '&apos;': \"'\",\n            };\n            timeResult.push({\n              content: line\n                .replace(/\\[(\\d{2,}):(\\d{2})(?:\\.(\\d{1,3}))?\\]/g, '')\n                .replace(\n                  /&(?:amp|lt|gt|quot|#39|apos);/g,\n                  (match) => htmlUnescapes[match]\n                ),\n              seconds:\n                parseInt(timeRegResult[1], 10) * 60 * 1000 + // min\n                parseInt(timeRegResult[2], 10) * 1000 + // sec\n                (timeRegResult[3]\n                  ? parseInt(rightPadding(timeRegResult[3], 3, '0'), 10)\n                  : 0), // microsec\n              translationFlag,\n              index,\n            });\n          }\n        };\n\n      lines.forEach(process(result, timeResult, false));\n      linesTrans.forEach(process(resultTrans, timeResultTrans, true));\n\n      // sort time line\n      result = timeResult.concat(timeResultTrans).sort((a, b) => {\n        const keyA = a.seconds;\n        const keyB = b.seconds;\n\n        // Compare the 2 dates\n        if (keyA < keyB) return -1;\n        if (keyA > keyB) return 1;\n        if (a.translationFlag !== b.translationFlag) {\n          if (a.translationFlag === false) {\n            return -1;\n          }\n          return 1;\n        }\n        if (a.index < b.index) return -1;\n        if (a.index > b.index) return 1;\n        return 0;\n      });\n      // disable tag info, because music provider always write\n      // tag info in lyric timeline.\n      // result.push.apply(result, timeResult);\n      // result = timeResult; // executed up there\n\n      for (let i = 0; i < result.length; i += 1) {\n        result[i].lineNumber = i;\n      }\n\n      return result;\n    }\n    const mode =\n      isElectron() || getLocalStorageValue('enable_stop_when_close', true)\n        ? 'front'\n        : 'background';\n\n    getPlayer(mode).setMode(mode);\n    if (mode === 'front') {\n      if (!isElectron()) {\n        // avoid background keep playing when change to front mode\n        getPlayerAsync('background', (player) => {\n          player.pause();\n        });\n      }\n    }\n\n    addPlayerListener(mode, (msg, sender, sendResponse) => {\n      if (\n        typeof msg.type === 'string' &&\n        msg.type.split(':')[0] === 'BG_PLAYER'\n      ) {\n        switch (msg.type.split(':').slice(1).join('')) {\n          case 'READY': {\n            break;\n          }\n          case 'PLAY_FAILED': {\n            notyf.info(i18next.t('_COPYRIGHT_ISSUE'), true);\n            break;\n          }\n\n          case 'VOLUME': {\n            $scope.$evalAsync(() => {\n              $scope.volume = msg.data;\n            });\n            break;\n          }\n\n          case 'FRAME_UPDATE': {\n            // 'currentTrack:position'\n            // update lyric position\n            if (!l1Player.status.playing.id) break;\n            const currentSeconds = msg.data.pos;\n            let lastObject = null;\n            let lastObjectTrans = null;\n            $scope.lyricArray.forEach((lyric) => {\n              if (currentSeconds >= lyric.seconds / 1000) {\n                if (lyric.translationFlag !== true) {\n                  lastObject = lyric;\n                } else {\n                  lastObjectTrans = lyric;\n                }\n              }\n            });\n            if (\n              lastObject &&\n              lastObject.lineNumber !== $scope.lyricLineNumber\n            ) {\n              const lineElement = document.querySelector(\n                `.playsong-detail .detail-songinfo .lyric p[data-line=\"${lastObject.lineNumber}\"]`\n              );\n\n              let windowHeight = document.querySelector(\n                '.playsong-detail .detail-songinfo .lyric'\n              ).offsetHeight;\n              if (useModernTheme()) {\n                windowHeight =\n                  document.querySelector('body').offsetHeight - 100;\n              }\n\n              const adjustOffset = 30;\n              const offset =\n                lineElement.offsetTop - windowHeight / 2 + adjustOffset;\n              smoothScrollTo(document.querySelector('.lyric'), offset, 500);\n              $scope.lyricLineNumber = lastObject.lineNumber;\n              if (\n                lastObjectTrans &&\n                lastObjectTrans.lineNumber !== $scope.lyricLineNumberTrans\n              ) {\n                $scope.lyricLineNumberTrans = lastObjectTrans.lineNumber;\n              }\n              if (isElectron()) {\n                const { ipcRenderer } = require('electron');\n                const currentLyric =\n                  $scope.lyricArray[lastObject.lineNumber].content;\n                let currentLyricTrans = '';\n                if (\n                  $scope.enableLyricFloatingWindowTranslation === true &&\n                  lastObjectTrans\n                ) {\n                  currentLyricTrans =\n                    $scope.lyricArray[lastObjectTrans.lineNumber].content;\n                }\n                ipcRenderer.send('currentLyric', {\n                  lyric: currentLyric,\n                  tlyric: currentLyricTrans,\n                });\n              }\n            }\n\n            // 'currentTrack:duration'\n            (() => {\n              const durationSec = Math.floor(msg.data.duration);\n              const durationStr = `${Math.floor(durationSec / 60)}:${`0${\n                durationSec % 60\n              }`.substr(-2)}`;\n              if (\n                msg.data.duration === 0 ||\n                $scope.currentDuration === durationStr\n              ) {\n                return;\n              }\n              $scope.currentDuration = durationStr;\n              $scope.currentDurationSeconds = msg.data.duration;\n            })();\n\n            // 'track:progress'\n            if ($scope.changingProgress === false) {\n              $scope.$evalAsync(() => {\n                if (msg.data.duration === 0) {\n                  $scope.myProgress = 0;\n                } else {\n                  $scope.myProgress = (msg.data.pos / msg.data.duration) * 100;\n                }\n                const posSec = Math.floor(msg.data.pos);\n                const posStr = formatSecond(posSec);\n                $scope.currentPosition = posStr;\n              });\n            }\n            break;\n          }\n\n          case 'LOAD': {\n            $scope.currentPlaying = msg.data.currentPlaying;\n            const { length, index } = msg.data.playlist;\n\n            if (useModernTheme()) {\n              $scope.currentIndex = index;\n              $scope.refreshStage(index, length);\n            }\n\n            if (useModernTheme()) {\n              const rotatemark = document.getElementById('rotatemark');\n              const circlmark = document.getElementById('circlmark');\n              if (rotatemark !== null && circlmark !== null) {\n                circlmark.classList.add('circlmark');\n                rotatemark.classList.add('rotatemark');\n                circlmark.addEventListener('animationend', () => {\n                  circlmark.classList.remove('circlmark');\n                });\n                rotatemark.addEventListener('animationend', () => {\n                  rotatemark.classList.remove('rotatemark');\n                });\n              }\n            }\n\n            if (msg.data.currentPlaying.id === undefined) {\n              break;\n            }\n            $scope.currentPlaying.platformText = i18next.t(\n              $scope.currentPlaying.platform\n            );\n            $scope.myProgress = 0;\n            if ($scope.lastTrackId === msg.data.currentPlaying.id) {\n              break;\n            }\n            const current = localStorage.getObject('player-settings') || {};\n            current.nowplaying_track_id = msg.data.currentPlaying.id;\n            localStorage.setObject('player-settings', current);\n            // update lyric\n            $scope.lyricArray = [];\n            $scope.lyricLineNumber = -1;\n            $scope.lyricLineNumberTrans = -1;\n            smoothScrollTo(document.querySelector('.lyric'), 0, 300);\n            const track = msg.data.currentPlaying;\n            $rootScope.page_title = {\n              title: track.title,\n              artist: track.artist,\n              status: 'playing',\n            };\n            if (lastfm.isAuthorized()) {\n              lastfm.sendNowPlaying(track.title, track.artist, () => {});\n            }\n            MediaService.getLyric(\n              msg.data.currentPlaying.id,\n              msg.data.currentPlaying.album_id,\n              track.lyric_url,\n              track.tlyric_url\n            ).success((res) => {\n              const { lyric, tlyric } = res;\n              if (!lyric) {\n                return;\n              }\n              $scope.lyricArray = parseLyric(lyric, tlyric);\n            });\n            $scope.lastTrackId = msg.data.currentPlaying.id;\n            if (isElectron()) {\n              const { ipcRenderer } = require('electron');\n              ipcRenderer.send('currentLyric', track.title);\n              ipcRenderer.send('trackPlayingNow', track);\n            }\n            break;\n          }\n\n          case 'MUTE': {\n            // 'music:mute'\n            $scope.$evalAsync(() => {\n              $scope.mute = msg.data;\n            });\n            break;\n          }\n\n          case 'PLAYLIST': {\n            // 'player:playlist'\n            $scope.$evalAsync(() => {\n              $scope.playlist = msg.data;\n              $scope.refreshStage();\n              localStorage.setObject('current-playing', msg.data);\n            });\n\n            break;\n          }\n\n          case 'PLAY_STATE': {\n            // 'music:isPlaying'\n            $scope.$evalAsync(() => {\n              $scope.isPlaying = !!msg.data.isPlaying;\n            });\n            let title = 'Listen 1';\n            if ($rootScope.page_title !== undefined) {\n              title = '';\n              if (msg.data.isPlaying) {\n                $rootScope.page_title.status = 'playing';\n              } else {\n                $rootScope.page_title.status = 'paused';\n              }\n              if ($rootScope.page_title.status !== '') {\n                if ($rootScope.page_title.status === 'playing') {\n                  title += '▶ ';\n                } else if ($rootScope.page_title.status === 'paused') {\n                  title += '❚❚ ';\n                }\n              }\n              title += $rootScope.page_title.title;\n              if ($rootScope.page_title.artist !== '') {\n                title += ` - ${$rootScope.page_title.artist}`;\n              }\n            }\n\n            $rootScope.document_title = title;\n            if (isElectron()) {\n              const { ipcRenderer } = require('electron');\n              if (msg.data.isPlaying) {\n                ipcRenderer.send('isPlaying', true);\n              } else {\n                ipcRenderer.send('isPlaying', false);\n              }\n            }\n\n            if (msg.data.reason === 'Ended') {\n              if (!lastfm.isAuthorized()) {\n                break;\n              }\n              // send lastfm scrobble\n              const track = l1Player.getTrackById(l1Player.status.playing.id);\n              lastfm.scrobble(\n                l1Player.status.playing.playedFrom,\n                track.title,\n                track.artist,\n                track.album,\n                () => {}\n              );\n            }\n\n            break;\n          }\n          case 'RETRIEVE_URL_SUCCESS': {\n            $scope.currentPlaying = msg.data;\n            // update translate whenever set value\n            $scope.currentPlaying.platformText = i18next.t(\n              $scope.currentPlaying.platform\n            );\n            break;\n          }\n          case 'RETRIEVE_URL_FAIL': {\n            $scope.copyrightNotice();\n            break;\n          }\n          case 'RETRIEVE_URL_FAIL_ALL': {\n            $scope.failAllNotice();\n            break;\n          }\n          default:\n            break;\n        }\n      }\n      if (sendResponse !== undefined) {\n        sendResponse();\n      }\n    });\n\n    // connect player should run after all addListener function finished\n    l1Player.connectPlayer();\n\n    // define keybind\n    // description: '播放/暂停',\n    hotkeys('p', l1Player.togglePlayPause);\n\n    // description: '上一首',\n    hotkeys('[', l1Player.prev);\n\n    // description: '下一首',\n    hotkeys(']', l1Player.next);\n\n    // description: '静音/取消静音',\n    hotkeys('m', l1Player.toggleMute);\n\n    // description: '打开/关闭播放列表',\n    hotkeys('l', $scope.togglePlaylist);\n\n    // description: '切换播放模式（顺序/随机/单曲循环）',\n    hotkeys('s', $scope.changePlaymode);\n\n    // description: '音量增加',\n    hotkeys('u', () => {\n      $timeout(() => {\n        l1Player.adjustVolume(true);\n      });\n    });\n\n    // description: '音量减少',\n    hotkeys('d', () => {\n      $timeout(() => {\n        l1Player.adjustVolume(false);\n      });\n    });\n\n    $scope.toggleLyricTranslation = () => {\n      $scope.enableLyricTranslation = !$scope.enableLyricTranslation;\n      localStorage.setObject(\n        'enable_lyric_translation',\n        $scope.enableLyricTranslation\n      );\n    };\n\n    $scope.toggleLyricFloatingWindowTranslation = () => {\n      $scope.enableLyricFloatingWindowTranslation =\n        !$scope.enableLyricFloatingWindowTranslation;\n      localStorage.setObject(\n        'enable_lyric_floating_window_translation',\n        $scope.enableLyricFloatingWindowTranslation\n      );\n    };\n\n    if (isElectron()) {\n      require('electron').ipcRenderer.on('globalShortcut', (event, message) => {\n        if (message === 'right') {\n          l1Player.next();\n        } else if (message === 'left') {\n          l1Player.prev();\n        } else if (message === 'space') {\n          l1Player.togglePlayPause();\n        }\n      });\n    }\n\n    $scope.setAutoChooseSource = (toggle) => {\n      if (toggle === true) {\n        $scope.enableAutoChooseSource = !$scope.enableAutoChooseSource;\n      }\n      localStorage.setObject(\n        'enable_auto_choose_source',\n        $scope.enableAutoChooseSource\n      );\n    };\n\n    $scope.enableSource = (source) => {\n      if ($scope.autoChooseSourceList.indexOf(source) > -1) {\n        return;\n      }\n      $scope.autoChooseSourceList = [...$scope.autoChooseSourceList, source];\n      localStorage.setObject(\n        'auto_choose_source_list',\n        $scope.autoChooseSourceList\n      );\n    };\n\n    $scope.disableSource = (source) => {\n      if ($scope.autoChooseSourceList.indexOf(source) === -1) {\n        return;\n      }\n      $scope.autoChooseSourceList = $scope.autoChooseSourceList.filter(\n        (i) => i !== source\n      );\n      localStorage.setObject(\n        'auto_choose_source_list',\n        $scope.autoChooseSourceList\n      );\n    };\n\n    $scope.setStopWhenClose = (status) => {\n      $scope.enableStopWhenClose = status;\n      localStorage.setObject(\n        'enable_stop_when_close',\n        $scope.enableStopWhenClose\n      );\n    };\n\n    $scope.setNowplayingCoverBackground = (toggle) => {\n      if (toggle === true) {\n        $scope.enableNowplayingCoverBackground =\n          !$scope.enableNowplayingCoverBackground;\n      }\n      localStorage.setObject(\n        'enable_nowplaying_cover_background',\n        $scope.enableNowplayingCoverBackground\n      );\n    };\n    $scope.setNowplayingBitrate = (toggle) => {\n      if (toggle === true) {\n        $scope.enableNowplayingBitrate = !$scope.enableNowplayingBitrate;\n      }\n      localStorage.setObject(\n        'enable_nowplaying_bitrate',\n        $scope.enableNowplayingBitrate\n      );\n    };\n    $scope.setNowplayingPlatform = (toggle) => {\n      if (toggle === true) {\n        $scope.enableNowplayingPlatform = !$scope.enableNowplayingPlatform;\n      }\n      localStorage.setObject(\n        'enable_nowplaying_platform',\n        $scope.enableNowplayingPlatform\n      );\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/playlist.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global angular MediaService sourceList */\n\nangular.module('listenone').controller('PlayListController', [\n  '$scope',\n  '$timeout',\n  ($scope) => {\n    $scope.result = [];\n    $scope.tab = sourceList[0].name;\n    $scope.sourceList = sourceList;\n    $scope.playlistFilters = {};\n    $scope.allPlaylistFilters = {};\n    $scope.currentFilterId = '';\n    $scope.loading = true;\n    $scope.showMore = false;\n\n    $scope.$on('infinite_scroll:hit_bottom', (event, data) => {\n      if ($scope.loading === true) {\n        return;\n      }\n      $scope.loading = true;\n      const offset = $scope.result.length;\n      MediaService.showPlaylistArray(\n        $scope.tab,\n        offset,\n        $scope.currentFilterId\n      ).success((res) => {\n        $scope.result = $scope.result.concat(res.result);\n        $scope.loading = false;\n      });\n    });\n\n    $scope.loadPlaylist = () => {\n      const offset = 0;\n      $scope.showMore = false;\n      MediaService.showPlaylistArray(\n        $scope.tab,\n        offset,\n        $scope.currentFilterId\n      ).success((res) => {\n        $scope.result = res.result;\n        $scope.loading = false;\n      });\n\n      if (\n        $scope.playlistFilters[$scope.tab] === undefined &&\n        $scope.allPlaylistFilters[$scope.tab] === undefined\n      ) {\n        MediaService.getPlaylistFilters($scope.tab).success((res) => {\n          $scope.playlistFilters[$scope.tab] = res.recommend;\n          $scope.allPlaylistFilters[$scope.tab] = res.all;\n        });\n      }\n    };\n\n    $scope.changeTab = (newTab) => {\n      $scope.tab = newTab;\n      $scope.result = [];\n      $scope.currentFilterId = '';\n      $scope.loadPlaylist();\n    };\n\n    $scope.changeFilter = (filterId) => {\n      $scope.result = [];\n      $scope.currentFilterId = filterId;\n      $scope.loadPlaylist();\n    };\n\n    $scope.toggleMorePlaylists = () => {\n      $scope.showMore = !$scope.showMore;\n    };\n  },\n]);\n"
  },
  {
    "path": "js/controller/profile.js",
    "content": "/* eslint-disable import/no-unresolved */\n/* eslint-disable global-require */\n/* eslint-disable no-undef */\n/* eslint-disable no-param-reassign */\n/* global angular i18next sourceList platformSourceList */\nangular.module('listenone').controller('ProfileController', [\n  '$scope',\n  ($scope) => {\n    let defaultLang = 'zh-CN';\n    const supportLangs = ['zh-CN', 'en-US'];\n    if (supportLangs.indexOf(navigator.language) !== -1) {\n      defaultLang = navigator.language;\n    }\n    if (supportLangs.indexOf(localStorage.getObject('language')) !== -1) {\n      defaultLang = localStorage.getObject('language');\n    }\n    $scope.lastestVersion = '';\n    $scope.theme = '';\n    $scope.proxyModes = [\n      { name: 'system', displayId: '_PROXY_SYSTEM' },\n      { name: 'direct', displayId: '_PROXY_DIRECT' },\n      { name: 'custom', displayId: '_PROXY_CUSTOM' },\n    ];\n\n    [$scope.proxyModeInput] = $scope.proxyModes;\n    [$scope.proxyMode] = $scope.proxyModes;\n    $scope.proxyProtocols = ['http', 'https', 'quic', 'socks4', 'socks5'];\n\n    $scope.proxyProtocol = 'http';\n    $scope.proxyRules = '';\n\n    $scope.changeProxyProtocol = (newProtocol) => {\n      $scope.proxyProtocol = newProtocol;\n    };\n\n    $scope.changeProxyMode = (newMode) => {\n      $scope.proxyModeInput = newMode;\n    };\n\n    $scope.setProxyConfig = () => {\n      const mode = $scope.proxyModeInput.name;\n      $scope.proxyMode = $scope.proxyModeInput;\n      const host = document.getElementById('proxy-rules-host').value;\n      const port = document.getElementById('proxy-rules-port').value;\n      $scope.proxyRules = `${$scope.proxyProtocol}://${host}:${port}`;\n      if (isElectron()) {\n        const message = 'update_proxy_config';\n        const { ipcRenderer } = require('electron');\n        if (mode === 'system' || mode === 'direct') {\n          ipcRenderer.send('control', message, { mode });\n        } else {\n          ipcRenderer.send('control', message, {\n            proxyRules: $scope.proxyRules,\n          });\n        }\n      }\n    };\n\n    $scope.getProxyConfig = () => {\n      if (isElectron()) {\n        // get proxy config from main process\n        const message = 'get_proxy_config';\n        const { ipcRenderer } = require('electron');\n        ipcRenderer.send('control', message);\n      }\n    };\n\n    $scope.initProfile = () => {\n      const url = `https://api.github.com/repos/listen1/listen1_chrome_extension/releases/latest`;\n      axios.get(url).then((response) => {\n        $scope.lastestVersion = response.data.tag_name;\n      });\n\n      $scope.getProxyConfig();\n    };\n\n    if (isElectron()) {\n      const { ipcRenderer } = require('electron');\n\n      ipcRenderer.on('proxyConfig', (event, config) => {\n        // parse config\n        if (config.mode === 'system' || config.mode === 'direct') {\n          [$scope.proxyMode] = $scope.proxyModes.filter(\n            (i) => i.name === config.mode\n          );\n          $scope.proxyModeInput = $scope.proxyMode;\n          $scope.proxyRules = '';\n        } else {\n          [$scope.proxyMode] = $scope.proxyModes.filter(\n            (i) => i.name === 'custom'\n          );\n          $scope.proxyModeInput = $scope.proxyMode;\n          $scope.proxyRules = config.proxyRules;\n          // rules = 'socks5://127.0.0.1:1080'\n          const match = /(\\w+):\\/\\/([\\d.]+):(\\d+)/.exec(config.proxyRules);\n          const [, protocol, host, port] = match;\n\n          $scope.proxyProtocol = protocol;\n          document.getElementById('proxy-rules-host').value = host;\n          document.getElementById('proxy-rules-port').value = port;\n        }\n      });\n    }\n    $scope.setLang = (langKey) => {\n      // You can change the language during runtime\n      i18next.changeLanguage(langKey).then((t) => {\n        axios.get('i18n/zh-CN.json').then((res) => {\n          Object.keys(res.data).forEach((key) => {\n            $scope[key] = t(key);\n          });\n          sourceList.forEach((item) => {\n            item.displayText = t(item.displayId);\n          });\n          platformSourceList.forEach((item) => {\n            item.displayText = t(item.displayId);\n          });\n          $scope.proxyModes.forEach((item) => {\n            item.displayText = t(item.displayId);\n          });\n        });\n        localStorage.setObject('language', langKey);\n      });\n    };\n    $scope.setLang(defaultLang);\n\n    let defaultTheme = 'white';\n    if (localStorage.getObject('theme') !== null) {\n      defaultTheme = localStorage.getObject('theme');\n    }\n    $scope.setTheme = (theme) => {\n      $scope.theme = theme;\n\n      const themeFiles = {\n        white: ['css/iparanoid.css', 'css/common.css'],\n        black: ['css/origin.css', 'css/common.css'],\n        white2: ['css/iparanoid2.css', 'css/common2.css'],\n        black2: ['css/origin2.css', 'css/common2.css'],\n      };\n      // You can change the language during runtime\n      if (themeFiles[theme] !== undefined) {\n        const keys = ['theme-css', 'common-css'];\n        for (let i = 0; i < themeFiles[theme].length; i += 1) {\n          document.getElementById(keys[i]).href = themeFiles[theme][i];\n        }\n        localStorage.setObject('theme', theme);\n      }\n      axios.get('images/feather-sprite.svg').then((res) => {\n        document.getElementById('feather-container').innerHTML = res.data;\n      });\n    };\n    $scope.setTheme(defaultTheme);\n  },\n]);\n"
  },
  {
    "path": "js/github.js",
    "content": "/* global isElectron require */\n/* eslint-disable global-require */\nfunction github() {\n  const OAUTH_URL = 'https://github.com/login/oauth';\n  const API_URL = 'https://api.github.com';\n\n  const client_id = 'e099a4803bb1e2e773a3';\n  const client_secret = '81fbfc45c65af8c0fbf2b4dae6f23f22e656cfb8';\n\n  const GithubAPI = axios.create({\n    baseURL: API_URL,\n    headers: { accept: 'application/json' },\n  });\n  GithubAPI.interceptors.request.use((config) => {\n    const accessToken = localStorage.getObject('githubOauthAccessKey');\n    // eslint-disable-next-line no-param-reassign\n    config.headers.Authorization = `token ${accessToken}`;\n    return config;\n  });\n\n  const Github = {\n    status: 0,\n    username: '',\n  };\n\n  window.GithubClient = {\n    github: {\n      handleCallback: (code, cb) => {\n        const url = `${OAUTH_URL}/access_token`;\n        const params = {\n          client_id,\n          client_secret,\n          code,\n        };\n        axios\n          .post(url, '', {\n            params,\n            headers: { accept: 'application/json' },\n          })\n          .then((res) => {\n            const ak = res.data.access_token;\n            if (ak)\n              localStorage.setItem('githubOauthAccessKey', JSON.stringify(ak));\n            if (cb !== undefined) {\n              cb(ak);\n            }\n          });\n      },\n      openAuthUrl: () => {\n        Github.status = 1;\n        const url = `${OAUTH_URL}/authorize?client_id=${client_id}&scope=gist`;\n        if (isElectron()) {\n          // normal window for link\n          const { BrowserWindow } = require('@electron/remote'); // eslint-disable-line import/no-unresolved\n          let win = new BrowserWindow({\n            width: 1000,\n            height: 670,\n          });\n          win.on('closed', () => {\n            win = null;\n          });\n          win.loadURL(url);\n          return;\n        }\n        window.open(url, '_blank');\n      },\n      getStatus: () => Github.status,\n      getStatusText: () => {\n        switch (Github.status) {\n          case 0:\n            return '未连接';\n          case 1:\n            return '连接中';\n          case 2:\n            return `${Github.username}已登录`;\n          default:\n            return '???';\n        }\n      },\n      updateStatus: async (callback) => {\n        const access_token = localStorage.getObject('githubOauthAccessKey');\n        if (access_token == null) {\n          Github.status = 0;\n        } else {\n          const { data } = await GithubAPI.get('/user');\n          if (data.login === undefined) {\n            Github.status = 1;\n          } else {\n            Github.status = 2;\n            Github.username = data.login;\n          }\n        }\n        if (callback != null) {\n          callback(Github.status);\n        }\n      },\n      logout: () => {\n        localStorage.removeItem('githubOauthAccessKey');\n        Github.status = 0;\n      },\n    },\n\n    gist: {\n      json2gist(jsonObject) {\n        const result = {};\n\n        result['listen1_backup.json'] = {\n          content: JSON.stringify(jsonObject),\n        };\n        // const markdown = '# My Listen1 Playlists\\n';\n        const playlistIds = jsonObject.playerlists;\n        const songsCount = playlistIds.reduce((count, playlistId) => {\n          const playlist = jsonObject[playlistId];\n          const cover = `<img src=\"${playlist.info.cover_img_url}\" width=\"140\" height=\"140\"><br/>`;\n          const { title } = playlist.info;\n          let tableHeader = '\\n| 音乐标题 | 歌手 | 专辑 |\\n';\n          tableHeader += '| --- | --- | --- |\\n';\n          const tableBody = playlist.tracks.reduce(\n            (r, track) =>\n              `${r} | ${track.title} | ${track.artist} | ${track.album} | \\n`,\n            ''\n          );\n          const content = `<details>\\n  <summary>${cover}   ${title}</summary><p>\\n${tableHeader}${tableBody}</p></details>`;\n          const filename = `listen1_${playlistId}.md`;\n          result[filename] = {\n            content,\n          };\n          return count + playlist.tracks.length;\n        }, 0);\n        const summary = `本歌单由[Listen1](https://listen1.github.io/listen1/)创建, 歌曲数：${songsCount}，歌单数：${playlistIds.length}，点击查看更多`;\n        result['listen1_aha_playlist.md'] = {\n          content: summary,\n        };\n\n        return result;\n      },\n\n      gist2json(gistFiles, callback) {\n        if (!gistFiles['listen1_backup.json'].truncated) {\n          const jsonString = gistFiles['listen1_backup.json'].content;\n          return callback(JSON.parse(jsonString));\n        }\n\n        const url = gistFiles['listen1_backup.json'].raw_url;\n        // const { size } = gistFiles['listen1_backup.json'];\n        GithubAPI.get(url).then((res) => callback(res.data));\n        return null;\n      },\n\n      listExistBackup() {\n        return GithubAPI.get('/gists').then((res) => {\n          const result = res.data;\n          return result.filter((backupObject) =>\n            backupObject.description.startsWith('updated by Listen1')\n          );\n        });\n      },\n\n      backupMySettings2Gist(files, gistId, isPublic) {\n        let method = '';\n        let url = '';\n        if (gistId != null) {\n          method = 'patch';\n          url = `/gists/${gistId}`;\n        } else {\n          method = 'post';\n          url = '/gists';\n        }\n        return GithubAPI.request({\n          method,\n          url,\n          data: {\n            description: `updated by Listen1(https://listen1.github.io/listen1/) at ${new Date().toLocaleString()}`,\n            public: isPublic,\n            files,\n          },\n        });\n      },\n\n      importMySettingsFromGist(gistId) {\n        return GithubAPI.get(`/gists/${gistId}`).then((res) => res.data.files);\n      },\n    },\n  };\n}\n\ngithub();\n"
  },
  {
    "path": "js/l1_player.js",
    "content": "/* eslint-disable no-param-reassign */\n/* global isElectron getPlayer getPlayerAsync addPlayerListener getLocalStorageValue */\n{\n  const mode =\n    isElectron() || getLocalStorageValue('enable_stop_when_close', true)\n      ? 'front'\n      : 'background';\n\n  const myPlayer = getPlayer(mode);\n  const l1Player = {\n    status: {\n      muted: myPlayer.muted,\n      volume: myPlayer.volume * 100,\n      loop_mode: myPlayer.loop_mode,\n      playing: myPlayer.playing,\n    },\n    play() {\n      getPlayerAsync(mode, (player) => {\n        player.play();\n      });\n    },\n    pause() {\n      getPlayerAsync(mode, (player) => {\n        player.pause();\n      });\n    },\n    togglePlayPause() {\n      getPlayerAsync(mode, (player) => {\n        if (player.playing) {\n          player.pause();\n        } else {\n          player.play();\n        }\n      });\n    },\n    playById(id) {\n      getPlayerAsync(mode, (player) => {\n        player.playById(id);\n      });\n    },\n    loadById(idx) {\n      getPlayerAsync(mode, (player) => {\n        player.loadById(idx);\n      });\n    },\n    seek(per) {\n      getPlayerAsync(mode, (player) => {\n        player.seek(per);\n      });\n    },\n    next() {\n      getPlayerAsync(mode, (player) => {\n        player.skip('next');\n      });\n    },\n    prev() {\n      getPlayerAsync(mode, (player) => {\n        player.skip('prev');\n      });\n    },\n    random() {\n      getPlayerAsync(mode, (player) => {\n        player.skip('random');\n      });\n    },\n    setLoopMode(input) {\n      getPlayerAsync(mode, (player) => {\n        // eslint-disable-next-line no-param-reassign\n        player.loop_mode = input;\n      });\n    },\n    mute() {\n      getPlayerAsync(mode, (player) => {\n        player.mute();\n      });\n    },\n    unmute() {\n      getPlayerAsync(mode, (player) => {\n        player.unmute();\n      });\n    },\n    toggleMute() {\n      getPlayerAsync(mode, (player) => {\n        if (player.muted) player.unmute();\n        else player.mute();\n      });\n    },\n    setVolume(per) {\n      getPlayerAsync(mode, (player) => {\n        // eslint-disable-next-line no-param-reassign\n        player.volume = per / 100;\n      });\n    },\n    adjustVolume(increase) {\n      getPlayerAsync(mode, (player) => {\n        player.adjustVolume(increase);\n      });\n    },\n    addTrack(track) {\n      getPlayerAsync(mode, (player) => {\n        player.insertAudio(track);\n      });\n    },\n    insertTrack(track, to_track, direction) {\n      getPlayerAsync(mode, (player) => {\n        player.insertAudioByDirection(track, to_track, direction);\n      });\n    },\n    removeTrack(index) {\n      getPlayerAsync(mode, (player) => {\n        player.removeAudio(index);\n      });\n    },\n    addTracks(list) {\n      getPlayerAsync(mode, (player) => {\n        player.appendAudioList(list);\n      });\n    },\n    clearPlaylist() {\n      getPlayerAsync(mode, (player) => {\n        player.clearPlaylist();\n      });\n    },\n    setNewPlaylist(list) {\n      getPlayerAsync(mode, (player) => {\n        player.setNewPlaylist(list);\n      });\n    },\n    getTrackById(id) {\n      if (!l1Player.status.playlist) return null;\n      return l1Player.status.playlist.find((track) => track.id === id);\n    },\n    connectPlayer() {\n      getPlayerAsync(mode, (player) => {\n        if (!player.playing) {\n          // load local storage settings\n          if (!player.playlist.length) {\n            const localCurrentPlaying =\n              localStorage.getObject('current-playing');\n            if (localCurrentPlaying !== null) {\n              localCurrentPlaying.forEach((i) => {\n                i.disabled = false;\n              });\n              player.setNewPlaylist(localCurrentPlaying);\n            }\n          }\n\n          const localPlayerSettings = localStorage.getObject('player-settings');\n          if (localPlayerSettings !== null) {\n            player.loadById(localPlayerSettings.nowplaying_track_id);\n          }\n        }\n\n        player.sendPlaylistEvent();\n        player.sendPlayingEvent();\n        player.sendLoadEvent();\n      });\n    },\n  };\n\n  l1Player.injectDirectives = (ngApp) => {\n    ngApp.directive('playFromPlaylist', () => ({\n      restrict: 'EA',\n      scope: {\n        song: '=playFromPlaylist',\n      },\n      link(scope, element) {\n        element.bind('click', () => {\n          l1Player.playById(scope.song.id);\n        });\n      },\n    }));\n\n    ngApp.directive('nextTrack', () => ({\n      restrict: 'EA',\n      link(scope, element) {\n        element.bind('click', () => {\n          l1Player.next();\n        });\n      },\n    }));\n\n    ngApp.directive('prevTrack', () => ({\n      restrict: 'EA',\n      link(scope, element) {\n        element.bind('click', () => {\n          l1Player.prev();\n        });\n      },\n    }));\n\n    ngApp.directive('clearPlaylist', () => ({\n      restrict: 'EA',\n      link(scope, element) {\n        element.bind('click', () => {\n          l1Player.clearPlaylist();\n        });\n      },\n    }));\n\n    ngApp.directive('removeFromPlaylist', () => ({\n      restrict: 'EA',\n      scope: {\n        song: '=removeFromPlaylist',\n      },\n      link(scope, element, attrs) {\n        element.bind('click', () => {\n          l1Player.removeTrack(attrs.index);\n        });\n      },\n    }));\n\n    ngApp.directive('playPauseToggle', () => ({\n      restrict: 'EA',\n      link(scope, element) {\n        element.bind('click', () => {\n          l1Player.togglePlayPause();\n        });\n      },\n    }));\n  };\n\n  addPlayerListener(mode, (msg, sender, res) => {\n    if (msg.type === 'BG_PLAYER:FRAME_UPDATE') {\n      l1Player.status.playing = {\n        ...l1Player.status.playing,\n        ...msg.data,\n      };\n    } else if (msg.type === 'BG_PLAYER:PLAYLIST') {\n      l1Player.status.playlist = msg.data || [];\n    }\n    if (res !== undefined) {\n      res();\n    }\n  });\n\n  window.l1Player = l1Player;\n}\n"
  },
  {
    "path": "js/lastfm.js",
    "content": "/* global forge */\n// eslint-disable-next-line no-unused-vars\n{\n  const options = {\n    apiKey: '6790c00a181128dc7c4ce06cd99d17c8',\n    apiSecret: 'd68f1dfc6ff43044c96a79ae7dfb5c27',\n  };\n\n  const apiUrl = 'https://ws.audioscrobbler.com/2.0/';\n\n  let status = 0;\n\n  // const publicApi = {\n  //   getAuth,\n  //   cancelAuth,\n  //   getSession,\n  //   sendNowPlaying,\n  //   scrobble,\n  //   getUserInfo,\n  //   getStatusText,\n  //   updateStatus,\n  //   isAuthorized,\n  //   isAuthRequested,\n  // };\n\n  /**\n   * Computes string for signing request\n   *\n   * See https://www.last.fm/api/authspec#8\n   */\n  const generateSign = (params) => {\n    const keys = Object.keys(params).filter(\n      (key) => key !== 'format' || key !== 'callback'\n    );\n\n    // params has to be ordered alphabetically\n    keys.sort();\n\n    const o = keys.reduce((r, key) => r + key + params[key], '');\n\n    // append secret\n    return forge.md5\n      .create()\n      .update(forge.util.encodeUtf8(o + options.apiSecret))\n      .digest()\n      .toHex();\n  };\n\n  // eslint-disable-next-line no-underscore-dangle\n  const _isAuthRequested = () => {\n    const token = localStorage.getObject('lastfmtoken');\n    return token != null;\n  };\n\n  // eslint-disable-next-line no-unused-vars\n  class lastfm {\n    static getSession(callback) {\n      // load session info from localStorage\n      let mySession = localStorage.getObject('lastfmsession');\n      if (mySession != null) {\n        return callback(mySession);\n      }\n      // trade session with token\n      const token = localStorage.getObject('lastfmtoken');\n      if (token == null) {\n        return callback(null);\n      }\n      // token exists\n      const params = {\n        method: 'auth.getsession',\n        api_key: options.apiKey,\n        token,\n      };\n      params.api_sig = generateSign(params);\n      params.format = 'json';\n\n      axios\n        .get(apiUrl, {\n          params,\n        })\n        .then((response) => {\n          const { data } = response;\n          mySession = data.session;\n          localStorage.setObject('lastfmsession', mySession);\n          callback(mySession);\n        })\n        .catch((error) => {\n          if (error.response.status === 403) {\n            callback(null);\n          }\n        });\n      return null;\n    }\n\n    static getUserInfo(callback) {\n      this.getSession((session) => {\n        if (session == null) {\n          callback(null);\n          return;\n        }\n        const params = {\n          method: 'user.getinfo',\n          api_key: options.apiKey,\n          sk: session.key,\n        };\n\n        params.api_sig = generateSign(params);\n        params.format = 'json';\n\n        axios\n          .post(apiUrl, '', {\n            params,\n          })\n          .then((response) => {\n            const { data } = response;\n            if (callback != null) {\n              callback(data);\n            }\n          });\n      });\n    }\n\n    static updateStatus() {\n      // auth status\n      // 0: never request for auth\n      // 1: request but fail to success\n      // 2: success auth\n      if (!_isAuthRequested()) {\n        status = 0;\n        return;\n      }\n      this.getUserInfo((data) => {\n        if (data === null) {\n          status = 1;\n        } else {\n          status = 2;\n        }\n      });\n    }\n\n    static getAuth(callback) {\n      axios\n        .get(apiUrl, {\n          params: {\n            method: 'auth.gettoken',\n            api_key: options.apiKey,\n            format: 'json',\n          },\n        })\n        .then((response) => {\n          const { data } = response;\n          const { token } = data;\n          localStorage.setObject('lastfmtoken', token);\n          const grant_url = `https://www.last.fm/api/auth/?api_key=${options.apiKey}&token=${token}`;\n          window.open(grant_url, '_blank');\n          status = 1;\n          if (callback != null) {\n            callback();\n          }\n        });\n    }\n\n    static cancelAuth() {\n      localStorage.removeItem('lastfmsession');\n      localStorage.removeItem('lastfmtoken');\n      this.updateStatus();\n    }\n\n    static sendNowPlaying(track, artist, callback) {\n      this.getSession((session) => {\n        const params = {\n          method: 'track.updatenowplaying',\n          track,\n          artist,\n          api_key: options.apiKey,\n          sk: session.key,\n        };\n\n        params.api_sig = generateSign(params);\n        params.format = 'json';\n\n        axios\n          .post(apiUrl, '', {\n            params,\n          })\n          .then((response) => {\n            const { data } = response;\n            if (callback != null) {\n              callback(data);\n            }\n          });\n      });\n    }\n\n    static scrobble(timestamp, track, artist, album, callback) {\n      this.getSession((session) => {\n        const params = {\n          method: 'track.scrobble',\n          'timestamp[0]': timestamp,\n          'track[0]': track,\n          'artist[0]': artist,\n          api_key: options.apiKey,\n          sk: session.key,\n        };\n\n        if (album !== '' && album != null) {\n          params['album[0]'] = album;\n        }\n\n        params.api_sig = generateSign(params);\n        params.format = 'json';\n\n        axios\n          .post(apiUrl, '', {\n            params,\n          })\n          .then((response) => {\n            const { data } = response;\n            if (callback != null) {\n              callback(data);\n            }\n          });\n      });\n    }\n\n    static isAuthorized() {\n      return status === 2;\n    }\n\n    static isAuthRequested() {\n      return !(status === 0);\n    }\n\n    static getStatusText() {\n      switch (status) {\n        case 0:\n          return '未连接';\n        case 1:\n          return '连接中';\n        case 2:\n          return '已连接';\n        default:\n          return '';\n      }\n    }\n  }\n\n  window.lastfm = lastfm;\n}\n"
  },
  {
    "path": "js/loweb.js",
    "content": "/* global async LRUCache setPrototypeOfLocalStorage getLocalStorageValue */\n/* global netease xiami qq kugou kuwo bilibili migu taihe localmusic myplaylist */\n\nconst PROVIDERS = [\n  {\n    name: 'netease',\n    instance: netease,\n    searchable: true,\n    support_login: true,\n    id: 'ne',\n  },\n  {\n    name: 'xiami',\n    instance: xiami,\n    searchable: false,\n    hidden: true,\n    support_login: false,\n    id: 'xm',\n  },\n  {\n    name: 'qq',\n    instance: qq,\n    searchable: true,\n    support_login: true,\n    id: 'qq',\n  },\n  {\n    name: 'kugou',\n    instance: kugou,\n    searchable: true,\n    support_login: false,\n    id: 'kg',\n  },\n  {\n    name: 'kuwo',\n    instance: kuwo,\n    searchable: true,\n    support_login: false,\n    id: 'kw',\n  },\n  {\n    name: 'bilibili',\n    instance: bilibili,\n    searchable: true,\n    support_login: false,\n    id: 'bi',\n  },\n  {\n    name: 'migu',\n    instance: migu,\n    searchable: true,\n    support_login: true,\n    id: 'mg',\n  },\n  {\n    name: 'taihe',\n    instance: taihe,\n    searchable: true,\n    support_login: false,\n    id: 'th',\n  },\n  {\n    name: 'localmusic',\n    instance: localmusic,\n    searchable: false,\n    hidden: true,\n    support_login: false,\n    id: 'lm',\n  },\n  {\n    name: 'myplaylist',\n    instance: myplaylist,\n    searchable: false,\n    hidden: true,\n    support_login: false,\n    id: 'my',\n  },\n];\n\nfunction getProviderByName(sourceName) {\n  return (PROVIDERS.find((i) => i.name === sourceName) || {}).instance;\n}\n\nfunction getAllProviders() {\n  return PROVIDERS.filter((i) => !i.hidden).map((i) => i.instance);\n}\n\nfunction getAllSearchProviders() {\n  return PROVIDERS.filter((i) => i.searchable).map((i) => i.instance);\n}\n\nfunction getProviderNameByItemId(itemId) {\n  const prefix = itemId.slice(0, 2);\n  return (PROVIDERS.find((i) => i.id === prefix) || {}).name;\n}\n\nfunction getProviderByItemId(itemId) {\n  const prefix = itemId.slice(0, 2);\n  return (PROVIDERS.find((i) => i.id === prefix) || {}).instance;\n}\n\n/* cache for all playlist request except myplaylist and localmusic */\nconst playlistCache = new LRUCache({\n  max: 100,\n  maxAge: 60 * 60 * 1000, // 1 hour cache expire\n});\n\nfunction queryStringify(options) {\n  const query = JSON.parse(JSON.stringify(options));\n  return new URLSearchParams(query).toString();\n}\n\nsetPrototypeOfLocalStorage();\n\n// eslint-disable-next-line no-unused-vars\nconst MediaService = {\n  getLoginProviders() {\n    return PROVIDERS.filter((i) => !i.hidden && i.support_login);\n  },\n  search(source, options) {\n    const url = `/search?${queryStringify(options)}`;\n    if (source === 'allmusic') {\n      // search all platform and merge result\n      const callbackArray = getAllSearchProviders().map((p) => (fn) => {\n        p.search(url).success((r) => {\n          fn(null, r);\n        });\n      });\n      return {\n        success: (fn) =>\n          async.parallel(callbackArray, (err, platformResultArray) => {\n            // TODO: nicer pager, playlist support\n            const result = {\n              result: [],\n              total: 1000,\n              type: platformResultArray[0].type,\n            };\n            const maxLength = Math.max(\n              ...platformResultArray.map((elem) => elem.result.length)\n            );\n            for (let i = 0; i < maxLength; i += 1) {\n              platformResultArray.forEach((elem) => {\n                if (i < elem.result.length) {\n                  result.result.push(elem.result[i]);\n                }\n              });\n            }\n            return fn(result);\n          }),\n      };\n    }\n    const provider = getProviderByName(source);\n    return provider.search(url);\n  },\n\n  showMyPlaylist() {\n    return myplaylist.show_myplaylist('my');\n  },\n\n  showPlaylistArray(source, offset, filter_id) {\n    const provider = getProviderByName(source);\n    const url = `/show_playlist?${queryStringify({ offset, filter_id })}`;\n    return provider.show_playlist(url);\n  },\n\n  getPlaylistFilters(source) {\n    const provider = getProviderByName(source);\n    return provider.get_playlist_filters();\n  },\n\n  getLyric(track_id, album_id, lyric_url, tlyric_url) {\n    const provider = getProviderByItemId(track_id);\n    const url = `/lyric?${queryStringify({\n      track_id,\n      album_id,\n      lyric_url,\n      tlyric_url,\n    })}`;\n    return provider.lyric(url);\n  },\n\n  showFavPlaylist() {\n    return myplaylist.show_myplaylist('favorite');\n  },\n\n  queryPlaylist(listId, type) {\n    const result = myplaylist.myplaylist_containers(type, listId);\n    return {\n      success: (fn) => fn({ result }),\n    };\n  },\n\n  getPlaylist(listId, useCache = true) {\n    const provider = getProviderByItemId(listId);\n    const url = `/playlist?list_id=${listId}`;\n    let hit = null;\n    if (useCache) {\n      hit = playlistCache.get(listId);\n    }\n\n    if (hit) {\n      return {\n        success: (fn) => fn(hit),\n      };\n    }\n    return {\n      success: (fn) =>\n        provider.get_playlist(url).success((playlist) => {\n          if (provider !== myplaylist && provider !== localmusic) {\n            playlistCache.set(listId, playlist);\n          }\n          fn(playlist);\n        }),\n    };\n  },\n\n  clonePlaylist(id, type) {\n    const provider = getProviderByItemId(id);\n    const url = `/playlist?list_id=${id}`;\n    return {\n      success: (fn) => {\n        provider.get_playlist(url).success((data) => {\n          myplaylist.save_myplaylist(type, data);\n          fn();\n        });\n      },\n    };\n  },\n\n  removeMyPlaylist(id, type) {\n    myplaylist.remove_myplaylist(type, id);\n    return {\n      success: (fn) => fn(),\n    };\n  },\n\n  addMyPlaylist(id, track) {\n    const newPlaylist = myplaylist.add_track_to_myplaylist(id, track);\n    return {\n      success: (fn) => fn(newPlaylist),\n    };\n  },\n  insertTrackToMyPlaylist(id, track, to_track, direction) {\n    const newPlaylist = myplaylist.insert_track_to_myplaylist(\n      id,\n      track,\n      to_track,\n      direction\n    );\n    return {\n      success: (fn) => fn(newPlaylist),\n    };\n  },\n  addPlaylist(id, tracks) {\n    const provider = getProviderByItemId(id);\n    return provider.add_playlist(id, tracks);\n  },\n\n  removeTrackFromMyPlaylist(id, track) {\n    myplaylist.remove_track_from_myplaylist(id, track);\n    return {\n      success: (fn) => fn(),\n    };\n  },\n\n  removeTrackFromPlaylist(id, track) {\n    const provider = getProviderByItemId(id);\n    return provider.remove_from_playlist(id, track);\n  },\n\n  createMyPlaylist(title, track) {\n    myplaylist.create_myplaylist(title, track);\n    return {\n      success: (fn) => {\n        fn();\n      },\n    };\n  },\n  insertMyplaylistToMyplaylists(\n    playlistType,\n    playlistId,\n    toPlaylistId,\n    direction\n  ) {\n    const newPlaylists = myplaylist.insert_myplaylist_to_myplaylists(\n      playlistType,\n      playlistId,\n      toPlaylistId,\n      direction\n    );\n    return {\n      success: (fn) => fn(newPlaylists),\n    };\n  },\n  editMyPlaylist(id, title, coverImgUrl) {\n    myplaylist.edit_myplaylist(id, title, coverImgUrl);\n    return {\n      success: (fn) => fn(),\n    };\n  },\n\n  parseURL(url) {\n    return {\n      success: (fn) => {\n        const providers = getAllProviders();\n        Promise.all(\n          providers.map(\n            (provider) =>\n              new Promise((res, rej) =>\n                provider.parse_url(url).success((r) => {\n                  if (r !== undefined) {\n                    return rej(r);\n                  }\n                  return res(r);\n                })\n              )\n          )\n        )\n          .then(() => fn({}))\n          .catch((result) => fn({ result }));\n      },\n    };\n  },\n\n  mergePlaylist(source, target) {\n    const tarData = localStorage.getObject(target).tracks;\n    const srcData = localStorage.getObject(source).tracks;\n    tarData.forEach((tarTrack) => {\n      if (!srcData.find((srcTrack) => srcTrack.id === tarTrack.id)) {\n        myplaylist.add_track_to_myplaylist(source, tarTrack);\n      }\n    });\n    return {\n      success: (fn) => fn(),\n    };\n  },\n\n  bootstrapTrack(track, playerSuccessCallback, playerFailCallback) {\n    const successCallback = playerSuccessCallback;\n    const sound = {};\n    function failureCallback() {\n      if (localStorage.getObject('enable_auto_choose_source') === false) {\n        playerFailCallback();\n        return;\n      }\n      const trackPlatform = getProviderNameByItemId(track.id);\n      const failover_source_list = getLocalStorageValue(\n        'auto_choose_source_list',\n        ['kuwo', 'qq', 'migu']\n      ).filter((i) => i !== trackPlatform);\n\n      const getUrlPromises = failover_source_list.map(\n        (source) =>\n          new Promise((resolve, reject) => {\n            if (track.source === source) {\n              // come from same source, no need to check\n              resolve();\n              return;\n            }\n            // TODO: better query method\n            const keyword = `${track.title} ${track.artist}`;\n            const curpage = 1;\n            const url = `/search?keywords=${keyword}&curpage=${curpage}&type=0`;\n            const provider = getProviderByName(source);\n            provider.search(url).success((data) => {\n              for (let i = 0; i < data.result.length; i += 1) {\n                const searchTrack = data.result[i];\n                // compare search track and track to check if they are same\n                // TODO: better similar compare method (duration, md5)\n                if (\n                  !searchTrack.disable &&\n                  searchTrack.title === track.title &&\n                  searchTrack.artist === track.artist\n                ) {\n                  provider.bootstrap_track(\n                    searchTrack,\n                    (response) => {\n                      sound.url = response.url;\n                      sound.bitrate = response.bitrate;\n                      sound.platform = response.platform;\n                      reject(sound); // Use Reject to return immediately\n                    },\n                    resolve\n                  );\n                  return;\n                }\n              }\n              resolve(sound);\n            });\n          })\n      );\n      // TODO: Use Promise.any() in ES2021 replace the tricky workaround\n      Promise.all(getUrlPromises)\n        .then(playerFailCallback)\n        .catch((response) => {\n          playerSuccessCallback(response);\n        });\n    }\n\n    const provider = getProviderByName(track.source);\n\n    provider.bootstrap_track(track, successCallback, failureCallback);\n  },\n\n  login(source, options) {\n    const url = `/login?${queryStringify(options)}`;\n    const provider = getProviderByName(source);\n\n    return provider.login(url);\n  },\n  getUser(source) {\n    const provider = getProviderByName(source);\n    return provider.get_user();\n  },\n  getLoginUrl(source) {\n    const provider = getProviderByName(source);\n    return provider.get_login_url();\n  },\n  getUserCreatedPlaylist(source, options) {\n    const provider = getProviderByName(source);\n    const url = `/get_user_create_playlist?${queryStringify(options)}`;\n\n    return provider.get_user_created_playlist(url);\n  },\n  getUserFavoritePlaylist(source, options) {\n    const provider = getProviderByName(source);\n    const url = `/get_user_favorite_playlist?${queryStringify(options)}`;\n\n    return provider.get_user_favorite_playlist(url);\n  },\n  getRecommendPlaylist(source) {\n    const provider = getProviderByName(source);\n\n    return provider.get_recommend_playlist();\n  },\n  logout(source) {\n    const provider = getProviderByName(source);\n\n    return provider.logout();\n  },\n};\n\n// eslint-disable-next-line no-unused-vars\nconst loWeb = MediaService;\n"
  },
  {
    "path": "js/lowebutil.js",
    "content": "/* eslint-disable consistent-return */\n/* eslint-disable no-param-reassign */\n/* eslint-disable no-unused-vars */\n\nfunction getParameterByName(name, url) {\n  if (!url) url = window.location.href;\n  name = name.replace(/[[\\]]/g, '\\\\$&');\n  const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`);\n\n  const results = regex.exec(url);\n  if (!results) return null;\n  if (!results[2]) return '';\n  return decodeURIComponent(results[2].replace(/\\+/g, ' '));\n}\n\nfunction isElectron() {\n  return window && window.process && window.process.type;\n}\n\nfunction cookieGet(cookieRequest, callback) {\n  if (!isElectron()) {\n    return chrome.cookies.get(cookieRequest, (cookie) => {\n      callback(cookie);\n    });\n  }\n  const remote = require('@electron/remote'); // eslint-disable-line\n  remote.session.defaultSession.cookies\n    .get(cookieRequest)\n    .then((cookieArray) => {\n      let cookie = null;\n      if (cookieArray.length > 0) {\n        [cookie] = cookieArray;\n      }\n      callback(cookie);\n    });\n}\n\nfunction cookieSet(cookie, callback) {\n  if (!isElectron()) {\n    return chrome.cookies.set(cookie, (arg1, arg2) => {\n      callback(arg1, arg2);\n    });\n  }\n  const remote = require('@electron/remote'); // eslint-disable-line\n  remote.session.defaultSession.cookies.set(cookie).then((arg1, arg2) => {\n    callback(null, arg1, arg2);\n  });\n}\nfunction cookieRemove(cookie, callback) {\n  if (!isElectron()) {\n    return chrome.cookies.remove(cookie, (arg1, arg2) => {\n      callback(arg1, arg2);\n    });\n  }\n  const remote = require('@electron/remote'); // eslint-disable-line\n  remote.session.defaultSession.cookies\n    .remove(cookie.url, cookie.name)\n    .then((arg1, arg2) => {\n      callback(null, arg1, arg2);\n    });\n}\n\nfunction setPrototypeOfLocalStorage() {\n  const proto = Object.getPrototypeOf(localStorage);\n  proto.getObject = function getObject(key) {\n    const value = this.getItem(key);\n    try {\n      return value && JSON.parse(value);\n    } catch (error) {\n      return {};\n    }\n  };\n  proto.setObject = function setObject(key, value) {\n    this.setItem(key, JSON.stringify(value));\n  };\n  Object.setPrototypeOf(localStorage, proto);\n}\n\nfunction getLocalStorageValue(key, defaultValue) {\n  const keyString = localStorage.getItem(key);\n  let result = keyString && JSON.parse(keyString);\n  if (result === null) {\n    result = defaultValue;\n  }\n  return result;\n}\n\nfunction easeInOutQuad(t, b, c, d) {\n  // t = current time\n  // b = start value\n  // c = change in value\n  // d = duration\n  t /= d / 2;\n  if (t < 1) return (c / 2) * t * t + b;\n  t -= 1;\n  return (-c / 2) * (t * (t - 2) - 1) + b;\n}\n\nfunction smoothScrollTo(element, to, duration) {\n  const start = element.scrollTop;\n  const change = to - start;\n  const startTime = performance.now();\n\n  const animateScroll = (currentTime) => {\n    const timeElapsed = currentTime - startTime;\n    const val = easeInOutQuad(timeElapsed, start, change, duration);\n    element.scrollTop = val;\n    if (timeElapsed < duration) {\n      requestAnimationFrame(animateScroll);\n    } else {\n      element.scrollTop = to; // Ensure it ends exactly at 'to'\n    }\n  };\n  requestAnimationFrame(animateScroll);\n}\n"
  },
  {
    "path": "js/myplaylist.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global getParameterByName */\nconst myplaylistFactory = () => {\n  function array_move(arr, old_index, new_index) {\n    // https://stackoverflow.com/questions/5306680/move-an-array-element-from-one-array-position-to-another\n    if (new_index >= arr.length) {\n      let k = new_index - arr.length + 1;\n      while (k > 0) {\n        k -= 1;\n        arr.push(undefined);\n      }\n    }\n    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);\n    return arr; // for testing\n  }\n  function getPlaylistObjectKey(playlist_type) {\n    let key = '';\n    if (playlist_type === 'my') {\n      key = 'playerlists';\n    } else if (playlist_type === 'favorite') {\n      key = 'favoriteplayerlists';\n    }\n    return key;\n  }\n  function show_myplaylist(playlist_type) {\n    return {\n      success(fn) {\n        const key = getPlaylistObjectKey(playlist_type);\n        if (key === '') {\n          return fn({ result: [] });\n        }\n        let playlists = localStorage.getObject(key);\n        if (playlists == null) {\n          playlists = [];\n        }\n        const result = playlists.reduce((res, id) => {\n          const playlist = localStorage.getObject(id);\n          if (playlist !== null && playlist.tracks !== undefined) {\n            // clear url field when load old playlist\n            playlist.tracks.forEach((e) => {\n              delete e.url;\n            });\n          }\n          res.push(playlist);\n          return res;\n        }, []);\n        return fn({ result });\n      },\n    };\n  }\n\n  function get_myplaylist(url) {\n    const list_id = getParameterByName('list_id', url);\n    return {\n      success(fn) {\n        const playlist = localStorage.getObject(list_id);\n        // clear url field when load old playlist\n        if (playlist !== null && playlist.tracks !== undefined) {\n          playlist.tracks.forEach((e) => {\n            delete e.url;\n            e.disabled = false;\n          });\n        }\n        fn(playlist);\n      },\n    };\n  }\n\n  function guid() {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000)\n        .toString(16)\n        .substring(1);\n    }\n    return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;\n  }\n\n  function insert_myplaylist_to_myplaylists(\n    playlist_type,\n    playlist_id,\n    to_playlist_id,\n    direction\n  ) {\n    const key = getPlaylistObjectKey(playlist_type);\n    if (key === '') {\n      return [];\n    }\n    const playlists = localStorage.getObject(key);\n\n    const index = playlists.findIndex((i) => i === playlist_id);\n    let insertIndex = playlists.findIndex((i) => i === to_playlist_id);\n    if (index === insertIndex) {\n      return playlists;\n    }\n    if (insertIndex > index) {\n      insertIndex -= 1;\n    }\n    const offset = direction === 'top' ? 0 : 1;\n\n    array_move(playlists, index, insertIndex + offset);\n\n    localStorage.setObject(key, playlists);\n    return playlists;\n  }\n\n  const save_myplaylist = (playlist_type, playlistObj) => {\n    const playlist = playlistObj;\n    const key = getPlaylistObjectKey(playlist_type);\n    if (key === '') {\n      return;\n    }\n    let playlists = localStorage.getObject(key);\n    if (playlists == null) {\n      playlists = [];\n    }\n    // update listid\n    let playlist_id = '';\n    if (playlist_type === 'my') {\n      playlist_id = `myplaylist_${guid()}`;\n      playlist.info.id = playlist_id;\n      playlist.is_mine = 1; // eslint-disable-line no-param-reassign\n    } else if (playlist_type === 'favorite') {\n      playlist_id = playlist.info.id;\n      playlist.is_fav = 1;\n      // remove all tracks info, cause favorite playlist always load latest\n      delete playlist.tracks;\n    }\n\n    playlists.push(playlist_id);\n    localStorage.setObject(key, playlists);\n    localStorage.setObject(playlist_id, playlist);\n  };\n\n  const remove_myplaylist = (playlist_type, playlist_id) => {\n    const key = getPlaylistObjectKey(playlist_type);\n    if (key === '') {\n      return;\n    }\n    const playlists = localStorage.getObject(key);\n    if (playlists == null) {\n      return;\n    }\n    const newplaylists = playlists.filter((item) => item !== playlist_id);\n    localStorage.removeItem(playlist_id);\n    localStorage.setObject(key, newplaylists);\n  };\n\n  function add_track_to_myplaylist(playlist_id, track) {\n    const playlist = localStorage.getObject(playlist_id);\n    if (playlist == null) {\n      return null;\n    }\n    // new track will always insert in beginning of playlist\n    if (Array.isArray(track)) {\n      playlist.tracks = track.concat(playlist.tracks);\n    } else {\n      playlist.tracks.unshift(track);\n    }\n\n    // dedupe\n    const newTracks = [];\n    const trackIds = [];\n\n    playlist.tracks.forEach((tracki) => {\n      if (trackIds.indexOf(tracki.id) === -1) {\n        newTracks.push(tracki);\n        trackIds.push(tracki.id);\n      }\n    });\n    playlist.tracks = newTracks;\n\n    localStorage.setObject(playlist_id, playlist);\n    return playlist;\n  }\n\n  function insert_track_to_myplaylist(playlist_id, track, to_track, direction) {\n    const playlist = localStorage.getObject(playlist_id);\n    if (playlist == null) {\n      return null;\n    }\n    const index = playlist.tracks.findIndex((i) => i.id === track.id);\n    let insertIndex = playlist.tracks.findIndex((i) => i.id === to_track.id);\n    if (index === insertIndex) {\n      return playlist;\n    }\n    if (insertIndex > index) {\n      insertIndex -= 1;\n    }\n    const offset = direction === 'top' ? 0 : 1;\n    array_move(playlist.tracks, index, insertIndex + offset);\n    localStorage.setObject(playlist_id, playlist);\n    return playlist;\n  }\n\n  function remove_track_from_myplaylist(playlist_id, track_id) {\n    const playlist = localStorage.getObject(playlist_id);\n    if (playlist == null) {\n      return;\n    }\n    const newtracks = playlist.tracks.filter((item) => item.id !== track_id);\n    playlist.tracks = newtracks;\n    localStorage.setObject(playlist_id, playlist);\n  }\n\n  function create_myplaylist(playlist_title, track) {\n    const playlist = {};\n\n    const info = {\n      cover_img_url: 'images/mycover.jpg',\n      title: playlist_title,\n      id: '',\n      source_url: '',\n    };\n\n    playlist.is_mine = 1;\n    playlist.info = info;\n\n    if (Array.isArray(track)) {\n      playlist.tracks = track;\n    } else {\n      playlist.tracks = [track];\n    }\n\n    // notice: create only used by my playlist, favorite created by clone interface\n    save_myplaylist('my', playlist);\n  }\n\n  function edit_myplaylist(playlist_id, title, cover_img_url) {\n    const playlist = localStorage.getObject(playlist_id);\n    if (playlist == null) {\n      return;\n    }\n    playlist.info.title = title;\n    playlist.info.cover_img_url = cover_img_url;\n    localStorage.setObject(playlist_id, playlist);\n  }\n\n  function myplaylist_containers(playlist_type, list_id) {\n    const key = getPlaylistObjectKey(playlist_type);\n    if (key === '') {\n      return false;\n    }\n    const playlist = localStorage.getObject(list_id);\n    return playlist !== null && playlist.is_fav;\n  }\n\n  return {\n    show_myplaylist,\n    save_myplaylist,\n    get_playlist: get_myplaylist,\n    remove_myplaylist,\n    add_track_to_myplaylist,\n    remove_track_from_myplaylist,\n    create_myplaylist,\n    edit_myplaylist,\n    myplaylist_containers,\n    insert_track_to_myplaylist,\n    insert_myplaylist_to_myplaylists,\n  };\n};\n\nconst myplaylist = myplaylistFactory(); // eslint-disable-line no-unused-vars\n"
  },
  {
    "path": "js/oauth_callback.js",
    "content": "/**\n * Get and send oauth tokens from query string.\n */\n\nchrome.runtime.sendMessage(\n  {\n    type: 'code',\n    code: new URLSearchParams(window.location.search).get('code'),\n  },\n  // eslint-disable-next-line no-unused-vars\n  (response) => {\n    // window.open('', '_self', '');\n    // window.close();\n  }\n);\n"
  },
  {
    "path": "js/player_thread.js",
    "content": "/* eslint-disable no-underscore-dangle */\n/* global MediaMetadata playerSendMessage MediaService */\n/* global Howl Howler */\n{\n  /**\n   * Player class containing the state of our playlist and where we are in it.\n   * Includes all methods for playing, skipping, updating the display, etc.\n   * @param {Array} playlist Array of objects with playlist song details ({title, file, howl}).\n   */\n  class Player {\n    constructor() {\n      this.playlist = [];\n      this._random_playlist = [];\n      this.index = -1;\n      this._loop_mode = 0;\n      this._media_uri_list = {};\n      this.playedFrom = 0;\n      this.mode = 'background';\n      this.skipTime = 15;\n    }\n\n    setMode(newMode) {\n      this.mode = newMode;\n    }\n\n    setRefreshRate(rate = 10) {\n      clearInterval(this.refreshTimer);\n      this.refreshTimer = setInterval(() => {\n        if (this.playing) {\n          this.sendFrameUpdate();\n        }\n      }, 1000 / rate);\n    }\n\n    get currentAudio() {\n      return this.playlist[this.index];\n    }\n\n    get currentHowl() {\n      return this.currentAudio && this.currentAudio.howl;\n    }\n\n    get playing() {\n      return this.currentHowl ? this.currentHowl.playing() : false;\n    }\n\n    // eslint-disable-next-line class-methods-use-this\n    get muted() {\n      return !!Howler._muted;\n    }\n\n    insertAudio(audio, idx) {\n      if (this.playlist.find((i) => audio.id === i.id)) return;\n\n      const audioData = {\n        ...audio,\n        disabled: false, // avoid first time load block\n        howl: null,\n      };\n      if (idx) {\n        this.playlist.splice(idx, 0, [audio]);\n      } else {\n        this.playlist.push(audioData);\n      }\n      this.sendPlaylistEvent();\n      this.sendLoadEvent();\n    }\n\n    static array_move(arr, old_index, new_index) {\n      // https://stackoverflow.com/questions/5306680/move-an-array-element-from-one-array-position-to-another\n      if (new_index >= arr.length) {\n        let k = new_index - arr.length + 1;\n        while (k > 0) {\n          k -= 1;\n          arr.push(undefined);\n        }\n      }\n      arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);\n      return arr; // for testing\n    }\n\n    insertAudioByDirection(audio, to_audio, direction) {\n      const originTrack = this.playlist[this.index];\n      const index = this.playlist.findIndex((i) => i.id === audio.id);\n      let insertIndex = this.playlist.findIndex((i) => i.id === to_audio.id);\n      if (index === insertIndex) {\n        return;\n      }\n      if (insertIndex > index) {\n        insertIndex -= 1;\n      }\n      const offset = direction === 'top' ? 0 : 1;\n      this.playlist = Player.array_move(\n        this.playlist,\n        index,\n        insertIndex + offset\n      );\n      const foundOriginTrackIndex = this.playlist.findIndex(\n        (i) => i.id === originTrack.id\n      );\n      if (foundOriginTrackIndex >= 0) {\n        this.index = foundOriginTrackIndex;\n      }\n\n      this.sendPlaylistEvent();\n      this.sendLoadEvent();\n    }\n\n    removeAudio(idx) {\n      if (!this.playlist[idx]) {\n        return;\n      }\n      // restore playing status before change\n      const isPlaying = this.playing;\n      const { id: trackId } = this.currentAudio;\n\n      if (isPlaying && this.playlist[idx].id === trackId) {\n        this.pause();\n      }\n\n      this.playlist.splice(idx, 1);\n      const newIndex = this.playlist.findIndex((i) => i.id === trackId);\n      if (newIndex >= 0) {\n        this.index = newIndex;\n      } else {\n        // current playing is deleted\n        if (idx >= this.playlist.length) {\n          this.index = this.playlist.length - 1;\n        } else {\n          this.index = idx;\n        }\n        if (isPlaying) {\n          this.play();\n        }\n      }\n\n      this.sendPlaylistEvent();\n      this.sendLoadEvent();\n    }\n\n    appendAudioList(list) {\n      if (!Array.isArray(list)) {\n        return;\n      }\n      list.forEach((audio) => {\n        this.insertAudio(audio);\n      });\n    }\n\n    clearPlaylist() {\n      this.stopAll(); // stop the loadded track before remove list\n      this.playlist = [];\n      Howler.unload();\n      this.sendPlaylistEvent();\n      this.sendLoadEvent();\n    }\n\n    stopAll() {\n      this.playlist.forEach((i) => {\n        if (i.howl) {\n          i.howl.stop();\n        }\n      });\n    }\n\n    setNewPlaylist(list) {\n      if (list.length) {\n        // stop current\n        this.stopAll();\n        Howler.unload();\n\n        this.playlist = list.map((audio) => ({\n          ...audio,\n          howl: null,\n        }));\n        // TODO: random mode need random choose first song to load\n        this.index = 0;\n        this.load(0);\n      }\n      this.sendPlaylistEvent();\n    }\n\n    playById(id) {\n      const idx = this.playlist.findIndex((audio) => audio.id === id);\n      this.play(idx);\n    }\n\n    loadById(id) {\n      const idx = this.playlist.findIndex((audio) => audio.id === id);\n      this.load(idx);\n    }\n\n    /**\n     * Play a song in the playlist.\n     * @param  {Number} index Index of the song in the playlist\n     * (leave empty to play the first or current).\n     */\n    play(idx) {\n      this.load(idx);\n\n      const data = this.playlist[this.index];\n      if (!data.howl || !this._media_uri_list[data.id]) {\n        this.retrieveMediaUrl(this.index, true);\n      } else {\n        this.finishLoad(this.index, true);\n      }\n    }\n\n    retrieveMediaUrl(index, playNow) {\n      const msg = {\n        type: 'BG_PLAYER:RETRIEVE_URL',\n        data: {\n          ...this.playlist[index],\n          howl: undefined,\n          index,\n          playNow,\n        },\n      };\n\n      MediaService.bootstrapTrack(\n        msg.data,\n        (bootinfo) => {\n          msg.type = 'BG_PLAYER:RETRIEVE_URL_SUCCESS';\n\n          msg.data = { ...msg.data, ...bootinfo };\n\n          this.playlist[index].bitrate = bootinfo.bitrate;\n          this.playlist[index].platform = bootinfo.platform;\n\n          this.setMediaURI(msg.data.url, msg.data.id);\n          this.setAudioDisabled(false, msg.data.index);\n          this.finishLoad(msg.data.index, playNow);\n          playerSendMessage(this.mode, msg);\n        },\n        () => {\n          msg.type = 'BG_PLAYER:RETRIEVE_URL_FAIL';\n\n          this.setAudioDisabled(true, msg.data.index);\n          playerSendMessage(this.mode, msg);\n\n          this.skip('next');\n        }\n      );\n    }\n\n    /**\n     * Load a song from the playlist.\n     * @param  {Number} index Index of the song in the playlist\n     * (leave empty to load the first or current).\n     */\n    load(idx) {\n      let index = typeof idx === 'number' ? idx : this.index;\n      if (index < 0) return;\n      if (!this.playlist[index]) {\n        index = 0;\n      }\n      // stop when load new track to avoid multiple songs play in same time\n      if (index !== this.index) {\n        Howler.unload();\n      }\n      this.index = index;\n\n      this.sendLoadEvent();\n    }\n\n    finishLoad(index, playNow) {\n      const data = this.playlist[index];\n\n      // If we already loaded this track, use the current one.\n      // Otherwise, setup and load a new Howl.\n      const self = this;\n      if (!data.howl) {\n        data.howl = new Howl({\n          src: [self._media_uri_list[data.url || data.id]],\n          format: 'mp3', // bypass Howl checking url extension, issue #1200\n          volume: 1,\n          mute: self.muted,\n          html5: true, // Force to HTML5 so that the audio can stream in (best for large files).\n          onplay() {\n            if ('mediaSession' in navigator) {\n              const { mediaSession } = navigator;\n              mediaSession.playbackState = 'playing';\n              mediaSession.metadata = new MediaMetadata({\n                title: self.currentAudio.title,\n                artist: self.currentAudio.artist,\n                album: `Listen 1  •  ${(\n                  self.currentAudio.album || '<???>'\n                ).padEnd(100)}`,\n                artwork: [\n                  {\n                    src: self.currentAudio.img_url,\n                    sizes: '500x500',\n                  },\n                ],\n              });\n            }\n            self.currentAudio.disabled = false;\n            // Date.now() returns a millisecond timestamp that needs to be converted to a second timestamp\n            self.playedFrom = Math.round(Date.now() / 1000);\n            self.sendPlayingEvent('Playing');\n          },\n          onload() {\n            self.currentAudio.disabled = false;\n            self.sendPlayingEvent('Loaded');\n          },\n          onend() {\n            switch (self.loop_mode) {\n              case 2:\n                self.skip('random');\n                break;\n\n              case 1:\n                self.play();\n                break;\n\n              case 0:\n              default:\n                self.skip('next');\n                break;\n            }\n            self.sendPlayingEvent('Ended');\n          },\n          onpause() {\n            navigator.mediaSession.playbackState = 'paused';\n            self.sendPlayingEvent('Paused');\n          },\n          onstop() {\n            self.sendPlayingEvent('Stopped');\n          },\n          onseek() {},\n          onvolume() {},\n          onloaderror(id, err) {\n            playerSendMessage(this.mode, {\n              type: 'BG_PLAYER:PLAY_FAILED',\n              data: err,\n            });\n            self.currentAudio.disabled = true;\n            self.sendPlayingEvent('err');\n            self.currentHowl.unload();\n            data.howl = null;\n            delete self._media_uri_list[data.id];\n          },\n          onplayerror(id, err) {\n            playerSendMessage(this.mode, {\n              type: 'BG_PLAYER:PLAY_FAILED',\n              data: err,\n            });\n            self.currentAudio.disabled = true;\n            self.sendPlayingEvent('err');\n          },\n        });\n      }\n\n      if (playNow) {\n        if (this.playing && index === this.index) {\n          return;\n        }\n        this.playlist.forEach((i) => {\n          if (i.howl && i.howl !== this.currentHowl) {\n            i.howl.stop();\n          }\n        });\n        this.currentHowl.play();\n      }\n    }\n\n    /**\n     * Pause the currently playing track.\n     */\n    pause() {\n      if (!this.currentHowl) return;\n\n      // Puase the sound.\n      this.currentHowl.pause();\n    }\n\n    /**\n     * Skip to the next or previous track.\n     * @param  {String} direction 'next' or 'prev'.\n     */\n    skip(direction) {\n      Howler.unload();\n      // Get the next track based on the direction of the track.\n      const nextIndexFn = (idx) => {\n        const l = this.playlist.length;\n        const random_mode = this._loop_mode === 2 || direction === 'random';\n\n        let rdx = idx;\n\n        if (random_mode) {\n          if (this._random_playlist.length / 2 !== l) {\n            // construction random playlist\n            const a = Array.from({ length: l }, (_v, i) => i);\n            for (let i = 0; i < l; i += 1) {\n              const e = l - i - 1;\n              const s = Math.floor(Math.random() * e);\n              const t = a[s];\n              a[s] = a[e];\n              a[e] = t;\n              // lookup table\n              a[t + l] = e;\n            }\n            this._random_playlist = a;\n          }\n          rdx = this._random_playlist[idx + l];\n        } else if (this._random_playlist.length !== 0) {\n          // clear random playlist\n          this._random_playlist = [];\n        }\n\n        if (direction === 'prev') {\n          if (rdx === 0) rdx = l;\n          rdx -= 1;\n        } else {\n          rdx += 1;\n        }\n        const result = random_mode ? this._random_playlist[rdx % l] : rdx % l;\n\n        return result;\n      };\n      this.index = nextIndexFn(this.index);\n\n      let tryCount = 0;\n      while (tryCount < this.playlist.length) {\n        if (!this.playlist[this.index].disabled) {\n          this.play(this.index);\n          return;\n        }\n        this.index = nextIndexFn(this.index);\n        tryCount += 1;\n      }\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:RETRIEVE_URL_FAIL_ALL',\n      });\n      this.sendLoadEvent();\n    }\n\n    set loop_mode(input) {\n      const LOOP_MODE = {\n        all: 0,\n        one: 1,\n        shuffle: 2,\n      };\n      let myMode = 0;\n      if (typeof input === 'string') {\n        myMode = LOOP_MODE[input];\n      } else {\n        myMode = input;\n      }\n      if (!Object.values(LOOP_MODE).includes(myMode)) {\n        return;\n      }\n      this._loop_mode = myMode;\n    }\n\n    get loop_mode() {\n      return this._loop_mode;\n    }\n\n    /**\n     * Set the volume and update the volume slider display.\n     * @param  {Number} val Volume between 0 and 1.\n     */\n    set volume(val) {\n      // Update the global volume (affecting all Howls).\n      if (typeof val === 'number') {\n        Howler.volume(val);\n        this.sendVolumeEvent();\n        this.sendFrameUpdate();\n      }\n    }\n\n    // eslint-disable-next-line class-methods-use-this\n    get volume() {\n      return Howler.volume();\n    }\n\n    adjustVolume(inc) {\n      this.volume = inc\n        ? Math.min(this.volume + 0.1, 1)\n        : Math.max(this.volume - 0.1, 0);\n      this.sendVolumeEvent();\n      this.sendFrameUpdate();\n    }\n\n    mute() {\n      Howler.mute(true);\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:MUTE',\n        data: true,\n      });\n    }\n\n    unmute() {\n      Howler.mute(false);\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:MUTE',\n        data: false,\n      });\n    }\n\n    /**\n     * Seek to a new position in the currently playing track.\n     * @param  {Number} per Percentage through the song to skip.\n     */\n    seek(per) {\n      if (!this.currentHowl) return;\n\n      // Get the Howl we want to manipulate.\n      const audio = this.currentHowl;\n\n      // Convert the percent into a seek position.\n      // if (audio.playing()) {\n      // }\n      audio.seek(audio.duration() * per);\n    }\n    /**\n     * Seek to a new position in the currently playing track.\n     * @param {Number} seconds Seconds through the song to skip.\n     */\n\n    seekTime(seconds) {\n      if (!this.currentHowl) return;\n      const audio = this.currentHowl;\n      audio.seek(seconds);\n    }\n\n    /**\n     * Format the time from seconds to M:SS.\n     * @param  {Number} secs Seconds to format.\n     * @return {String}      Formatted time.\n     */\n    static formatTime(secs) {\n      const minutes = Math.floor(secs / 60) || 0;\n      const seconds = secs - minutes * 60 || 0;\n\n      return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;\n    }\n\n    setMediaURI(uri, url) {\n      if (url) {\n        this._media_uri_list[url] = uri;\n      }\n    }\n\n    setAudioDisabled(disabled, idx) {\n      if (this.playlist[idx]) {\n        this.playlist[idx].disabled = disabled;\n      }\n    }\n\n    async sendFrameUpdate() {\n      const data = {\n        id: this.currentAudio ? this.currentAudio.id : 0,\n        duration: this.currentHowl ? this.currentHowl.duration() : 0,\n        pos: this.currentHowl ? this.currentHowl.seek() : 0,\n        playedFrom: this.playedFrom,\n        playing: this.playing,\n      };\n      if ('setPositionState' in navigator.mediaSession) {\n        navigator.mediaSession.setPositionState({\n          duration: this.currentHowl ? this.currentHowl.duration() : 0,\n          playbackRate: this.currentHowl ? this.currentHowl.rate() : 1,\n          position: this.currentHowl ? this.currentHowl.seek() : 0,\n        });\n      }\n\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:FRAME_UPDATE',\n        data,\n      });\n    }\n\n    async sendPlayingEvent(reason = 'UNKNOWN') {\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:PLAY_STATE',\n        data: {\n          isPlaying: this.playing,\n          reason,\n        },\n      });\n    }\n\n    async sendLoadEvent() {\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:LOAD',\n        data: {\n          currentPlaying: {\n            ...this.currentAudio,\n            howl: undefined,\n          },\n          playlist: {\n            index: this.index,\n            length: this.playlist.length,\n          },\n        },\n      });\n    }\n\n    async sendVolumeEvent() {\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:VOLUME',\n        data: this.volume * 100,\n      });\n    }\n\n    async sendPlaylistEvent() {\n      playerSendMessage(this.mode, {\n        type: 'BG_PLAYER:PLAYLIST',\n        data: this.playlist.map((audio) => ({ ...audio, howl: undefined })),\n      });\n    }\n  }\n\n  // Setup our new audio player class and pass it the playlist.\n\n  const threadPlayer = new Player();\n  threadPlayer.setRefreshRate();\n  window.threadPlayer = threadPlayer;\n\n  if ('mediaSession' in navigator) {\n    const { mediaSession } = navigator;\n    mediaSession.setActionHandler('play', () => {\n      threadPlayer.play();\n    });\n    mediaSession.setActionHandler('pause', () => {\n      threadPlayer.pause();\n    });\n    mediaSession.setActionHandler('seekforward', (details) => {\n      // User clicked \"Seek Forward\" media notification icon.\n      const { currentHowl } = threadPlayer;\n      const skipTime = details.seekOffset || threadPlayer.skipTime;\n      const newTime = Math.min(\n        currentHowl.seek() + skipTime,\n        currentHowl.duration()\n      );\n      threadPlayer.seekTime(newTime);\n      threadPlayer.sendFrameUpdate();\n    });\n    mediaSession.setActionHandler('seekbackward', (details) => {\n      // User clicked \"Seek Backward\" media notification icon.\n      const { currentHowl } = threadPlayer;\n      const skipTime = details.seekOffset || threadPlayer.skipTime;\n      const newTime = Math.max(currentHowl.seek() - skipTime, 0);\n      threadPlayer.seekTime(newTime);\n      threadPlayer.sendFrameUpdate();\n    });\n    mediaSession.setActionHandler('seekto', (details) => {\n      const { seekTime } = details;\n      threadPlayer.seekTime(seekTime);\n      threadPlayer.sendFrameUpdate();\n    });\n    mediaSession.setActionHandler('nexttrack', () => {\n      threadPlayer.skip('next');\n      threadPlayer.sendFrameUpdate();\n    });\n    mediaSession.setActionHandler('previoustrack', () => {\n      threadPlayer.skip('prev');\n      threadPlayer.sendFrameUpdate();\n    });\n  }\n  playerSendMessage(this.mode, {\n    type: 'BG_PLAYER:READY',\n  });\n}\n"
  },
  {
    "path": "js/provider/bilibili.js",
    "content": "let wbi_key = null;\n/* global getParameterByName */\n// eslint-disable-next-line no-unused-vars\n/* global cookieSet cookieGet */\n// eslint-disable-next-line no-unused-vars\nclass bilibili {\n  static htmlDecode(value) {\n    const parser = new DOMParser();\n    return parser.parseFromString(value, 'text/html').body.textContent;\n  }\n\n  static fetch_wbi_key() {\n    return axios({\n      url: 'https://api.bilibili.com/x/web-interface/nav',\n      method: 'get',\n      responseType: 'json',\n    }).then((resp) => {\n      const json_content = resp.data;\n      const { img_url } = json_content.data.wbi_img;\n      const { sub_url } = json_content.data.wbi_img;\n      return {\n        img_key: img_url.slice(\n          img_url.lastIndexOf('/') + 1,\n          img_url.lastIndexOf('.')\n        ),\n        sub_key: sub_url.slice(\n          sub_url.lastIndexOf('/') + 1,\n          sub_url.lastIndexOf('.')\n        ),\n      };\n    });\n  }\n\n  static clear_wbi_key() {\n    wbi_key = null;\n  }\n\n  static get_wbi_key() {\n    if (wbi_key) {\n      return Promise.resolve(wbi_key);\n    }\n    return bilibili.fetch_wbi_key().then((key) => {\n      wbi_key = key;\n      return key;\n    });\n  }\n\n  static enc_wbi(params) {\n    return bilibili.get_wbi_key().then(({ img_key, sub_key }) => {\n      const mixinKeyEncTab = [\n        46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5,\n        49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24,\n        55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63,\n        57, 62, 11, 36, 20, 34, 44, 52,\n      ];\n\n      // 对 imgKey 和 subKey 进行字符顺序打乱编码\n      function get_mixin_key(original) {\n        let temp = '';\n        mixinKeyEncTab.forEach((n) => {\n          temp += original[n];\n        });\n        return temp.slice(0, 32);\n      }\n\n      const mixin_key = get_mixin_key(img_key + sub_key);\n      const curr_time = Math.round(Date.now() / 1000);\n      const chr_filter = /[!'()*]/g;\n      const query = [];\n      Object.assign(params, { wts: curr_time }); // 添加 wts 字段\n      // 按照 key 重排参数\n      Object.keys(params)\n        .sort()\n        .forEach((key) => {\n          query.push(\n            `${encodeURIComponent(key)}=${encodeURIComponent(\n              // 过滤 value 中的 \"!'()*\" 字符\n              params[key].toString().replace(chr_filter, '')\n            )}`\n          );\n        });\n      const query_string = query.join('&');\n      const wbi_sign = window.forge.md5\n        .create()\n        .update(window.forge.util.encodeUtf8(query_string + mixin_key))\n        .digest()\n        .toHex();\n      return `${query_string}&w_rid=${wbi_sign}`;\n    });\n  }\n\n  static wrap_wbi_request(url, params) {\n    return bilibili\n      .enc_wbi(params)\n      .then((query_string) => {\n        const target_url = `${url}?${query_string}`;\n        return axios.get(target_url);\n      })\n      .catch(() => {\n        // 失败时进行一次清空 wbi_key 后的重试，避免因为 wbi_key 过期导致的错误\n        bilibili.clear_wbi_key();\n        return bilibili\n          .enc_wbi(params)\n          .then((query_string) => {\n            const target_url = `${url}?${query_string}`;\n            return axios.get(target_url);\n          })\n          .catch(() => undefined);\n      });\n  }\n\n  static bi_convert_song(song_info) {\n    const track = {\n      id: `bitrack_${song_info.id}`,\n      title: song_info.title,\n      artist: song_info.uname,\n      artist_id: `biartist_${song_info.uid}`,\n      source: 'bilibili',\n      source_url: `https://www.bilibili.com/audio/au${song_info.id}`,\n      img_url: song_info.cover,\n      // url: song_info.id,\n      lyric_url: song_info.lyric,\n    };\n    return track;\n  }\n\n  static bi_convert_song2(song_info) {\n    let imgUrl = song_info.pic;\n    if (imgUrl.startsWith('//')) {\n      imgUrl = `https:${imgUrl}`;\n    }\n    const track = {\n      id: `bitrack_v_${song_info.bvid}`,\n      title: this.htmlDecode(song_info.title),\n      artist: this.htmlDecode(song_info.author),\n      artist_id: `biartist_v_${song_info.mid}`,\n      source: 'bilibili',\n      source_url: `https://www.bilibili.com/${song_info.bvid}`,\n      img_url: imgUrl,\n    };\n    return track;\n  }\n\n  static show_playlist(url) {\n    let offset = getParameterByName('offset', url);\n    if (offset === undefined) {\n      offset = 0;\n    }\n    const page = offset / 20 + 1;\n    const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/hit?ps=20&pn=${page}`;\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response.data.data;\n          const result = data.map((item) => ({\n            cover_img_url: item.cover,\n            title: item.title,\n            id: `biplaylist_${item.menuId}`,\n            source_url: `https://www.bilibili.com/audio/am${item.menuId}`,\n          }));\n          return fn({\n            result,\n          });\n        });\n      },\n    };\n  }\n\n  static bi_get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/info?sid=${list_id}`;\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response.data;\n          const info = {\n            cover_img_url: data.cover,\n            title: data.title,\n            id: `biplaylist_${list_id}`,\n            source_url: `https://www.bilibili.com/audio/am${list_id}`,\n          };\n          const target = `https://www.bilibili.com/audio/music-service-c/web/song/of-menu?pn=1&ps=100&sid=${list_id}`;\n          axios.get(target).then((res) => {\n            const tracks = res.data.data.data.map((item) =>\n              this.bi_convert_song(item)\n            );\n            return fn({\n              info,\n              tracks,\n            });\n          });\n        });\n      },\n    };\n  }\n\n  // eslint-disable-next-line no-unused-vars\n  static bi_album(url) {\n    return {\n      success: (fn) =>\n        fn({\n          tracks: [],\n          info: {},\n        }),\n      // bilibili havn't album\n      // const album_id = getParameterByName('list_id', url).split('_').pop();\n      // const target_url = '';\n      // axios.get(target_url).then((response) => {\n      //   const data = response.data;\n      //   const info = {};\n      //   const tracks = [];\n      //   return fn({\n      //     tracks,\n      //     info,\n      //   });\n      // });\n    };\n  }\n\n  static bi_track(url) {\n    const track_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const target_url = `https://api.bilibili.com/x/web-interface/view?bvid=${track_id}`;\n        axios.get(target_url).then((response) => {\n          const info = {\n            cover_img_url: response.data.data.pic,\n            title: response.data.data.title,\n            id: `bitrack_v_${track_id}`,\n            source_url: `https://www.bilibili.com/${track_id}`,\n          };\n          const author = response.data.data.owner;\n          const default_img = response.data.data.pic;\n          const tracks = response.data.data.pages.map((item) =>\n            this.bi_convert_song3(item, track_id, author, default_img)\n          );\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static bi_convert_song3(song_info, bvid, author, default_img) {\n    let imgUrl = song_info.first_frame;\n    if (imgUrl === undefined) {\n      imgUrl = default_img;\n    } else if (imgUrl.startsWith('//')) {\n      imgUrl = `https:${imgUrl}`;\n    }\n    const track = {\n      id: `bitrack_v_${bvid}-${song_info.cid}`,\n      title: this.htmlDecode(song_info.part),\n      artist: this.htmlDecode(author.name),\n      artist_id: `biartist_v_${author.mid}`,\n      source: 'bilibili',\n      source_url: `https://www.bilibili.com/${bvid}/?p=${song_info.page}`,\n      img_url: imgUrl,\n    };\n    return track;\n  }\n\n  static bi_artist(url) {\n    const artist_id = getParameterByName('list_id', url).split('_').pop();\n\n    return {\n      success: (fn) => {\n        let target_url;\n        bilibili\n          .wrap_wbi_request('https://api.bilibili.com/x/space/wbi/acc/info', {\n            mid: artist_id,\n          })\n          .then((response) => {\n            const info = {\n              cover_img_url: response.data.data.face,\n              title: response.data.data.name,\n              id: `biartist_${artist_id}`,\n              source_url: `https://space.bilibili.com/${artist_id}/#/audio`,\n            };\n            if (getParameterByName('list_id', url).split('_').length === 3) {\n              return bilibili\n                .wrap_wbi_request(\n                  'https://api.bilibili.com/x/space/wbi/arc/search',\n                  {\n                    mid: artist_id,\n                    pn: 1,\n                    ps: 25,\n                    order: 'click',\n                    index: 1,\n                  }\n                )\n                .then((res) => {\n                  const tracks = res.data.data.list.vlist.map((item) =>\n                    this.bi_convert_song2(item)\n                  );\n                  return fn({\n                    tracks,\n                    info,\n                  });\n                });\n            }\n            target_url = `https://api.bilibili.com/audio/music-service-c/web/song/upper?pn=1&ps=0&order=2&uid=${artist_id}`;\n            return axios.get(target_url).then((res) => {\n              const tracks = res.data.data.data.map((item) =>\n                this.bi_convert_song(item)\n              );\n              return fn({\n                tracks,\n                info,\n              });\n            });\n          });\n      },\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    const match = /\\/\\/www.bilibili.com\\/audio\\/am([0-9]+)/.exec(url);\n    if (match != null) {\n      const playlist_id = match[1];\n      result = {\n        type: 'playlist',\n        id: `biplaylist_${playlist_id}`,\n      };\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const trackId = track.id;\n    if (trackId.startsWith('bitrack_v_')) {\n      const sound = {};\n      let bvid = track.id.slice('bitrack_v_'.length);\n\n      const trackIdCheck = trackId.split('-');\n      if (trackIdCheck.length > 1) {\n        bvid = trackIdCheck[0].slice('bitrack_v_'.length);\n      }\n      const target_url = `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`;\n      return axios.get(target_url).then((response) => {\n        let { cid } = response.data.data.pages[0];\n        if (trackIdCheck.length > 1) {\n          [, cid] = trackIdCheck;\n        }\n        const target_url2 = `http://api.bilibili.com/x/player/playurl?fnval=16&bvid=${bvid}&cid=${cid}`;\n        axios.get(target_url2).then((response2) => {\n          if (response2.data.data.dash.audio.length > 0) {\n            const url = response2.data.data.dash.audio[0].baseUrl;\n            sound.url = url;\n            sound.platform = 'bilibili';\n            success(sound);\n          } else {\n            failure(sound);\n          }\n        });\n      });\n    }\n    const sound = {};\n    const song_id = track.id.slice('bitrack_'.length);\n    const target_url = `https://www.bilibili.com/audio/music-service-c/web/url?sid=${song_id}`;\n    return axios.get(target_url).then((response) => {\n      const { data } = response;\n      if (data.code === 0) {\n        [sound.url] = data.data.cdns;\n        sound.platform = 'bilibili';\n\n        success(sound);\n      } else {\n        failure(sound);\n      }\n    });\n  }\n\n  static search(url) {\n    return {\n      success: (fn) => {\n        const keyword = getParameterByName('keywords', url);\n        const curpage = getParameterByName('curpage', url);\n\n        const target_url = `https://api.bilibili.com/x/web-interface/search/type?__refresh__=true&_extra=&context=&page=${curpage}&page_size=42&platform=pc&highlight=1&single_column=0&keyword=${encodeURIComponent(\n          keyword\n        )}&category_id=&search_type=video&dynamic_offset=0&preload=true&com2co=true`;\n\n        const domain = `https://api.bilibili.com`;\n        const cookieName = 'buvid3';\n        const expire =\n          (new Date().getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000;\n\n        cookieSet(\n          {\n            url: domain,\n            name: cookieName,\n            value: '0',\n            expirationDate: expire,\n            sameSite: 'no_restriction',\n          },\n          () => {\n            axios\n              .get(target_url, { withCredentials: true })\n              .then((response) => {\n                const result = response.data.data.result.map((song) =>\n                  this.bi_convert_song2(song)\n                );\n                const total = response.data.data.numResults;\n                return fn({\n                  result,\n                  total,\n                });\n              });\n          }\n        );\n      },\n    };\n  }\n\n  static lyric() {\n    return {\n      success: (fn) => {\n        fn({\n          lyric: '',\n        });\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'biplaylist':\n        return this.bi_get_playlist(url);\n      case 'bialbum':\n        return this.bi_album(url);\n      case 'biartist':\n        return this.bi_artist(url);\n      case 'bitrack':\n        return this.bi_track(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => fn({ recommend: [], all: [] }),\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => fn({ status: 'fail', data: {} }),\n    };\n  }\n\n  static get_login_url() {\n    return `https://www.bilibili.com`;\n  }\n\n  static logout() {}\n\n  // return {\n  //   show_playlist: bi_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: bi_parse_url,\n  //   bootstrap_track: bi_bootstrap_track,\n  //   search: bi_search,\n  //   lyric: bi_lyric,\n  //   get_user: bi_get_user,\n  //   get_login_url: bi_get_login_url,\n  //   logout: bi_logout,\n  // };\n}\n"
  },
  {
    "path": "js/provider/kugou.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global async getParameterByName */\nclass kugou {\n  static kg_convert_song(song) {\n    const track = {\n      id: `kgtrack_${song.FileHash}`,\n      title: song.SongName,\n      artist: '',\n      artist_id: '',\n      album: song.AlbumName,\n      album_id: `kgalbum_${song.AlbumID}`,\n      source: 'kugou',\n      source_url: `https://www.kugou.com/song/#hash=${song.FileHash}&album_id=${song.AlbumID}`,\n      img_url: '',\n      // url: `kgtrack_${song.FileHash}`,\n      lyric_url: song.FileHash,\n    };\n    let singer_id = song.SingerId;\n    let singer_name = song.SingerName;\n    if (song.SingerId instanceof Array) {\n      [singer_id] = singer_id;\n      [singer_name] = singer_name.split('、');\n    }\n    track.artist = singer_name;\n    track.artist_id = `kgartist_${singer_id}`;\n    return track;\n  }\n\n  static async_process_list(\n    data_list,\n    handler,\n    handler_extra_param_list,\n    callback\n  ) {\n    const fnDict = {};\n    data_list.forEach((item, index) => {\n      fnDict[index] = (cb) =>\n        handler(index, item, handler_extra_param_list, cb);\n    });\n    async.parallel(fnDict, (err, results) =>\n      callback(\n        null,\n        data_list.map((item, index) => results[index])\n      )\n    );\n  }\n\n  static kg_render_search_result_item(index, item, params, callback) {\n    const track = kugou.kg_convert_song(item);\n    // Add singer img\n    const url = `${'https://www.kugou.com/yy/index.php?r=play/getdata&hash='}${\n      track.lyric_url\n    }`;\n    axios.get(url).then((response) => {\n      const { data } = response;\n      track.img_url = data.data.img;\n      callback(null, track);\n    });\n  }\n\n  static search(url) {\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const searchType = getParameterByName('type', url);\n    if (searchType === '1') {\n      return {\n        success: (fn) => {\n          const target_url = `${'http://mobilecdnbj.kugou.com/api/v3/search/special?keyword='}${keyword}&pagesize=20&filter=0&page=${curpage}`;\n          axios\n            .get(target_url)\n            .then((response) => {\n              const result = response.data.data.info.map((item) => ({\n                id: `kgplaylist_${item.specialid}`,\n                title: item.specialname,\n                source: 'kugou',\n                source_url:\n                  'https://www.kugou.com/yy/special/single/{size}.html'.replace(\n                    '{size}',\n                    item.specialid\n                  ),\n                img_url: item.imgurl\n                  ? item.imgurl.replace('{size}', '400')\n                  : '',\n                url: `kgplaylist_${item.specialid}`,\n                author: item.nickname,\n                count: item.songcount,\n              }));\n              const { total } = response.data.data;\n              return fn({\n                result,\n                total,\n                type: searchType,\n              });\n            })\n            .catch(() => {\n              fn({\n                result: [],\n                total: 0,\n                type: searchType,\n              });\n            });\n        },\n      };\n    }\n    return {\n      success: (fn) => {\n        const target_url = `${'https://songsearch.kugou.com/song_search_v2?keyword='}${keyword}&page=${curpage}`;\n        axios\n          .get(target_url)\n          .then((response) => {\n            const { data } = response;\n            this.async_process_list(\n              data.data.lists,\n              this.kg_render_search_result_item,\n              [],\n              (err, tracks) =>\n                fn({\n                  result: tracks,\n                  total: data.data.total,\n                  type: searchType,\n                })\n            );\n          })\n          .catch(() =>\n            fn({\n              result: [],\n              total: 0,\n              type: searchType,\n            })\n          );\n      },\n    };\n  }\n\n  static kg_render_playlist_result_item(index, item, params, callback) {\n    const { hash } = item;\n\n    let target_url = `${'https://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash='}${hash}`;\n    const track = {\n      id: `kgtrack_${hash}`,\n      title: '',\n      artist: '',\n      artist_id: '',\n      album: '',\n      album_id: `kgalbum_${item.album_id}`,\n      source: 'kugou',\n      source_url: `https://www.kugou.com/song/#hash=${hash}&album_id=${item.album_id}`,\n      img_url: '',\n      lyric_url: hash,\n    };\n    // Fix song info\n    axios.get(target_url).then((response) => {\n      const { data } = response;\n      track.title = data.songName;\n      track.artist = data.singerId === 0 ? '未知' : data.singerName;\n      track.artist_id = `kgartist_${data.singerId}`;\n      if (data.album_img !== undefined) {\n        track.img_url = data.album_img.replace('{size}', '400');\n      } else {\n        // track['img_url'] = data.imgUrl.replace('{size}', '400');\n      }\n      // Fix album\n      target_url = `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`;\n      axios.get(target_url).then((res) => {\n        const { data: res_data } = res;\n        if (\n          res_data.status &&\n          res_data.data !== undefined &&\n          res_data.data !== null\n        ) {\n          track.album = res_data.data.albumname;\n        } else {\n          track.album = '';\n        }\n        return callback(null, track);\n      });\n    });\n  }\n\n  static kg_get_playlist(url) {\n    return {\n      success: (fn) => {\n        const list_id = getParameterByName('list_id', url).split('_').pop();\n        const target_url = `https://m.kugou.com/plist/list/${list_id}?json=true`;\n\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const info = {\n            cover_img_url: data.info.list.imgurl\n              ? data.info.list.imgurl.replace('{size}', '400')\n              : '',\n            title: data.info.list.specialname,\n            id: `kgplaylist_${data.info.list.specialid}`,\n            source_url:\n              'https://www.kugou.com/yy/special/single/{size}.html'.replace(\n                '{size}',\n                data.info.list.specialid\n              ),\n          };\n\n          this.async_process_list(\n            data.list.list.info,\n            this.kg_render_playlist_result_item,\n            [],\n            (err, tracks) =>\n              fn({\n                tracks,\n                info,\n              })\n          );\n        });\n      },\n    };\n  }\n\n  static kg_render_artist_result_item(index, item, params, callback) {\n    const info = params[0];\n    const track = {\n      id: `kgtrack_${item.hash}`,\n      title: '',\n      artist: '',\n      artist_id: info.id,\n      album: '',\n      album_id: `kgalbum_${item.album_id}`,\n      source: 'kugou',\n      source_url: `https://www.kugou.com/song/#hash=${item.hash}&album_id=${item.album_id}`,\n      img_url: '',\n      // url: `kgtrack_${item.hash}`,\n      lyric_url: item.hash,\n    };\n    const one = item.filename.split('-');\n    track.title = one[1].trim();\n    track.artist = one[0].trim();\n    // Fix album name and img\n    const target_url = `${'https://www.kugou.com/yy/index.php?r=play/getdata&hash='}${\n      item.hash\n    }`;\n    axios\n      .get(\n        `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`\n      )\n      .then((response) => {\n        const { data } = response;\n        if (data.status && data.data !== undefined) {\n          track.album = data.data.albumname;\n        } else {\n          track.album = '';\n        }\n        axios.get(target_url).then((res) => {\n          track.img_url = res.data.data.img;\n          callback(null, track);\n        });\n      });\n  }\n\n  static kg_artist(url) {\n    return {\n      success: (fn) => {\n        const artist_id = getParameterByName('list_id', url).split('_').pop();\n        let target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/info?singerid=${artist_id}`;\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const info = {\n            cover_img_url: data.data.imgurl.replace('{size}', '400'),\n            title: data.data.singername,\n            id: `kgartist_${artist_id}`,\n            source_url: 'https://www.kugou.com/singer/{id}.html'.replace(\n              '{id}',\n              artist_id\n            ),\n          };\n          target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/song?singerid=${artist_id}&page=1&pagesize=30`;\n          axios.get(target_url).then((res) => {\n            this.async_process_list(\n              res.data.data.info,\n              this.kg_render_artist_result_item,\n              [info],\n              (err, tracks) =>\n                fn({\n                  tracks,\n                  info,\n                })\n            );\n          });\n        });\n      },\n    };\n  }\n\n  static getTimestampString() {\n    return new Date().getTime().toString();\n  }\n\n  static getRandomIntString() {\n    return (Math.random() * 100).toString().replace(/\\D/g, '');\n  }\n\n  static getRandomHexString() {\n    let result = '';\n    const letters = '0123456789abcdef';\n    for (let i = 0; i < 16; i += 1) {\n      result += letters[Math.floor(Math.random() * 16)];\n    }\n    return result;\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const track_id = track.id.slice('kgtrack_'.length);\n\n    const target_url = `https://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash=${track_id}`;\n\n    axios.get(target_url).then((response) => {\n      const { data: info } = response;\n\n      const { url } = info;\n\n      if (url === '') {\n        return failure({});\n      }\n\n      return success({\n        url,\n        bitrate: `${info.bitRate}kbps`,\n        platform: 'kugou',\n      });\n    });\n  }\n\n  static lyric(url) {\n    const track_id = getParameterByName('track_id', url).split('_').pop();\n    const album_id = getParameterByName('album_id', url).split('_').pop();\n    let lyric_url = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery&mid=1&hash=${track_id}&platid=4&album_id=${album_id}`;\n    const timstamp = +new Date();\n    lyric_url += `&_=${timstamp}`;\n    return {\n      success: (fn) => {\n        axios.get(lyric_url).then((response) => {\n          const { data } = response;\n          const jsonString = data.slice('jQuery('.length, data.length - 1 - 1);\n          const info = JSON.parse(jsonString);\n          return fn({\n            lyric: info.data.lyrics,\n          });\n        });\n      },\n    };\n  }\n\n  static kg_render_album_result_item(index, item, params, callback) {\n    const info = params[0];\n    const album_id = params[1];\n    const track = {\n      id: `kgtrack_${item.hash}`,\n      title: '',\n      artist: '',\n      artist_id: '',\n      album: info.title,\n      album_id: `kgalbum_${album_id}`,\n      source: 'kugou',\n      source_url: `https://www.kugou.com/song/#hash=${item.hash}&album_id=${album_id}`,\n      img_url: '',\n      // url: `xmtrack_${item.hash}`,\n      lyric_url: item.hash,\n    };\n    // Fix other data\n    const target_url = `${'https://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash='}${\n      item.hash\n    }`;\n    axios.get(target_url).then((response) => {\n      const { data } = response;\n      track.title = data.songName;\n      track.artist = data.singerId === 0 ? '未知' : data.singerName;\n      track.artist_id = `kgartist_${data.singerId}`;\n      track.img_url = data.imgUrl.replace('{size}', '400');\n      callback(null, track);\n    });\n  }\n\n  static kg_album(url) {\n    return {\n      success: (fn) => {\n        const album_id = getParameterByName('list_id', url).split('_').pop();\n        let target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/info?albumid='}${album_id}`;\n\n        let info;\n        // info\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          info = {\n            cover_img_url: data.data.imgurl.replace('{size}', '400'),\n            title: data.data.albumname,\n            id: `kgalbum_${data.data.albumid}`,\n            source_url: 'https://www.kugou.com/album/{id}.html'.replace(\n              '{id}',\n              data.data.albumid\n            ),\n          };\n\n          target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/song?albumid='}${album_id}&page=1&pagesize=-1`;\n          axios.get(target_url).then((res) => {\n            this.async_process_list(\n              res.data.data.info,\n              this.kg_render_album_result_item,\n              [info, album_id],\n              (err, tracks) =>\n                fn({\n                  tracks,\n                  info,\n                })\n            );\n          });\n        });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    let offset = getParameterByName('offset', url);\n    if (offset === undefined) {\n      offset = 0;\n    }\n    const page = offset / 30 + 1;\n    const target_url = `${'https://m.kugou.com/plist/index&json=true&page='}${page}`;\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          // const total = data.plist.total;\n          const result = data.plist.list.info.map((item) => ({\n            cover_img_url: item.imgurl\n              ? item.imgurl.replace('{size}', '400')\n              : '',\n            title: item.specialname,\n            id: `kgplaylist_${item.specialid}`,\n            source_url:\n              'https://www.kugou.com/yy/special/single/{size}.html'.replace(\n                '{size}',\n                item.specialid\n              ),\n          }));\n          return fn({\n            result,\n          });\n        });\n      },\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    const match = /\\/\\/www.kugou.com\\/yy\\/special\\/single\\/([0-9]+).html/.exec(\n      url\n    );\n    if (match != null) {\n      const playlist_id = match[1];\n      result = {\n        type: 'playlist',\n        id: `kgplaylist_${playlist_id}`,\n      };\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    // eslint-disable-line no-unused-vars\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'kgplaylist':\n        return this.kg_get_playlist(url);\n      case 'kgalbum':\n        return this.kg_album(url);\n      case 'kgartist':\n        return this.kg_artist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => fn({ recommend: [], all: [] }),\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => fn({ status: 'fail', data: {} }),\n    };\n  }\n\n  static get_login_url() {\n    return `https://www.kugou.com`;\n  }\n\n  static logout() {}\n\n  // return {\n  //   show_playlist: kg_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: kg_parse_url,\n  //   bootstrap_track: kg_bootstrap_track,\n  //   search: kg_search,\n  //   lyric: kg_lyric,\n  //   get_user: kg_get_user,\n  //   get_login_url: kg_get_login_url,\n  //   logout: kg_logout,\n  // };\n}\n"
  },
  {
    "path": "js/provider/kuwo.js",
    "content": "/* eslint-disable no-undef */\n/* eslint-disable no-unused-vars */\n/* global async getParameterByName isElectron */\n\nfunction h(t, e) {\n  // NOTICE: this function is from kuwo website, so eslint is skipped.\n  /* eslint-disable */\n  if (null == e || e.length <= 0)\n    return (\n      console.log('Please enter a password with which to encrypt the message.'),\n      null\n    );\n  for (var n = '', i = 0; i < e.length; i++) n += e.charCodeAt(i).toString();\n  var r = Math.floor(n.length / 5),\n    o = parseInt(\n      n.charAt(r) +\n        n.charAt(2 * r) +\n        n.charAt(3 * r) +\n        n.charAt(4 * r) +\n        n.charAt(5 * r)\n    ),\n    l = Math.ceil(e.length / 2),\n    c = Math.pow(2, 31) - 1;\n  if (o < 2)\n    return (\n      console.log(\n        'Algorithm cannot find a suitable hash. Please choose a different password. \\nPossible considerations are to choose a more complex or longer password.'\n      ),\n      null\n    );\n  var d = Math.round(1e9 * Math.random()) % 1e8;\n  for (n += d; n.length > 10; )\n    n = (\n      parseInt(n.substring(0, 10)) + parseInt(n.substring(10, n.length))\n    ).toString();\n  n = (o * n + l) % c;\n  var h = '',\n    f = '';\n  for (i = 0; i < t.length; i++)\n    (f +=\n      (h = parseInt(t.charCodeAt(i) ^ Math.floor((n / c) * 255))) < 16\n        ? '0' + h.toString(16)\n        : h.toString(16)),\n      (n = (o * n + l) % c);\n  for (d = d.toString(16); d.length < 8; ) d = '0' + d;\n  return (f += d);\n}\n\nclass kuwo {\n  static forgeMD5(message) {\n    const md = forge.md.sha1.create();\n    md.update(message);\n    const sig1 = md.digest().toHex();\n    const sig2 = forge.md5\n      .create()\n      .update(forge.util.encodeUtf8(sig1))\n      .digest()\n      .toHex();\n    return sig2;\n  }\n\n  // Convert html code\n  static html_decode(str) {\n    let text = str;\n    const entities = [\n      ['amp', '&'],\n      ['apos', \"'\"],\n      ['#x27', \"'\"],\n      ['#x2F', '/'],\n      ['#39', \"'\"],\n      ['#47', '/'],\n      ['lt', '<'],\n      ['gt', '>'],\n      ['nbsp', ' '],\n      ['quot', '\"'],\n    ];\n\n    for (let i = 0, max = entities.length; i < max; i += 1) {\n      text = text.replace(\n        new RegExp(`&${entities[i][0]};`, 'g'),\n        entities[i][1]\n      );\n    }\n\n    return text;\n  }\n\n  // Fix single quote in json\n  static fix_json(data) {\n    return data.replace(/(')/g, '\"');\n  }\n\n  static num2str(num) {\n    // const t = parseInt(num, 10);\n    return parseInt(num / 10, 10).toString() + (num % 10).toString();\n  }\n\n  /*\n  static kw_convert_song(item) {\n    const song_id = item.MUSICRID.split('_').pop();\n    const track = {\n      id: `kwtrack_${song_id}`,\n      title: html_decode(item.SONGNAME),\n      artist: html_decode(item.ARTIST),\n      artist_id: `kwartist_${item.ARTISTID}`,\n      album: html_decode(item.ALBUM),\n      album_id: `kwalbum_${item.ALBUMID}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${song_id}`,\n      img_url: '',\n      // url: `kwtrack_${song_id}`,\n      lyric_url: song_id,\n    };\n    return track;\n  }\n  */\n  static kw_convert_song2(item) {\n    return {\n      id: `kwtrack_${item.rid}`,\n      title: this.html_decode(item.name),\n      artist: this.html_decode(item.artist),\n      artist_id: `kwartist_${item.artistid}`,\n      album: this.html_decode(item.album),\n      album_id: `kwalbum_${item.albumid}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${item.rid}`,\n      img_url: item.pic,\n      // url: `kwtrack_${musicrid}`,\n      lyric_url: item.rid,\n    };\n  }\n  static kw_convert_song3(item) {\n    return {\n      id: `kwtrack_${item.DC_TARGETID}`,\n      title: this.html_decode(item.NAME),\n      artist: this.html_decode(item.ARTIST),\n      artist_id: `kwartist_${item.ARTISTID}`,\n      album: this.html_decode(item.ALBUM),\n      album_id: `kwalbum_${item.ALBUMID}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${item.DC_TARGETID}`,\n      img_url: `https://img2.kuwo.cn/star/albumcover/${item.web_albumpic_short}`,\n      // url: `kwtrack_${musicrid}`,\n      lyric_url: item.DC_TARGETID,\n    };\n  }\n\n  /*\n  function async_process_list(data_list, handler, handler_extra_param_list, callback) {\n    const fnDict = {};\n    data_list.forEach((item, index) => {\n      fnDict[index] = (cb) => handler(index, item, handler_extra_param_list, cb);\n    });\n    async.parallel(fnDict, (err, results) => {\n      callback(null, data_list.map((item, index) => results[index]));\n    });\n  }\n\n  function kw_add_song_pic_in_track(track, params, callback) {\n    // Add song picture image\n    const target_url = 'https://artistpicserver.kuwo.cn/pic.web?'\n      + `type=rid_pic&pictype=url&size=240&rid=${track.lyric_url}`;\n    axios.get(target_url)\n      .then((response) => {\n        const { data } = response;\n        track.img_url = data; // eslint-disable-line no-param-reassign\n        callback(null, track);\n      });\n  }\n\n  function kw_render_search_result_item(index, item, params, callback) {\n    const track = kw_convert_song(item);\n    kw_add_song_pic_in_track(track, params, callback);\n  }\n\n  function kw_render_artist_result_item(index, item, params, callback) {\n    const track = {\n      id: `kwtrack_${item.musicrid}`,\n      title: html_decode(item.name),\n      artist: item.artist,\n      artist_id: `kwartist_${item.artistid}`,\n      album: html_decode(item.album),\n      album_id: `kwalbum_${item.albumid}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${item.musicrid}`,\n      img_url: '',\n      //url: `kwtrack_${item.musicrid}`,\n      lyric_url: item.musicrid,\n    };\n\n    kw_add_song_pic_in_track(track, params, callback);\n  }\n\n  function kw_render_album_result_item(index, item, params, callback) {\n    const info = params[0];\n\n    const track = {\n      id: `kwtrack_${item.id}`,\n      title: html_decode(item.name),\n      artist: item.artist,\n      artist_id: `kwartist_${item.artistid}`,\n      album: info.title,\n      album_id: `kwalbum_${info.id}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${item.id}`,\n      img_url: '',\n      //url: `kwtrack_${item.id}`,\n      lyric_url: item.id,\n    };\n\n    kw_add_song_pic_in_track(track, params, callback);\n  }\n\n  function kw_render_playlist_result_item(index, item, params, callback) {\n    const track = {\n      id: `kwtrack_${item.id}`,\n      title: html_decode(item.name),\n      artist: item.artist,\n      artist_id: `kwartist_${item.artistid}`,\n      album: html_decode(item.album),\n      album_id: `kwalbum_${item.albumid}`,\n      source: 'kuwo',\n      source_url: `https://www.kuwo.cn/play_detail/${item.id}`,\n      img_url: '',\n      //url: `kwtrack_${item.id}`,\n      lyric_url: item.id,\n    };\n\n    kw_add_song_pic_in_track(track, params, callback);\n  }\n  */\n  static kw_get_token(callback, isRetry) {\n    let isRetryValue = true;\n    if (isRetry === undefined) {\n      isRetryValue = false;\n    } else {\n      isRetryValue = isRetry;\n    }\n    const domain = 'https://www.kuwo.cn';\n    const name = 'Hm_Iuvt_cdb524f42f23cer9b268564v7y735ewrq2324';\n\n    cookieGet(\n      {\n        url: domain,\n        name,\n      },\n      (cookie) => {\n        if (cookie == null) {\n          if (isRetryValue) {\n            return callback('');\n          }\n          return axios.get('https://www.kuwo.cn/').then(() => {\n            this.kw_get_token(callback, true);\n          });\n        }\n        return callback(cookie.value);\n      }\n    );\n  }\n\n  static kw_cookie_get(url, callback) {\n    const name = 'Hm_Iuvt_cdb524f42f23cer9b268564v7y735ewrq2324';\n    this.kw_get_token((token) => {\n      axios\n        .get(url, {\n          headers: {\n            Secret: h(token, name),\n          },\n        })\n        .then((response) => {\n          if (response.data.success === false) {\n            // token expire, refetch token and start get url\n            this.kw_get_token((token2) => {\n              axios\n                .get(url, {\n                  headers: {\n                    Secret: h(token2, name),\n                  },\n                })\n                .then((res) => {\n                  callback(res);\n                });\n            });\n          } else {\n            callback(response);\n          }\n        })\n        .catch(() => {\n          callback();\n        });\n    });\n  }\n\n  static kw_render_tracks(url, page, callback) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    const playlist_type = getParameterByName('list_id', url).split('_')[0];\n    let tracks_url = '';\n    switch (playlist_type) {\n      case 'kwplaylist':\n        // tracks_url = `https://m.kuwo.cn/newh5app/api/mobile/v1/music/playlist/${list_id}?pn=${page}&rn=1000`\n        tracks_url = `https://www.kuwo.cn/api/www/playlist/playListInfo?pid=${list_id}&pn=${page}&rn=100&httpsStatus=1`;\n        break;\n      case 'kwalbum':\n        // tracks_url = `https://m.kuwo.cn/newh5app/api/mobile/v1/music/album/${list_id}?rn=1000`\n        tracks_url = `https://www.kuwo.cn/api/www/album/albumInfo?albumId=${list_id}&pn=${page}&rn=100&httpsStatus=1`;\n        break;\n      default:\n        break;\n    }\n    // axios.get(tracks_url).then((response) => {\n    this.kw_cookie_get(tracks_url, (response) => {\n      const tracks = response.data.data.musicList.map((item) =>\n        this.kw_convert_song2(item)\n      );\n      return callback(null, tracks);\n    });\n  }\n\n  static search(url) {\n    // eslint-disable-line no-unused-vars\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const pn = parseInt(curpage) - 1;\n    const searchType = getParameterByName('type', url);\n    let api = '';\n    let target_url = '';\n    switch (searchType) {\n      case '0':\n        api = 'searchMusicBykeyWord';\n        target_url = `https://www.kuwo.cn/search/${api}?vipver=1&client=kt&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&mobi=1&issubtitle=1&show_copyright_off=1&pn=${pn}&rn=20&all=${keyword}`;\n        break;\n      case '1':\n        api = 'searchPlayListBykeyWord';\n        target_url = `https://www.kuwo.cn/api/www/search/${api}?key=${keyword}&pn=${curpage}&rn=30`;\n        break;\n      default:\n        break;\n    }\n\n    return {\n      success: (fn) => {\n        this.kw_cookie_get(target_url, (response) => {\n          let result = [];\n          let total = 0;\n          if (response === undefined) {\n            return fn({\n              result,\n              total,\n              type: searchType,\n            });\n          }\n          if (searchType === '0' && response.data.abslist !== undefined) {\n            result = response.data.abslist.map((item) =>\n              this.kw_convert_song3(item)\n            );\n            total = parseInt(response.data.HIT);\n          } else if (searchType === '1' && response.data.data !== undefined) {\n            result = response.data.data.list.map((item) => ({\n              id: `kwplaylist_${item.id}`,\n              title: this.html_decode(item.name),\n              source: 'kuwo',\n              source_url: `https://www.kuwo.cn/playlist_detail/${item.id}`,\n              img_url: item.img,\n              url: `kwplaylist_${item.id}`,\n              author: this.html_decode(item.uname),\n              count: item.total,\n            }));\n            total = response.data.data.total;\n          }\n          return fn({\n            result,\n            total,\n            type: searchType,\n          });\n        });\n      },\n    };\n  }\n\n  // eslint-disable-next-line no-unused-vars\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    const song_id = track.id.slice('kwtrack_'.length);\n\n    const target_url = `https://www.kuwo.cn/api/v1/www/music/playUrl?mid=${song_id}&type=music&httpsStatus=1&reqId=&plat=web_www&from=`;\n    this.kw_cookie_get(target_url, (response) => {\n      const { data } = response;\n\n      if (data && data.data && data.data.url) {\n        sound.url = data.data.url;\n        sound.platform = 'kuwo';\n\n        success(sound);\n      } else {\n        failure(sound);\n      }\n    });\n  }\n\n  static kw_get_lrc(arr) {\n    const lyric = arr.reduce((str, item) => {\n      const t = parseFloat(item.time);\n      const m = parseInt(t / 60, 10);\n      const s = parseInt(t - m * 60, 10);\n      const ms = parseInt((t - m * 60 - s) * 100, 10);\n      return `${str}[${this.num2str(m)}:${this.num2str(\n        parseInt(s, 10)\n      )}.${this.num2str(ms)}]${item.lineLyric}\\n`;\n    }, '');\n    return lyric;\n  }\n\n  static kw_generate_translation(lrclist) {\n    if (lrclist) {\n      lrclist.filter((e) => e && e.lineLyric !== '//');\n\n      // 暂时原歌词和翻译都在原歌词显示\n      // 酷我的歌词格式中没区分，查看了几个歌词文件发现，翻译歌词也存在和原来歌词的时间轴不一致的情况\n      // 如果按照时间区分可能造成错行问题。\n\n      // 将歌词和翻译分成两个数组，并将对应歌词和翻译的时间调整为相等，数组最后一个数据无法做判断，故传给翻译数组做后续处理\n      // const lrc_arr = [];\n      // const tlrc_arr = [];\n      // let lrc_arr = lrclist.filter(\n      //   (item, index, self) => {\n      //     if (index < self.length - 1) {\n      //       if (Number(item.time) === 0) {\n      //         return item;\n      //       }\n      //       return item.time !== self[index + 1].time;\n      //     }\n      //     return null;\n      //   },\n      // );\n      // let tlrc_arr = lrclist.filter(\n      //   (item, index, self) => {\n      //     if (index < self.length - 1 && Number(item.time) !== 0\n      //       && item.time === self[index + 1].time) {\n      //       return item.time === self[index - 1].time;\n      //     }\n      //     return (index === self.length - 1 ? item : null);\n      //   },\n      // );\n      // // tlrc_arr如只有一个即为没有翻译歌词\n      // if (tlrc_arr.length === 1) {\n      //   lrc_arr = [...lrc_arr, ...tlrc_arr];\n      //   tlrc_arr = [];\n      // } else {\n      //   tlrc_arr[tlrc_arr.length - 1].time = lrc_arr[lrc_arr.length - 1].time;\n      // }\n      return {\n        lrc: kuwo.kw_get_lrc(lrclist),\n        tlrc: kuwo.kw_get_lrc([]),\n      };\n    }\n    return {\n      lrc: '',\n      tlrc: '',\n    };\n  }\n\n  static lyric(url) {\n    // eslint-disable-line no-unused-vars\n    const track_id = getParameterByName('lyric_url', url);\n    const target_url = `https://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${track_id}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          let { data } = response;\n          data =\n            data.status === 200\n              ? this.kw_generate_translation(data.data.lrclist)\n              : {};\n          return fn({\n            lyric: data.lrc || '',\n            tlyric: data.tlrc || '',\n          });\n        });\n      },\n    };\n  }\n\n  static kw_artist(url) {\n    // eslint-disable-line no-unused-vars\n    const artist_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        let target_url = `https://www.kuwo.cn/api/www/artist/artist?artistid=${artist_id}`;\n        this.kw_cookie_get(target_url, (response) => {\n          const { data } = response.data;\n          // data = JSON.parse(fix_json(data));\n          const info = {\n            cover_img_url: data.pic300,\n            title: this.html_decode(data.name),\n            id: `kwartist_${data.id}`,\n            source_url: `https://www.kuwo.cn/singer_detail/${data.id}`,\n          };\n\n          // Get songs\n          target_url = `https://www.kuwo.cn/api/www/artist/artistMusic?artistid=${artist_id}&pn=1&rn=50`;\n          this.kw_cookie_get(target_url, (res) => {\n            const tracks = res.data.data.list.map((item) =>\n              this.kw_convert_song2(item)\n            );\n            return fn({\n              tracks,\n              info,\n            });\n          });\n          /*\n          target_url = 'https://search.kuwo.cn/r.s?stype=artist2music'\n            + '&sortby=0&alflac=1&pcmp4=1&encoding=utf8'\n            + `&artistid=${artist_id}&pn=0&rn=100`;\n          axios.get(target_url).then((response) => {\n            let { data } = response; // TODO: Check JSON Schema is correct\n            data = JSON.parse(fix_json(data));\n\n            async_process_list(data.musiclist, kw_render_artist_result_item, [],\n              (err, tracks) => fn({\n                tracks,\n                info,\n              }));\n            */\n        });\n      },\n    };\n  }\n\n  static kw_album(url) {\n    // eslint-disable-line no-unused-vars\n    const album_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const target_url =\n          'https://search.kuwo.cn/r.s?pn=0&rn=0&stype=albuminfo' +\n          `&albumid=${album_id}&alflac=1&pcmp4=1&encoding=utf8` +\n          '&vipver=MUSIC_8.7.7.0_W4';\n        axios.get(target_url).then((response) => {\n          let { data } = response;\n          data = JSON.parse(this.fix_json(data));\n\n          const info = {\n            cover_img_url: data.hts_img.replace('/120/', '/400/'),\n            title: this.html_decode(data.name),\n            id: `kwalbum_${data.albumid}`,\n            source_url: `https://www.kuwo.cn/album_detail/${data.albumid}`,\n          };\n          // Get songs\n          const total = data.songnum;\n          const page = Math.ceil(total / 100);\n          const page_array = Array.from({ length: page }, (v, k) => k + 1);\n          async.concat(\n            page_array,\n            (item, callback) => this.kw_render_tracks(url, item, callback),\n            (err, tracks) => {\n              fn({\n                tracks,\n                info,\n              });\n            }\n          );\n          /*\n          async_process_list(data.musiclist, kw_render_album_result_item, [info],\n            (err, tracks) => fn({\n              tracks,\n              info,\n            }));\n          */\n        });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    const offset = Number(getParameterByName('offset', url));\n\n    /* const id_available = {\n      1265: '经典',\n      577: '纯音乐',\n      621: '网络',\n      155: '怀旧',\n      1879: '网红',\n      220: '佛乐',\n      180: '影视',\n      578: '器乐',\n      1877: '游戏',\n      181: '二次元',\n      882: 'KTV',\n      216: '喊麦',\n      1366: '3D',\n      146: '伤感',\n      62: '放松',\n      58: '励志',\n      143: '开心',\n      137: '甜蜜',\n      139: '兴奋',\n      67: '安静',\n      66: '治愈',\n      147: '寂寞',\n      160: '四年',\n      366: '运动',\n      354: '睡前',\n      378: '跳舞',\n      1876: '学习',\n      353: '清晨',\n      359: '夜店',\n      382: '校园',\n      544: '亲热',\n      363: '咖啡店',\n      375: '旅行',\n      371: '散步',\n      386: '工作',\n      336: '婚礼',\n      637: '70后',\n      638: '80后',\n      639: '90后',\n      640: '00后',\n      268: '10后',\n      393: '流行',\n      391: '电子',\n      389: '摇滚',\n      1921: '民歌',\n      392: '民谣',\n      399: '乡村',\n      35: '欧洲',\n      37: '华语',\n    }; */\n    // const target_url = 'https://www.kuwo.cn/www/categoryNew/getPlayListInfoUnderCategory?'\n    // + `type=taglist&digest=10000&id=${37}&start=${offset}&count=50`;\n    const target_url = `https://www.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?pn=${\n      offset / 25 + 1\n    }&rn=25&order=hot&httpsStatus=1`;\n    /*\n    精选歌单:roder=最热:hot, 最新:new\n    tag歌单地址 https://www.kuwo.cn/api/pc/classify/playlist/getTagPlayList?pn=${offset / 25 + 1}&rn=25&id=37&httpsStatus=1\n    id =华语:37,\n    */\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response.data;\n          if (!data) {\n            return fn([]);\n          }\n          const result = data.data.map((item) => ({\n            cover_img_url: item.img,\n            title: item.name,\n            id: `kwplaylist_${item.id}`,\n            source_url: `https://www.kuwo.cn/playlist_detail/${item.id}`,\n          }));\n          return fn({\n            result,\n          });\n        });\n      },\n    };\n  }\n\n  static kw_get_playlist(url) {\n    // eslint-disable-line no-unused-vars\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    const target_url =\n      'https://nplserver.kuwo.cn/pl.svc?' +\n      'op=getlistinfo&pn=0&rn=0&encode=utf-8&keyset=pl2012&pcmp4=1' +\n      `&pid=${list_id}&vipver=MUSIC_9.0.2.0_W1&newver=1`;\n    // https://www.kuwo.cn/api/www/playlist/playListInfo?pid=3134372426&pn=1&rn=30\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const info = {\n            cover_img_url: data.pic.replace('_150.jpg', '_400.jpg'),\n            title: data.title,\n            id: `kwplaylist_${data.id}`,\n            source_url: `https://www.kuwo.cn/playlist_detail/${data.id}`,\n          };\n          const { total } = data;\n          const page = Math.ceil(total / 100);\n          const page_array = Array.from({ length: page }, (v, k) => k + 1);\n          async.concat(\n            page_array,\n            (item, callback) => this.kw_render_tracks(url, item, callback),\n            (err, tracks) => {\n              fn({\n                tracks,\n                info,\n              });\n            }\n          );\n          /*\n          async_process_list(data.musiclist, kw_render_playlist_result_item, [],\n            (err, tracks) => fn({\n              tracks,\n              info,\n            }));\n          */\n        });\n      },\n    };\n  }\n\n  static parse_url(myurl) {\n    let result;\n    let id;\n    let url = myurl;\n    url = url.replace(/kuwo.cn\\/(h5app|newh5(?:app){0,1})\\//, 'kuwo.cn/');\n    url = url.replace(/kuwo.cn\\/(album\\/|\\?albumid=)/, 'kuwo.cn/album_detail/');\n    url = url.replace(/kuwo.cn\\/(artist|singers)\\//, 'kuwo.cn/singer_detail/');\n    url = url.replace(/kuwo.cn\\/playlist\\//, 'kuwo.cn/playlist_detail/');\n    if (url.search('kuwo.cn/playlist_detail') !== -1) {\n      const match = /kuwo.cn\\/playlist_detail\\/([0-9]+)/.exec(url);\n      id = match ? match[1] : getParameterByName('pid', url);\n      result = {\n        type: 'playlist',\n        id: `kwplaylist_${id}`,\n      };\n    } else if (url.search('kuwo.cn/singer_detail') !== -1) {\n      const match = /kuwo.cn\\/singer_detail\\/([0-9]+)/.exec(url);\n      id = match ? match[1] : getParameterByName('id', url);\n      result = {\n        type: 'playlist',\n        id: `kwartist_${id}`,\n      };\n    } else if (url.search('kuwo.cn/album_detail') !== -1) {\n      const match = /kuwo.cn\\/album_detail\\/([0-9]+)/.exec(url);\n      if (match) {\n        // eslint-disable-next-line prefer-destructuring\n        id = match[1];\n        result = {\n          type: 'playlist',\n          id: `kwalbum_${id}`,\n        };\n      }\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'kwplaylist':\n        return this.kw_get_playlist(url);\n      case 'kwalbum':\n        return this.kw_album(url);\n      case 'kwartist':\n        return this.kw_artist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => fn({ recommend: [], all: [] }),\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => {\n        fn({ status: 'fail', data: {} });\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://www.kuwo.com`;\n  }\n\n  static logout() {}\n\n  // return {\n  //   show_playlist: kw_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: kw_parse_url,\n  //   bootstrap_track: kw_bootstrap_track,\n  //   search: kw_search,\n  //   lyric: kw_lyric,\n  //   get_user: kw_get_user,\n  //   get_login_url: kw_get_login_url,\n  //   logout: kw_logout,\n  // };\n}\n"
  },
  {
    "path": "js/provider/localmusic.js",
    "content": "/* eslint-disable no-param-reassign */\n/* eslint-disable no-unused-vars */\n/* global getParameterByName */\nconst defaultLocalMusicPlaylist = {\n  tracks: [],\n  info: {\n    id: 'lmplaylist_reserve',\n    cover_img_url: 'images/mycover.jpg',\n    title: '本地音乐',\n    source_url: '',\n  },\n};\n\nclass localmusic {\n  static show_playlist(url, hm) {\n    return {\n      success: (fn) =>\n        fn({\n          result: [],\n        }),\n    };\n  }\n\n  static lm_get_playlist(url) {\n    const list_id = getParameterByName('list_id', url);\n    return {\n      success: (fn) => {\n        let playlist = localStorage.getObject(list_id);\n\n        if (playlist === null || playlist === undefined) {\n          playlist = defaultLocalMusicPlaylist;\n        }\n        fn(playlist);\n      },\n    };\n  }\n\n  static lm_album(url) {\n    const album = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const list_id = 'lmplaylist_reserve';\n        let playlist = localStorage.getObject(list_id);\n\n        if (playlist === null || playlist === undefined) {\n          playlist = JSON.parse(JSON.stringify(defaultLocalMusicPlaylist));\n          playlist.info.title = album;\n        } else {\n          playlist.info.title = album;\n          playlist.tracks = playlist.tracks.filter((tr) => tr.album === album);\n        }\n        fn(playlist);\n      },\n    };\n  }\n\n  static lm_artist(url) {\n    const artist = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const list_id = 'lmplaylist_reserve';\n        let playlist = localStorage.getObject(list_id);\n\n        if (playlist === null || playlist === undefined) {\n          playlist = JSON.parse(JSON.stringify(defaultLocalMusicPlaylist));\n          playlist.info.title = artist;\n        } else {\n          playlist.info.title = artist;\n          playlist.tracks = playlist.tracks.filter(\n            (tr) => tr.artist === artist\n          );\n        }\n        fn(playlist);\n      },\n    };\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    sound.url = track.sound_url;\n    sound.platform = 'localmusic';\n\n    success(sound);\n  }\n\n  static lyric(url) {\n    const track_id = getParameterByName('track_id', url);\n    const playlist = localStorage.getObject('lmplaylist_reserve');\n    const track = playlist.tracks.find((item) => item.id === track_id);\n    let lyric = '';\n    if (track.lyrics !== undefined) {\n      [lyric] = track.lyrics;\n    }\n    return {\n      success: (fn) =>\n        fn({\n          lyric,\n          tlyric: '',\n        }),\n    };\n  }\n\n  static add_playlist(list_id, tracks) {\n    if (typeof tracks === 'string') {\n      tracks = JSON.parse(tracks);\n    }\n    let playlist = localStorage.getObject(list_id);\n    if (playlist === null) {\n      playlist = JSON.parse(JSON.stringify(defaultLocalMusicPlaylist));\n    }\n    const tracksIdSet = {};\n    tracks.forEach((tr) => {\n      tracksIdSet[tr.id] = true;\n    });\n    playlist.tracks = tracks.concat(\n      playlist.tracks.filter((tr) => tracksIdSet[tr.id] !== true)\n    );\n    localStorage.setObject(list_id, playlist);\n\n    return {\n      success: (fn) => fn({ list_id, playlist }),\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'lmplaylist':\n        return this.lm_get_playlist(url);\n      case 'lmartist':\n        return this.lm_artist(url);\n      case 'lmalbum':\n        return this.lm_album(url);\n      default:\n        return null;\n    }\n  }\n\n  static remove_from_playlist(list_id, track_id) {\n    const playlist = localStorage.getObject(list_id);\n    if (playlist == null) {\n      return;\n    }\n    const newtracks = playlist.tracks.filter((item) => item.id !== track_id);\n    playlist.tracks = newtracks;\n    localStorage.setObject(list_id, playlist);\n\n    // eslint-disable-next-line consistent-return\n    return {\n      success: (fn) => fn(),\n    };\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => fn({ recommend: [], all: [] }),\n    };\n  }\n\n  // return {\n  //   show_playlist: lm_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: lm_parse_url,\n  //   bootstrap_track: lm_bootstrap_track,\n  //   search: lm_search,\n  //   lyric: lm_lyric,\n  //   add_playlist: lm_add_playlist,\n  //   remove_from_playlist: lm_remove_from_playlist,\n  // };\n}\n"
  },
  {
    "path": "js/provider/migu.js",
    "content": "/* eslint-disable consistent-return */\n/* eslint-disable no-unused-vars */\n/* eslint-disable no-use-before-define */\n/* global getParameterByName cookieRemove async forge */\nclass migu {\n  static mg_convert_song(song) {\n    return {\n      id: `mgtrack_${song.copyrightId}`,\n      title: song.songName,\n      artist: song.artists ? song.artists[0].name : song.singer,\n      artist_id: `mgartist_${\n        song.artists ? song.artists[0].id : song.singerId\n      }`,\n      album: song.albumId !== 1 ? song.album : '',\n      album_id: song.albumId !== 1 ? `mgalbum_${song.albumId}` : 'mgalbum_',\n      source: 'migu',\n      source_url: `https://music.migu.cn/v3/music/song/${song.copyrightId}`,\n      img_url: song.albumImgs[1].img,\n      // url: `mgtrack_${song.copyrightId}`,\n      lyric_url: song.lrcUrl || '',\n      tlyric_url: song.trcUrl || '',\n      quality: song.toneControl,\n      url: song.copyright === 0 ? '' : undefined,\n      song_id: song.songId,\n      content_id: song.contentId,\n    };\n  }\n\n  static mg_convert_song2(song) {\n    return {\n      id: `mgtrack_${song.copyrightId}`,\n      title: song.songName,\n      artist: song.singerList ? song.singerList[0].name : song.singer,\n      artist_id: `mgartist_${\n        song.singerList ? song.singerList[0].id : song.singerId\n      }`,\n      album: song.albumId !== 1 ? song.album : '',\n      album_id: song.albumId !== 1 ? `mgalbum_${song.albumId}` : 'mgalbum_',\n      source: 'migu',\n      source_url: `https://music.migu.cn/v3/music/song/${song.copyrightId}`,\n      img_url: song.img1,\n      // url: `mgtrack_${song.copyrightId}`,\n      lyric_url: song.ext ? song.ext.lrcUrl : '',\n      tlyric_url: song.ext ? song.ext.trcUrl : '',\n      quality: song.toneControl,\n      url: song.copyright === 0 ? '' : undefined,\n      song_id: song.songId,\n      content_id: song.contentId,\n    };\n  }\n\n  static mg_render_tracks(url, page, callback) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    const playlist_type = getParameterByName('list_id', url).split('_')[0];\n    let tracks_url = '';\n    switch (playlist_type) {\n      case 'mgplaylist':\n        tracks_url = `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/user/queryMusicListSongs.do?musicListId=${list_id}&pageNo=${page}&pageSize=50`;\n        break;\n      case 'mgalbum':\n        tracks_url = `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/queryAlbumSong?albumId=${list_id}&pageNo=${page}&pageSize=50`;\n        break;\n      default:\n        break;\n    }\n    axios.get(tracks_url).then((response) => {\n      const data =\n        playlist_type === 'mgplaylist'\n          ? response.data.list\n          : response.data.data.songList;\n      const tracks = data.map((item) => this.mg_convert_song(item));\n      return callback(null, tracks);\n    });\n  }\n\n  static mg_show_toplist(offset) {\n    if (offset !== undefined && offset > 0) {\n      return {\n        success: (fn) => fn({ result: [] }),\n      };\n    }\n\n    const url =\n      'https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release?dataVersion=1616469593718&templateVersion=9';\n    return {\n      success: (fn) => {\n        axios.get(url).then((response) => {\n          const migu_board = response.data.data.contentItemList[4].itemList.map(\n            (item) => ({\n              cover_img_url: item.imageUrl,\n              title: item.displayLogId.param.rankName,\n              id: `mgtoplist_${item.displayLogId.param.rankId}`,\n              source_url: '',\n            })\n          );\n          migu_board.splice(0, 2);\n          const global_board =\n            response.data.data.contentItemList[7].itemList.map((item) => ({\n              cover_img_url: item.imageUrl,\n              title: item.displayLogId.param.rankName,\n              id: `mgtoplist_${item.displayLogId.param.rankId}`,\n              source_url: '',\n            }));\n          const chart_board = [\n            {\n              cover_img_url:\n                'https://cdnmusic.migu.cn/tycms_picture/20/02/36/20020512065402_360x360_2997.png',\n              title: '尖叫新歌榜',\n              id: 'mgtoplist_27553319',\n              source: '',\n            },\n            {\n              cover_img_url:\n                'https://cdnmusic.migu.cn/tycms_picture/20/04/99/200408163640868_360x360_6587.png',\n              title: '尖叫热歌榜',\n              id: 'mgtoplist_27186466',\n              source: '',\n            },\n            {\n              cover_img_url:\n                'https://cdnmusic.migu.cn/tycms_picture/20/04/99/200408163702795_360x360_1614.png',\n              title: '尖叫原创榜',\n              id: 'mgtoplist_27553408',\n              source: '',\n            },\n          ];\n          const result = chart_board.concat(migu_board, global_board);\n          return fn({ result });\n        });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    const offset = Number(getParameterByName('offset', url));\n    const filterId = getParameterByName('filter_id', url);\n    if (filterId === 'toplist') {\n      return this.mg_show_toplist(offset);\n    }\n    const pageSize = 30;\n    let target_url = '';\n    if (!filterId) {\n      target_url = `https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=${pageSize}&start=${\n        offset / pageSize + 1\n      }&templateVersion=5&type=1`;\n    } else {\n      target_url = `https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/musiclistplaza-listbytag?pageNumber=${\n        offset / pageSize + 1\n      }&tagId=${filterId}&templateVersion=1`;\n      // const target_url = `https://m.music.migu.cn/migu/remoting/playlist_bycolumnid_tag?playListType=2&type=1&columnId=15127315&tagId=&startIndex=${offset}`;\n      // columnId=15127315为推荐，15127272为最新\n    }\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const data = !filterId\n            ? response.data.data.contentItemList[0].itemList\n            : response.data.data.contentItemList.itemList;\n          const result = data.map((item) => {\n            const match = /id=([0-9]+)&/.exec(item.actionUrl);\n            const id = match ? match[1] : '';\n            return {\n              cover_img_url: item.imageUrl,\n              title: item.title,\n              id: `mgplaylist_${id}`,\n              source_url: `https://music.migu.cn/v3/music/playlist/${id}`,\n            };\n          });\n          fn({ result });\n        });\n      },\n    };\n  }\n\n  static mg_toplist(url) {\n    const list_id = Number(getParameterByName('list_id', url).split('_').pop());\n    return {\n      success: (fn) => {\n        const board_list = {\n          27553319: {\n            name: '尖叫新歌榜',\n            url: 'jianjiao_newsong',\n            img: '/20/02/36/20020512065402_360x360_2997.png',\n          },\n          27186466: {\n            name: '尖叫热歌榜',\n            url: 'jianjiao_hotsong',\n            img: '/20/04/99/200408163640868_360x360_6587.png',\n          },\n          27553408: {\n            name: '尖叫原创榜',\n            url: 'jianjiao_original',\n            img: '/20/04/99/200408163702795_360x360_1614.png',\n          },\n          1: {\n            name: '音乐榜',\n            url: 'migumusic',\n            img: '/20/05/136/200515161733982_360x360_1523.png',\n          },\n          2: {\n            name: '影视榜',\n            url: 'movies',\n            img: '/20/05/136/200515161848938_360x360_673.png',\n          },\n          23189399: {\n            name: '内地榜',\n            url: 'mainland',\n            img: '/20/08/231/200818095104122_327x327_4971.png',\n          },\n          23189800: {\n            name: '港台榜',\n            url: 'hktw',\n            img: '/20/08/231/200818095125191_327x327_2382.png',\n          },\n          19190036: {\n            name: '欧美榜',\n            url: 'eur_usa',\n            img: '/20/08/231/200818095229556_327x327_1383.png',\n          },\n          23189813: {\n            name: '日韩榜',\n            url: 'jpn_kor',\n            img: '/20/08/231/200818095259569_327x327_4628.png',\n          },\n          23190126: {\n            name: '彩铃榜',\n            url: 'coloring',\n            img: '/20/08/231/200818095356693_327x327_7955.png',\n          },\n          15140045: {\n            name: 'KTV榜',\n            url: 'ktv',\n            img: '/20/08/231/200818095414420_327x327_4992.png',\n          },\n          15140034: {\n            name: '网络榜',\n            url: 'network',\n            img: '/20/08/231/200818095442606_327x327_1298.png',\n          },\n          23218151: {\n            name: '新专辑榜',\n            url: 'newalbum',\n            img: '/20/08/231/200818095603246_327x327_7480.png',\n          },\n          33683712: {\n            name: '数字专辑畅销榜',\n            url: '',\n            img: 'https://d.musicapp.migu.cn/prod/file-service/file-down/bcb5ddaf77828caee4eddc172edaa105/2297b53efa678bbc8a5b83064622c4c8/ebfe5bff9fd9981b5ae1c043f743bfb3',\n          },\n          23217754: {\n            name: 'MV榜',\n            url: 'mv',\n            img: '/20/08/231/200818095656365_327x327_8344.png',\n          },\n          21958042: {\n            name: '美国iTunes榜',\n            url: 'itunes',\n            img: '/20/08/231/200818095755771_327x327_9250.png',\n          },\n          21975570: {\n            name: '美国billboard榜',\n            url: 'billboard',\n            img: '/20/08/231/20081809581365_327x327_4636.png',\n          },\n          22272815: {\n            name: 'Hito中文榜',\n            url: 'hito',\n            img: '/20/08/231/200818095834912_327x327_5042.png',\n          },\n          22272943: {\n            name: '韩国Melon榜',\n            url: 'mnet',\n            img: '/20/08/231/200818095926828_327x327_3277.png',\n          },\n          22273437: {\n            name: '英国UK榜',\n            url: 'uk',\n            img: '/20/08/231/200818095950791_327x327_8293.png',\n          },\n        };\n        const target_url = `https://music.migu.cn/v3/music/top/${board_list[list_id].url}`;\n\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const info = {\n            id: `mgtoplist_${list_id}`,\n            cover_img_url:\n              list_id === 33683712\n                ? board_list[list_id].img\n                : `https://cdnmusic.migu.cn/tycms_picture${board_list[list_id].img}`,\n            title: data.data\n              ? data.data.columnInfo.title\n              : board_list[list_id].name,\n            source_url: `https://music.migu.cn/v3/music/top/${board_list[list_id].url}`,\n          };\n          let tracks = [];\n          const list_elements = new DOMParser()\n            .parseFromString(data, 'text/html')\n            .getElementsByTagName('script');\n          const result = JSON.parse(\n            list_elements[1].innerText.split('=').slice(1).join('=')\n          );\n          if (result.songs && result.songs.items) {\n            tracks = result.songs.items.map((song) => {\n              let songAlbum = '';\n              let songAlbumId = 'mgalbum_';\n              if (song.album && song.album.albumId !== 1) {\n                songAlbum = song.album.albumName;\n                songAlbumId = `mgalbum_${song.album.albumId}`;\n              }\n              const track = {\n                id: `mgtrack_${song.copyrightId}`,\n                title: song.name,\n                artist: song.singers[0].name,\n                artist_id: `mgartist_${song.singers[0].id}`,\n                album: songAlbum,\n                album_id: songAlbumId,\n                source: 'migu',\n                source_url: `https://music.migu.cn/v3/music/song/${song.copyrightId}`,\n                img_url: `https:${song.mediumPic}`,\n                // url: `mgtrack_${song.copyrightId}`,\n                lyric_url: 'null',\n                tlyric_url: '',\n                song_id: song.id,\n                url: undefined,\n              };\n              if (song.bit24) {\n                track.quality = '111111';\n              } else if (song.sq) {\n                track.quality = '111100';\n              } else {\n                track.quality = '110000';\n              }\n              return track;\n            });\n          }\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static mg_get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const info_url = `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?needSimple=00&resourceType=2021&resourceId=${list_id}`;\n        axios.get(info_url).then((response) => {\n          const info = {\n            id: `mgplaylist_${list_id}`,\n            cover_img_url: response.data.resource[0].imgItem.img,\n            title: response.data.resource[0].title,\n            source_url: `https://music.migu.cn/v3/music/playlist/${list_id}`,\n          };\n          const total = response.data.resource[0].musicNum;\n          const page = Math.ceil(total / 50);\n          const page_array = Array.from({ length: page }, (v, k) => k + 1);\n          async.concat(\n            page_array,\n            (item, callback) => this.mg_render_tracks(url, item, callback),\n            (err, tracks) => {\n              fn({\n                tracks,\n                info,\n              });\n            }\n          );\n        });\n      },\n    };\n  }\n\n  static mg_album(url) {\n    const album_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) => {\n        const info_url = `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?needSimple=00&resourceType=2003&resourceId=${album_id}`;\n        axios.get(info_url).then((response) => {\n          const { data } = response;\n          const info = {\n            id: `mgalbum_${album_id}`,\n            cover_img_url: data.resource[0].imgItems[1].img,\n            title: data.resource[0].title,\n            source_url: `https://music.migu.cn/v3/music/album/${album_id}`,\n          };\n          const total = data.resource[0].totalCount;\n          const page = Math.ceil(total / 50);\n          const page_array = Array.from({ length: page }, (v, k) => k + 1);\n          async.concat(\n            page_array,\n            (item, callback) => this.mg_render_tracks(url, item, callback),\n            (err, tracks) => {\n              fn({\n                tracks,\n                info,\n              });\n            }\n          );\n        });\n      },\n    };\n  }\n\n  static mg_artist(url) {\n    const artist_id = getParameterByName('list_id', url).split('_').pop();\n    const offset = Number(getParameterByName('offset', url));\n    const pageSize = 50;\n    const page = offset / pageSize + 1;\n    const target_url = `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/singer_songs.do?pageNo=${page}&pageSize=${pageSize}&resourceType=2&singerId=${artist_id}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const info = {\n            id: `mgartist_${artist_id}`,\n            cover_img_url: data.singer.imgs[1].img,\n            title: data.singer.singer,\n            source_url: `https://music.migu.cn/v3/music/artist/${artist_id}/song`,\n          };\n\n          const tracks = data.songlist.map((item) =>\n            this.mg_convert_song(item)\n          );\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    const contentId = track.content_id;\n    const copyrightId = track.id.slice('mgtrack_'.length);\n    /*\n    const copyrightId = track.id.slice('mgtrack_'.length);\n    const type = 1;\n    // NOTICE：howler flac support is not ready for production.\n    // Sometimes network keep pending forever and block later music.\n    // So use normal quality.\n\n    // switch (track.quality) {\n    //   case '110000':\n    //     type = 2;\n    //     break;\n    //   case '111100':\n    //     type = 3;\n    //     break;\n    //   case '111111':\n    //     type = 4;\n    //     break;\n    //   default:\n    //     type = 1;\n    // }\n    const k =\n      '4ea5c508a6566e76240543f8feb06fd457777be39549c4016436afda65d2330e';\n    // type parameter for music quality: 1: normal, 2: hq, 3: sq, 4: zq, 5: z3d\n    const plain = forge.util.createBuffer(\n      `{\"copyrightId\":\"${copyrightId}\",\"type\":${type},\"auditionsFlag\":0}`\n    );\n    const salt = forge.random.getBytesSync(8);\n    const derivedBytes = forge.pbe.opensslDeriveBytes(k, salt, 48);\n    const buffer = forge.util.createBuffer(derivedBytes);\n    const key = buffer.getBytes(32);\n    const iv = buffer.getBytes(16);\n\n    const cipher = forge.cipher.createCipher('AES-CBC', key);\n    cipher.start({ iv });\n    cipher.update(plain);\n    cipher.finish();\n    const output = forge.util.createBuffer();\n    output.putBytes('Salted__');\n    output.putBytes(salt);\n    output.putBuffer(cipher.output);\n    const aesResult = forge.util.encode64(output.bytes());\n\n    const publicKey =\n      '-----BEGIN PUBLIC KEY-----\\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8asrfSaoOb4je+DSmKdriQJKW\\nVJ2oDZrs3wi5W67m3LwTB9QVR+cE3XWU21Nx+YBxS0yun8wDcjgQvYt625ZCcgin\\n2ro/eOkNyUOTBIbuj9CvMnhUYiR61lC1f1IGbrSYYimqBVSjpifVufxtx/I3exRe\\nZosTByYp4Xwpb1+WAQIDAQAB\\n-----END PUBLIC KEY-----';\n    const secKey = forge.util.encode64(\n      forge.pki.publicKeyFromPem(publicKey).encrypt(k)\n    );\n\n    const target_url = `https://music.migu.cn/v3/api/music/audioPlayer/getPlayInfo?dataType=2&data=${encodeURIComponent(\n      aesResult\n    )}&secKey=${encodeURIComponent(secKey)}`;\n    */\n    let toneFlag;\n    switch (track.quality) {\n      case '110000':\n        toneFlag = 'HQ';\n        break;\n      case '111100':\n        toneFlag = 'SQ';\n        break;\n      case '111111':\n        toneFlag = 'ZQ';\n        break;\n      default:\n        toneFlag = 'PQ';\n    }\n    // const target_url = `https://app.c.nf.migu.cn/MIGUM2.0/strategy/listen-url/v2.2?netType=01&resourceType=E&songId=${songId}&toneFlag=${toneFlag}`;\n    const target_url = `https://app.c.nf.migu.cn/MIGUM3.0/strategy/pc/listen/v1.0?scene=&netType=01&resourceType=2&copyrightId=${copyrightId}&contentId=${contentId}&toneFlag=${toneFlag}`;\n    axios\n      .get(target_url, {\n        headers: {\n          channel: '0146951',\n          uid: 1234,\n        },\n      })\n      .then((response) => {\n        // const { data } = response.data;\n        // let playUrl = response.data.data ? response.data.data.playUrl : null;\n        let playUrl = response.data.data ? response.data.data.url : null;\n        if (playUrl) {\n          if (playUrl.startsWith('//')) {\n            playUrl = `https:${playUrl}`;\n          }\n          sound.url = playUrl.replace(/\\+/g, '%2B'); // eslint-disable-line no-param-reassign\n          sound.platform = 'migu';\n          switch (toneFlag) {\n            case 'HQ':\n              sound.bitrate = '320kbps';\n              break;\n            case 'SQ':\n              sound.bitrate = '999kbps';\n              break;\n            case 'ZQ':\n              sound.bitrate = '999kbps';\n              break;\n            default:\n              sound.bitrate = '128kbps';\n          }\n          success(sound);\n        } else {\n          failure(sound);\n        }\n      });\n  }\n\n  static search(url) {\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const searchType = getParameterByName('type', url);\n    // const sid = (this.uuid() + this.uuid()).replace(/-/g, '');\n    // // let type ='';\n    // let searchSwitch = '';\n    // let target_url =\n    //   'https://jadeite.migu.cn/music_search/v2/search/searchAll?';\n    // switch (searchType) {\n    //   case '0':\n    //     searchSwitch = '{\"song\":1}'; // {\"song\":1,\"album\":0,\"singer\":0,\"tagSong\":1,\"mvSong\":0,\"bestShow\":1,\"songlist\":0,\"lyricSong\":0}\n    //     // type = 2;\n    //     target_url =\n    //       `${target_url}sid=${sid}&isCorrect=1&isCopyright=1` +\n    //       `&searchSwitch=${encodeURIComponent(searchSwitch)}&pageSize=20` +\n    //       `&text=${encodeURIComponent(keyword)}&pageNo=${curpage}` +\n    //       '&feature=1000000000&sort=1';\n    //     break;\n    //   case '1':\n    //     searchSwitch = '{\"songlist\":1}';\n    //     // type = 6;\n    //     target_url =\n    //       `${target_url}sid=${sid}&isCorrect=1&isCopyright=1` +\n    //       `&searchSwitch=${encodeURIComponent(searchSwitch)}` +\n    //       '&userFilter=%7B%22songlisttag%22%3A%5B%5D%7D&pageSize=20' +\n    //       `&text=${encodeURIComponent(keyword)}&pageNo=${curpage}` +\n    //       // + `&sort=1&userSort=%7B%22songlist%22%3A%22default%22%7D`;\n    //       '&feature=0000000010&sort=1';\n    //     break;\n    //   default:\n    //     break;\n    // }\n    // const target_url = `https://pd.musicapp.migu.cn/MIGUM3.0/v1.0/content/search_all.do?&isCopyright=0&isCorrect=0&text=${keyword}&pageNo=${curpage}&searchSwitch=${searchSwitch}`;\n    // const target_url = `https://m.music.migu.cn/migu/remoting/scr_search_tag?rows=20&type=${type}&keyword=${keyword}'&pgc=${curpage}`;\n    let target_url = `https://app.u.nf.migu.cn/pc/resource/song/item/search/v1.0?text=${keyword}&pageNo=${curpage}&pageSize=20`;\n    if (searchType === '1') {\n      target_url = `https://app.u.nf.migu.cn/pc/v1.0/content/search_all.do?text=${keyword}&pageNo=${curpage}&pageSize=20&searchSwitch={%22songlist%22:+1}`;\n    }\n    // const deviceId = forge.md5\n    //   .create()\n    //   .update(this.uuid().replace(/-/g, ''))\n    //   .digest()\n    //   .toHex()\n    //   .toLocaleUpperCase(); // 设备的UUID\n    // const timestamp = new Date().getTime();\n    // const signature_md5 = '6cdc72a439cef99a3418d2a78aa28c73'; // app签名证书的md5\n    // const text = `${\n    //   keyword + signature_md5\n    // }yyapp2d16148780a1dcc7408e06336b98cfd50${deviceId}${timestamp}`;\n    // const sign = forge.md5\n    //   .create(text)\n    //   .update(forge.util.encodeUtf8(text))\n    //   .digest()\n    //   .toHex();\n    // const headers = {\n    //   // android_id: 'db2cd8c4cdc1345f',\n    //   appId: 'yyapp2',\n    //   // brand: 'google',\n    //   // channel: '0147151',\n    //   deviceId,\n    //   // HWID: '',\n    //   // IMEI: '',\n    //   // IMSI: '',\n    //   // ip: '192.168.1.101',\n    //   // mac: '02:00:00:00:00:00',\n    //   // 'mgm-Network-standard': '01',\n    //   // 'mgm-Network-type': '04',\n    //   // mode: 'android',\n    //   // msisdn: '',\n    //   // OAID: '',\n    //   // os: 'android 7.0',\n    //   // osVersion: 'android 7.0',\n    //   // platform: 'G011C',\n    //   sign,\n    //   timestamp,\n    //   // ua: 'Android_migu',\n    //   // uid: '',\n    //   uiVersion: 'A_music_3.3.0',\n    //   version: '7.0.4',\n    // };\n    return {\n      success: (fn) => {\n        axios\n          .get(target_url, {\n            // headers,\n          })\n          .then((response) => {\n            const { data } = response;\n            let result = [];\n            let total = 0;\n            if (searchType === '0') {\n              if (data) {\n                result = data.map((item) => this.mg_convert_song2(item));\n                // no total available\n                total = 1000;\n              }\n            } else if (searchType === '1') {\n              if (data.songListResultData.result) {\n                result = data.songListResultData.result.map((item) => ({\n                  // result = data.songLists.map(item => ({\n                  id: `mgplaylist_${item.id}`,\n                  title: item.name,\n                  source: 'migu',\n                  source_url: `https://music.migu.cn/v3/music/playlist/${item.id}`,\n                  // img_url: item.img,\n                  img_url: item.imgItems[0].img,\n                  url: `mgplaylist_${item.id}`,\n                  author: item.userName,\n                  count: item.musicNum,\n                }));\n                total = data.songListResultData.totalCount;\n              }\n            }\n            return fn({\n              result,\n              total,\n              type: searchType,\n            });\n          });\n      },\n    };\n  }\n\n  // https://abhishekdutta.org/blog/standalone_uuid_generator_in_javascript.html\n  static uuid() {\n    const temp_url = URL.createObjectURL(new Blob());\n    const strTemp = temp_url.toString();\n    URL.revokeObjectURL(temp_url);\n    return strTemp.substr(strTemp.lastIndexOf('/') + 1); // remove prefix (e.g. blob:null/, blob:www.test.com/, ...)\n  }\n\n  static lyric(url) {\n    const lyric_url = getParameterByName('lyric_url', url);\n    const tlyric_url = getParameterByName('tlyric_url', url);\n    return {\n      success: (fn) => {\n        if (lyric_url !== 'null') {\n          async.parallel(\n            [\n              (callback) => {\n                if (lyric_url) {\n                  axios\n                    .get(lyric_url)\n                    .then((response) => callback(null, response.data));\n                } else {\n                  return callback(null, '[00:00.00]暂无歌词\\r\\n[00:02.00]\\r\\n');\n                }\n              },\n              (callback) => {\n                if (tlyric_url) {\n                  axios\n                    .get(tlyric_url)\n                    .then((response) => callback(null, response.data));\n                } else {\n                  return callback(null, '');\n                }\n              },\n            ],\n            (err, results) => {\n              const data = this.mg_generate_translation(results[0], results[1]);\n              return fn({\n                lyric: data.lrc,\n                tlyric: data.tlrc,\n              });\n            }\n          );\n        } else {\n          const song_id = getParameterByName('track_id', url).split('_').pop();\n          const target_url = `https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${song_id}`;\n          axios.get(target_url).then((response) => {\n            const data = this.mg_generate_translation(\n              response.data.lyric,\n              response.data.translatedLyric\n            );\n            return fn({\n              lyric: data.lrc,\n              tlyric: data.tlrc,\n            });\n          });\n        }\n      },\n    };\n  }\n\n  static mg_generate_translation(plain, translation) {\n    if (!translation) {\n      return {\n        lrc: plain,\n        tlrc: '',\n      };\n    }\n    const arr_plain = plain.split('\\n');\n    let arr_translation = translation.split('\\n');\n    // 歌词和翻译顶部信息不一定都有，会导致行列对不齐，所以删掉\n    const reg_head = /\\[(ti|ar|al|by|offset|kana|high):/;\n    let plain_head_line = 0;\n    let trans_head_line = 0;\n    for (let i = 0; i < 7; i += 1) {\n      if (reg_head.test(arr_plain[i])) {\n        plain_head_line += 1;\n      }\n      if (reg_head.test(arr_translation[i])) {\n        trans_head_line += 1;\n      }\n    }\n    arr_plain.splice(0, plain_head_line);\n    arr_translation.splice(0, trans_head_line);\n    // 删除翻译与原歌词重复的歌曲名，歌手、作曲、作词等信息\n    const reg_info =\n      /(\\u4f5c|\\u7f16)(\\u8bcd|\\u66f2)|\\u6b4c(\\u624b|\\u66f2)\\u540d|Written by/;\n    let trans_info_line = 0;\n    for (let i = 0; i < 6; i += 1) {\n      if (reg_info.test(arr_translation[i])) {\n        trans_info_line += 1;\n      }\n    }\n    arr_translation = arr_translation.splice(trans_info_line);\n    const tlrc = arr_translation.join('\\r\\n');\n    return {\n      lrc: plain,\n      tlrc,\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    // eslint-disable-next-line no-param-reassign\n    url = url.replace(\n      'music.migu.cn/v3/my/playlist/',\n      'music.migu.cn/v3/music/playlist/'\n    );\n    const regex = /\\/\\/music.migu.cn\\/v3\\/music\\/playlist\\/([0-9]+)/g;\n    const regex_result = regex.exec(url);\n    if (regex_result) {\n      result = {\n        type: 'playlist',\n        id: `mgplaylist_${regex_result[1]}`,\n      };\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'mgplaylist':\n        return this.mg_get_playlist(url);\n      case 'mgalbum':\n        return this.mg_album(url);\n      case 'mgartist':\n        return this.mg_artist(url);\n      case 'mgtoplist':\n        return this.mg_toplist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => {\n        let target_url =\n          'https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/musiclistplaza-hottaglist/release';\n        axios.get(target_url).then((response) => {\n          const recommend = response.data.data.contentItemList.map((item) => ({\n            id: item.tagId,\n            name: item.tagName,\n          }));\n          recommend.unshift(\n            { id: '', name: '推荐' },\n            { id: 'toplist', name: '排行榜' }\n          );\n          target_url =\n            'https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/musiclistplaza-taglist/release?templateVersion=1';\n          axios.get(target_url).then((res) => {\n            const all = res.data.data.map((cate) => {\n              const result = { category: cate.header.title };\n              result.filters = cate.content.map((item) => ({\n                id: item.texts[1],\n                name: item.texts[0],\n              }));\n              return result;\n            });\n            return fn({\n              recommend,\n              all,\n            });\n          });\n        });\n      },\n    };\n  }\n\n  static get_user() {\n    const ts = +new Date();\n    const url = `https://music.migu.cn/v3/api/user/getUserInfo?_=${ts}`;\n    return {\n      success: (fn) => {\n        axios.get(url).then((res) => {\n          let result = { is_login: false };\n          let status = 'fail';\n\n          if (res.data.success) {\n            status = 'success';\n            const { data } = res;\n            result = {\n              is_login: true,\n              user_id: data.user.uid,\n              user_name: data.user.mobile,\n              nickname: data.user.nickname,\n              avatar: data.user.avatar.midAvatar,\n              platform: 'migu',\n              data,\n            };\n          }\n\n          return fn({\n            status,\n            data: result,\n          });\n        });\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://music.migu.cn`;\n  }\n\n  static logout() {\n    const removeFn = (url, name) =>\n      cookieRemove(\n        {\n          url,\n          name,\n        },\n        () => {}\n      );\n    const musicCookieList = [\n      'migu_music_sid',\n      'migu_music_platinum',\n      'migu_music_level',\n      'migu_music_nickname',\n      'migu_music_avatar',\n      'migu_music_uid',\n      'migu_music_credit_level',\n      'migu_music_passid',\n      'migu_music_email',\n      'migu_music_msisdn',\n      'migu_music_status',\n    ];\n    const passportCookieList = ['USessionID', 'LTToken'];\n    musicCookieList.map((name) => removeFn('https://music.migu.cn', name));\n    passportCookieList.map((name) =>\n      removeFn('https://passport.migu.cn', name)\n    );\n  }\n\n  // return {\n  //   show_playlist: mg_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: mg_parse_url,\n  //   bootstrap_track: mg_bootstrap_track,\n  //   search: mg_search,\n  //   lyric: mg_lyric,\n  //   get_user: migu_get_user,\n  //   get_login_url: migu_get_login_url,\n  //   logout: mg_logout,\n  // };\n}\n"
  },
  {
    "path": "js/provider/netease.js",
    "content": "/* eslint-disable no-underscore-dangle */\n/* eslint-disable no-unused-vars */\n/* global getParameterByName forge */\n/* global isElectron cookieSet cookieGet cookieRemove async */\nclass netease {\n  static _create_secret_key(size) {\n    const result = [];\n    const choice = '012345679abcdef'.split('');\n    for (let i = 0; i < size; i += 1) {\n      const index = Math.floor(Math.random() * choice.length);\n      result.push(choice[index]);\n    }\n    return result.join('');\n  }\n\n  static _aes_encrypt(text, sec_key, algo) {\n    const cipher = forge.cipher.createCipher(algo, sec_key);\n    cipher.start({ iv: '0102030405060708' });\n    cipher.update(forge.util.createBuffer(text));\n    cipher.finish();\n\n    return cipher.output;\n  }\n\n  static _rsa_encrypt(text, pubKey, modulus) {\n    text = text.split('').reverse().join(''); // eslint-disable-line no-param-reassign\n    const n = new forge.jsbn.BigInteger(modulus, 16);\n    const e = new forge.jsbn.BigInteger(pubKey, 16);\n    const b = new forge.jsbn.BigInteger(forge.util.bytesToHex(text), 16);\n    const enc = b.modPow(e, n).toString(16).padStart(256, '0');\n    return enc;\n  }\n\n  static weapi(text) {\n    const modulus =\n      '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b72' +\n      '5152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbd' +\n      'a92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe48' +\n      '75d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';\n    const nonce = '0CoJUm6Qyw8W8jud';\n    const pubKey = '010001';\n    text = JSON.stringify(text); // eslint-disable-line no-param-reassign\n    const sec_key = this._create_secret_key(16);\n    const enc_text = btoa(\n      this._aes_encrypt(\n        btoa(this._aes_encrypt(text, nonce, 'AES-CBC').data),\n        sec_key,\n        'AES-CBC'\n      ).data\n    );\n    const enc_sec_key = this._rsa_encrypt(sec_key, pubKey, modulus);\n    const data = {\n      params: enc_text,\n      encSecKey: enc_sec_key,\n    };\n\n    return data;\n  }\n\n  // refer to https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/util/crypto.js\n  static eapi(url, object) {\n    const eapiKey = 'e82ckenh8dichen8';\n\n    const text = typeof object === 'object' ? JSON.stringify(object) : object;\n    const message = `nobody${url}use${text}md5forencrypt`;\n    const digest = forge.md5\n      .create()\n      .update(forge.util.encodeUtf8(message))\n      .digest()\n      .toHex();\n    const data = `${url}-36cd479b6b5-${text}-36cd479b6b5-${digest}`;\n\n    return {\n      params: this._aes_encrypt(data, eapiKey, 'AES-ECB').toHex().toUpperCase(),\n    };\n  }\n\n  static ne_show_toplist(offset) {\n    if (offset !== undefined && offset > 0) {\n      return {\n        success: (fn) => fn({ result: [] }),\n      };\n    }\n    const url = 'https://music.163.com/weapi/toplist/detail';\n    const data = this.weapi({});\n    return {\n      success: (fn) => {\n        axios.post(url, new URLSearchParams(data)).then((response) => {\n          const result = [];\n          response.data.list.forEach((item) => {\n            const playlist = {\n              cover_img_url: item.coverImgUrl,\n              id: `neplaylist_${item.id}`,\n              source_url: `https://music.163.com/#/playlist?id=${item.id}`,\n              title: item.name,\n            };\n            result.push(playlist);\n          });\n          return fn({ result });\n        });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    const order = 'hot';\n    const offset = getParameterByName('offset', url);\n    const filterId = getParameterByName('filter_id', url);\n\n    if (filterId === 'toplist') {\n      return this.ne_show_toplist(offset);\n    }\n\n    let filter = '';\n    if (filterId !== '') {\n      filter = `&cat=${filterId}`;\n    }\n    let target_url = '';\n    if (offset != null) {\n      target_url = `https://music.163.com/discover/playlist/?order=${order}${filter}&limit=35&offset=${offset}`;\n    } else {\n      target_url = `https://music.163.com/discover/playlist/?order=${order}${filter}`;\n    }\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const list_elements = Array.from(\n            new DOMParser()\n              .parseFromString(data, 'text/html')\n              .getElementsByClassName('m-cvrlst')[0].children\n          );\n          const result = list_elements.map((item) => ({\n            cover_img_url: item\n              .getElementsByTagName('img')[0]\n              .src.replace('140y140', '512y512'),\n\n            title: item\n              .getElementsByTagName('div')[0]\n              .getElementsByTagName('a')[0].title,\n            id: `neplaylist_${getParameterByName(\n              'id',\n              item.getElementsByTagName('div')[0].getElementsByTagName('a')[0]\n                .href\n            )}`,\n            source_url: `https://music.163.com/#/playlist?id=${getParameterByName(\n              'id',\n              item.getElementsByTagName('div')[0].getElementsByTagName('a')[0]\n                .href\n            )}`,\n          }));\n          return fn({\n            result,\n          });\n        });\n      },\n    };\n  }\n\n  static ne_ensure_cookie(callback) {\n    const domain = 'https://music.163.com';\n    const nuidName = '_ntes_nuid';\n    const nnidName = '_ntes_nnid3';\n    const nuidValue = this._create_secret_key(32);\n    const nnidValue = `${nuidValue},${new Date().getTime()}`;\n    const nmtidName = 'NMTID';\n    const nmtidValue = '0';\n\n    // netease default cookie expire time: 100 years\n    const expire =\n      (new Date().getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000;\n    async.concat(\n      [\n        { url: domain, name: nuidName },\n        { url: domain, name: nnidName },\n        { url: domain, name: nmtidName },\n      ],\n      (item, cb) => {\n        cookieGet(item, (result) => cb(null, result));\n      },\n      (_, results) => {\n        if (results.filter((i) => i === null).length > 0) {\n          async.concat(\n            [\n              {\n                url: domain,\n                name: nuidName,\n                value: nuidValue,\n                expirationDate: expire,\n                sameSite: 'no_restriction',\n              },\n              {\n                url: domain,\n                name: nnidName,\n                value: nnidValue,\n                expirationDate: expire,\n                sameSite: 'no_restriction',\n              },\n              {\n                url: domain,\n                name: nmtidName,\n                value: nmtidValue,\n                expirationDate: expire,\n                sameSite: 'no_restriction',\n              },\n            ],\n            cookieSet,\n            () => {\n              callback(null);\n            }\n          );\n        }\n      }\n    );\n  }\n\n  static async_process_list(\n    data_list,\n    handler,\n    handler_extra_param_list,\n    callback\n  ) {\n    const fnDict = {};\n    data_list.forEach((item, index) => {\n      fnDict[index] = (cb) =>\n        handler(index, item, handler_extra_param_list, cb);\n    });\n    async.parallel(fnDict, (err, results) =>\n      callback(\n        null,\n        data_list.map((item, index) => results[index])\n      )\n    );\n  }\n\n  static ng_render_playlist_result_item(index, item, callback) {\n    const target_url = 'https://music.163.com/weapi/v3/song/detail';\n    const queryIds = [item.id];\n    const d = {\n      c: `[${queryIds.map((id) => `{\"id\":${id}}`).join(',')}]`,\n      ids: `[${queryIds.join(',')}]`,\n    };\n    const data = this.weapi(d);\n    axios\n      .post(target_url, new URLSearchParams(data).toString())\n      .then((response) => {\n        const track_json = response.data.songs[0];\n        const track = {\n          id: `netrack_${track_json.id}`,\n          title: track_json.name,\n          artist: track_json.ar[0].name,\n          artist_id: `neartist_${track_json.ar[0].id}`,\n          album: track_json.al.name,\n          album_id: `nealbum_${track_json.al.id}`,\n          source: 'netease',\n          source_url: `https://music.163.com/#/song?id=${track_json.id}`,\n          img_url: track_json.al.picUrl,\n          // url: `netrack_${track_json.id}`,\n        };\n        return callback(null, track);\n      });\n  }\n\n  static ng_parse_playlist_tracks(playlist_tracks, callback) {\n    const target_url = 'https://music.163.com/weapi/v3/song/detail';\n    const track_ids = playlist_tracks.map((i) => i.id);\n    const d = {\n      c: `[${track_ids.map((id) => `{\"id\":${id}}`).join(',')}]`,\n      ids: `[${track_ids.join(',')}]`,\n    };\n    const data = this.weapi(d);\n    axios.post(target_url, new URLSearchParams(data)).then((response) => {\n      const tracks = response.data.songs.map((track_json) => ({\n        id: `netrack_${track_json.id}`,\n        title: track_json.name,\n        artist: track_json.ar[0].name,\n        artist_id: `neartist_${track_json.ar[0].id}`,\n        album: track_json.al.name,\n        album_id: `nealbum_${track_json.al.id}`,\n        source: 'netease',\n        source_url: `https://music.163.com/#/song?id=${track_json.id}`,\n        img_url: track_json.al.picUrl,\n        // url: `netrack_${track_json.id}`,\n      }));\n\n      return callback(null, tracks);\n    });\n  }\n\n  static split_array(myarray, size) {\n    const count = Math.ceil(myarray.length / size);\n    const result = [];\n    for (let i = 0; i < count; i += 1) {\n      result.push(myarray.slice(i * size, (i + 1) * size));\n    }\n    return result;\n  }\n\n  static ne_get_playlist(url) {\n    // special thanks for @Binaryify\n    // https://github.com/Binaryify/NeteaseCloudMusicApi\n    return {\n      success: (fn) => {\n        const list_id = getParameterByName('list_id', url).split('_').pop();\n        const target_url = 'https://music.163.com/weapi/v3/playlist/detail';\n        const d = {\n          id: list_id,\n          offset: 0,\n          total: true,\n          limit: 1000,\n          n: 1000,\n          csrf_token: '',\n        };\n        const data = this.weapi(d);\n        this.ne_ensure_cookie(() => {\n          axios.post(target_url, new URLSearchParams(data)).then((response) => {\n            const { data: res_data } = response;\n            const info = {\n              id: `neplaylist_${list_id}`,\n              cover_img_url: res_data.playlist.coverImgUrl,\n              title: res_data.playlist.name,\n              source_url: `https://music.163.com/#/playlist?id=${list_id}`,\n            };\n            const max_allow_size = 1000;\n            const trackIdsArray = this.split_array(\n              res_data.playlist.trackIds,\n              max_allow_size\n            );\n\n            function ng_parse_playlist_tracks_wrapper(trackIds, callback) {\n              return netease.ng_parse_playlist_tracks(trackIds, callback);\n            }\n\n            async.concat(\n              trackIdsArray,\n              ng_parse_playlist_tracks_wrapper,\n              (err, tracks) => {\n                fn({ tracks, info });\n              }\n            );\n\n            // request every tracks to fetch song info\n            // async_process_list(res_data.playlist.trackIds, ng_render_playlist_result_item,\n            //   (err, tracks) => fn({\n            //     tracks,\n            //     info,\n            //   }));\n          });\n        });\n      },\n    };\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    const target_url = `https://interface3.music.163.com/eapi/song/enhance/player/url`;\n    let song_id = track.id;\n    const eapiUrl = '/api/song/enhance/player/url';\n\n    song_id = song_id.slice('netrack_'.length);\n\n    const d = {\n      ids: `[${song_id}]`,\n      br: 999000,\n    };\n    const data = this.eapi(eapiUrl, d);\n    const expire =\n      (new Date().getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000;\n\n    cookieSet(\n      {\n        url: 'https://interface3.music.163.com',\n        name: 'os',\n        value: 'pc',\n        expirationDate: expire,\n      },\n      (cookie) => {\n        axios.post(target_url, new URLSearchParams(data)).then((response) => {\n          const { data: res_data } = response;\n          const { url, br } = res_data.data[0];\n          if (url != null) {\n            sound.url = url;\n            const bitrate = `${(br / 1000).toFixed(0)}kbps`;\n            sound.bitrate = bitrate;\n            sound.platform = 'netease';\n\n            success(sound);\n          } else {\n            failure(sound);\n          }\n        });\n      }\n    );\n  }\n\n  static is_playable(song) {\n    return song.fee !== 4 && song.fee !== 1;\n  }\n\n  static search(url) {\n    // use chrome extension to modify referer.\n    const target_url = 'https://music.163.com/api/search/pc';\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const searchType = getParameterByName('type', url);\n    let ne_search_type = '1';\n    if (searchType === '1') {\n      ne_search_type = '1000';\n    }\n    const req_data = {\n      s: keyword,\n      offset: 20 * (curpage - 1),\n      limit: 20,\n      type: ne_search_type,\n    };\n    return {\n      success: (fn) => {\n        this.ne_ensure_cookie(() => {\n          axios\n            .post(target_url, new URLSearchParams(req_data))\n            .then((response) => {\n              const { data } = response;\n              let result = [];\n              let total = 0;\n              if (searchType === '0') {\n                result = data.result.songs.map((song_info) => ({\n                  id: `netrack_${song_info.id}`,\n                  title: song_info.name,\n                  artist: song_info.artists[0].name,\n                  artist_id: `neartist_${song_info.artists[0].id}`,\n                  album: song_info.album.name,\n                  album_id: `nealbum_${song_info.album.id}`,\n                  source: 'netease',\n                  source_url: `https://music.163.com/#/song?id=${song_info.id}`,\n                  img_url: song_info.album.picUrl,\n                  // url: `netrack_${song_info.id}`,\n                  url: !this.is_playable(song_info) ? '' : undefined,\n                }));\n                total = data.result.songCount;\n              } else if (searchType === '1') {\n                result = data.result.playlists.map((info) => ({\n                  id: `neplaylist_${info.id}`,\n                  title: info.name,\n                  source: 'netease',\n                  source_url: `https://music.163.com/#/playlist?id=${info.id}`,\n                  img_url: info.coverImgUrl,\n                  url: `neplaylist_${info.id}`,\n                  author: info.creator.nickname,\n                  count: info.trackCount,\n                }));\n                total = data.result.playlistCount;\n              }\n\n              return fn({\n                result,\n                total,\n                type: searchType,\n              });\n            })\n            .catch(() =>\n              fn({\n                result: [],\n                total: 0,\n                type: searchType,\n              })\n            );\n        });\n      },\n    };\n  }\n\n  static ne_album(url) {\n    const album_id = getParameterByName('list_id', url).split('_').pop();\n    // use chrome extension to modify referer.\n    const target_url = `https://music.163.com/api/album/${album_id}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const info = {\n            cover_img_url: data.album.picUrl,\n            title: data.album.name,\n            id: `nealbum_${data.album.id}`,\n            source_url: `https://music.163.com/#/album?id=${data.album.id}`,\n          };\n\n          const tracks = data.album.songs.map((song_info) => ({\n            id: `netrack_${song_info.id}`,\n            title: song_info.name,\n            artist: song_info.artists[0].name,\n            artist_id: `neartist_${song_info.artists[0].id}`,\n            album: song_info.album.name,\n            album_id: `nealbum_${song_info.album.id}`,\n            source: 'netease',\n            source_url: `https://music.163.com/#/song?id=${song_info.id}`,\n            img_url: song_info.album.picUrl,\n            url: !this.is_playable(song_info) ? '' : undefined,\n          }));\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static ne_artist(url) {\n    const artist_id = getParameterByName('list_id', url).split('_').pop();\n    // use chrome extension to modify referer.\n    const target_url = `https://music.163.com/api/artist/${artist_id}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const info = {\n            cover_img_url: data.artist.picUrl,\n            title: data.artist.name,\n            id: `neartist_${data.artist.id}`,\n            source_url: `https://music.163.com/#/artist?id=${data.artist.id}`,\n          };\n\n          const tracks = data.hotSongs.map((song_info) => ({\n            id: `netrack_${song_info.id}`,\n            title: song_info.name,\n            artist: song_info.artists[0].name,\n            artist_id: `neartist_${song_info.artists[0].id}`,\n            album: song_info.album.name,\n            album_id: `nealbum_${song_info.album.id}`,\n            source: 'netease',\n            source_url: `https://music.163.com/#/song?id=${song_info.id}`,\n            img_url: song_info.album.picUrl,\n            // url: `netrack_${song_info.id}`,\n            url: !this.is_playable(song_info) ? '' : undefined,\n          }));\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static lyric(url) {\n    const track_id = getParameterByName('track_id', url).split('_').pop();\n    // use chrome extension to modify referer.\n    const target_url = 'https://music.163.com/weapi/song/lyric?csrf_token=';\n    const csrf = '';\n    const d = {\n      id: track_id,\n      lv: -1,\n      tv: -1,\n      csrf_token: csrf,\n    };\n    const data = this.weapi(d);\n    return {\n      success: (fn) => {\n        axios.post(target_url, new URLSearchParams(data)).then((response) => {\n          const { data: res_data } = response;\n          let lrc = '';\n          let tlrc = '';\n          if (res_data.lrc != null) {\n            lrc = res_data.lrc.lyric;\n          }\n          if (res_data.tlyric != null && res_data.tlyric.lyric != null) {\n            // eslint-disable-next-line no-control-regex\n            tlrc = res_data.tlyric.lyric.replace(/(\b|\\\\)/g, '');\n            tlrc = tlrc.replace(/[\\u2005]+/g, ' ');\n          }\n          return fn({\n            lyric: lrc,\n            tlyric: tlrc,\n          });\n        });\n      },\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    let id = '';\n    // eslint-disable-next-line no-param-reassign\n    url = url.replace(\n      'music.163.com/#/discover/toplist?',\n      'music.163.com/#/playlist?'\n    ); // eslint-disable-line no-param-reassign\n    url = url.replace('music.163.com/#/my/m/music/', 'music.163.com/'); // eslint-disable-line no-param-reassign\n    url = url.replace('music.163.com/#/m/', 'music.163.com/'); // eslint-disable-line no-param-reassign\n    url = url.replace('music.163.com/#/', 'music.163.com/'); // eslint-disable-line no-param-reassign\n    if (url.search('//music.163.com/playlist') !== -1) {\n      const match = /\\/\\/music.163.com\\/playlist\\/([0-9]+)/.exec(url);\n      id = match ? match[1] : getParameterByName('id', url);\n      result = {\n        type: 'playlist',\n        id: `neplaylist_${id}`,\n      };\n    } else if (url.search('//music.163.com/artist') !== -1) {\n      result = {\n        type: 'playlist',\n        id: `neartist_${getParameterByName('id', url)}`,\n      };\n    } else if (url.search('//music.163.com/album') !== -1) {\n      const match = /\\/\\/music.163.com\\/album\\/([0-9]+)/.exec(url);\n      id = match ? match[1] : getParameterByName('id', url);\n      result = {\n        type: 'playlist',\n        id: `nealbum_${id}`,\n      };\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'neplaylist':\n        return this.ne_get_playlist(url);\n      case 'nealbum':\n        return this.ne_album(url);\n      case 'neartist':\n        return this.ne_artist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    const recommend = [\n      { id: '', name: '全部' },\n      { id: 'toplist', name: '排行榜' },\n      { id: '流行', name: '流行' },\n      { id: '民谣', name: '民谣' },\n      { id: '电子', name: '电子' },\n      { id: '舞曲', name: '舞曲' },\n      { id: '说唱', name: '说唱' },\n      { id: '轻音乐', name: '轻音乐' },\n      { id: '爵士', name: '爵士' },\n      { id: '乡村', name: '乡村' },\n    ];\n\n    const all = [\n      {\n        category: '语种',\n        filters: [\n          { id: '华语', name: '华语' },\n          { id: '欧美', name: '欧美' },\n          { id: '日语', name: '日语' },\n          { id: '韩语', name: '韩语' },\n          { id: '粤语', name: '粤语' },\n        ],\n      },\n      {\n        category: '风格',\n        filters: [\n          { id: '流行', name: '流行' },\n          { id: '民谣', name: '民谣' },\n          { id: '电子', name: '电子' },\n          { id: '舞曲', name: '舞曲' },\n          { id: '说唱', name: '说唱' },\n          { id: '轻音乐', name: '轻音乐' },\n          { id: '爵士', name: '爵士' },\n          { id: '乡村', name: '乡村' },\n          { id: 'R%26B%2FSoul', name: 'R&B/Soul' },\n          { id: '古典', name: '古典' },\n          { id: '民族', name: '民族' },\n          { id: '英伦', name: '英伦' },\n          { id: '金属', name: '金属' },\n          { id: '朋克', name: '朋克' },\n          { id: '蓝调', name: '蓝调' },\n          { id: '雷鬼', name: '雷鬼' },\n          { id: '世界音乐', name: '世界音乐' },\n          { id: '拉丁', name: '拉丁' },\n          { id: 'New Age', name: 'New Age' },\n          { id: '古风', name: '古风' },\n          { id: '后摇', name: '后摇' },\n          { id: 'Bossa Nova', name: 'Bossa Nova' },\n        ],\n      },\n      {\n        category: '场景',\n        filters: [\n          { id: '清晨', name: '清晨' },\n          { id: '夜晚', name: '夜晚' },\n          { id: '学习', name: '学习' },\n          { id: '工作', name: '工作' },\n          { id: '午休', name: '午休' },\n          { id: '下午茶', name: '下午茶' },\n          { id: '地铁', name: '地铁' },\n          { id: '驾车', name: '驾车' },\n          { id: '运动', name: '运动' },\n          { id: '旅行', name: '旅行' },\n          { id: '散步', name: '散步' },\n          { id: '酒吧', name: '酒吧' },\n        ],\n      },\n      {\n        category: '情感',\n        filters: [\n          { id: '怀旧', name: '怀旧' },\n          { id: '清新', name: '清新' },\n          { id: '浪漫', name: '浪漫' },\n          { id: '伤感', name: '伤感' },\n          { id: '治愈', name: '治愈' },\n          { id: '放松', name: '放松' },\n          { id: '孤独', name: '孤独' },\n          { id: '感动', name: '感动' },\n          { id: '兴奋', name: '兴奋' },\n          { id: '快乐', name: '快乐' },\n          { id: '安静', name: '安静' },\n          { id: '思念', name: '思念' },\n        ],\n      },\n      {\n        category: '主题',\n        filters: [\n          { id: '综艺', name: '综艺' },\n          { id: '影视原声', name: '影视原声' },\n          { id: 'ACG', name: 'ACG' },\n          { id: '儿童', name: '儿童' },\n          { id: '校园', name: '校园' },\n          { id: '游戏', name: '游戏' },\n          { id: '70后', name: '70后' },\n          { id: '80后', name: '80后' },\n          { id: '90后', name: '90后' },\n          { id: '网络歌曲', name: '网络歌曲' },\n          { id: 'KTV', name: 'KTV' },\n          { id: '经典', name: '经典' },\n          { id: '翻唱', name: '翻唱' },\n          { id: '吉他', name: '吉他' },\n          { id: '钢琴', name: '钢琴' },\n          { id: '器乐', name: '器乐' },\n          { id: '榜单', name: '榜单' },\n          { id: '00后', name: '00后' },\n        ],\n      },\n    ];\n    return {\n      success: (fn) => fn({ recommend, all }),\n    };\n  }\n\n  static login(url) {\n    // use chrome extension to modify referer.\n    let target_url = 'https://music.163.com/weapi/login';\n    const loginType = getParameterByName('type', url);\n\n    const password = getParameterByName('password', url);\n\n    let req_data = {};\n    if (loginType === 'email') {\n      const email = getParameterByName('email', url);\n\n      req_data = {\n        username: email,\n        password: forge.md5\n          .create()\n          .update(forge.util.encodeUtf8(password))\n          .digest()\n          .toHex(),\n        rememberLogin: 'true',\n      };\n    } else if (loginType === 'phone') {\n      target_url = `https://music.163.com/weapi/login/cellphone`;\n      const countrycode = getParameterByName('countrycode', url);\n      const phone = getParameterByName('phone', url);\n      req_data = {\n        phone,\n        countrycode,\n        password: forge.md5\n          .create()\n          .update(forge.util.encodeUtf8(password))\n          .digest()\n          .toHex(),\n        rememberLogin: 'true',\n      };\n    }\n\n    const encrypt_req_data = this.weapi(req_data);\n    const expire =\n      (new Date().getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000;\n\n    cookieSet(\n      {\n        url: 'https://music.163.com',\n        name: 'os',\n        value: 'pc',\n        expirationDate: expire,\n      },\n      (cookie) => {}\n    );\n    return {\n      success: (fn) => {\n        axios\n          .post(target_url, new URLSearchParams(encrypt_req_data))\n          .then((response) => {\n            const { data } = response;\n            const result = {\n              is_login: true,\n              user_id: data.account.id,\n              user_name: data.account.userName,\n              nickname: data.profile.nickname,\n              avatar: data.profile.avatarUrl,\n              platform: 'netease',\n              data,\n            };\n            return fn({\n              status: 'success',\n              data: result,\n            });\n          })\n          .catch(() =>\n            fn({\n              status: 'fail',\n              data: {},\n            })\n          );\n      },\n    };\n  }\n\n  static get_user_playlist(url, playlistType) {\n    const user_id = getParameterByName('user_id', url);\n    const target_url = 'https://music.163.com/api/user/playlist';\n\n    const req_data = {\n      uid: user_id,\n      limit: 1000,\n      offset: 0,\n      includeVideo: true,\n    };\n\n    return {\n      success: (fn) => {\n        axios\n          .post(target_url, new URLSearchParams(req_data))\n          .then((response) => {\n            const playlists = [];\n            response.data.playlist.forEach((item) => {\n              if (playlistType === 'created' && item.subscribed !== false) {\n                return;\n              }\n              if (playlistType === 'favorite' && item.subscribed !== true) {\n                return;\n              }\n              const playlist = {\n                cover_img_url: item.coverImgUrl,\n                id: `neplaylist_${item.id}`,\n                source_url: `https://music.163.com/#/playlist?id=${item.id}`,\n                title: item.name,\n              };\n              playlists.push(playlist);\n            });\n            return fn({\n              status: 'success',\n              data: {\n                playlists,\n              },\n            });\n          });\n      },\n    };\n  }\n\n  static get_user_created_playlist(url) {\n    return this.get_user_playlist(url, 'created');\n  }\n\n  static get_user_favorite_playlist(url) {\n    return this.get_user_playlist(url, 'favorite');\n  }\n\n  static get_recommend_playlist() {\n    const target_url = 'https://music.163.com/weapi/personalized/playlist';\n\n    const req_data = {\n      limit: 30,\n      total: true,\n      n: 1000,\n    };\n\n    const encrypt_req_data = this.weapi(req_data);\n\n    return {\n      success: (fn) => {\n        axios\n          .post(target_url, new URLSearchParams(encrypt_req_data))\n          .then((response) => {\n            const playlists = [];\n            response.data.result.forEach((item) => {\n              const playlist = {\n                cover_img_url: item.picUrl,\n                id: `neplaylist_${item.id}`,\n                source_url: `https://music.163.com/#/playlist?id=${item.id}`,\n                title: item.name,\n              };\n              playlists.push(playlist);\n            });\n            return fn({\n              status: 'success',\n              data: {\n                playlists,\n              },\n            });\n          });\n      },\n    };\n  }\n\n  static get_user() {\n    const url = `https://music.163.com/api/nuser/account/get`;\n\n    const encrypt_req_data = this.weapi({});\n    return {\n      success: (fn) => {\n        axios.post(url, new URLSearchParams(encrypt_req_data)).then((res) => {\n          let result = { is_login: false };\n          let status = 'fail';\n          if (res.data.account !== null) {\n            status = 'success';\n            const { data } = res;\n            result = {\n              is_login: true,\n              user_id: data.account.id,\n              user_name: data.account.userName,\n              nickname: data.profile.nickname,\n              avatar: data.profile.avatarUrl,\n              platform: 'netease',\n              data,\n            };\n          }\n\n          return fn({\n            status,\n            data: result,\n          });\n        });\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://music.163.com/#/login`;\n  }\n\n  static logout() {\n    cookieRemove(\n      {\n        url: 'https://music.163.com',\n        name: 'MUSIC_U',\n      },\n      (cookie) => {}\n    );\n  }\n}\n"
  },
  {
    "path": "js/provider/qq.js",
    "content": "/* eslint-disable no-use-before-define */\n/* global getParameterByName cookieGet cookieRemove */\n// eslint-disable-next-line no-unused-vars\nclass qq {\n  static htmlDecode(value) {\n    const parser = new DOMParser();\n    return parser.parseFromString(value, 'text/html').body.textContent;\n  }\n\n  static qq_show_toplist(offset) {\n    if (offset !== undefined && offset > 0) {\n      return {\n        success: (fn) => fn({ result: [] }),\n      };\n    }\n    const url =\n      'https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=5381&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5';\n\n    return {\n      success: (fn) => {\n        axios.get(url).then((response) => {\n          const result = [];\n          response.data.data.topList.forEach((item) => {\n            const playlist = {\n              cover_img_url: item.picUrl,\n              id: `qqtoplist_${item.id}`,\n              source_url: `https://y.qq.com/n/yqq/toplist/${item.id}.html`,\n              title: item.topTitle,\n            };\n            result.push(playlist);\n          });\n          return fn({ result });\n        });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    const offset = Number(getParameterByName('offset', url)) || 0;\n    let filterId = getParameterByName('filter_id', url) || '';\n    if (filterId === 'toplist') {\n      return this.qq_show_toplist(offset);\n    }\n    if (filterId === '') {\n      filterId = '10000000';\n    }\n\n    const target_url =\n      'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg' +\n      `?picmid=1&rnd=${Math.random()}&g_tk=732560869` +\n      '&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8' +\n      '&notice=0&platform=yqq.json&needNewCode=0' +\n      `&categoryId=${filterId}&sortId=5&sin=${offset}&ein=${29 + offset}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const playlists = data.data.list.map((item) => ({\n            cover_img_url: item.imgurl,\n            title: this.htmlDecode(item.dissname),\n            id: `qqplaylist_${item.dissid}`,\n            source_url: `https://y.qq.com/n/ryqq/playlist/${item.dissid}`,\n          }));\n\n          return fn({\n            result: playlists,\n          });\n        });\n      },\n    };\n  }\n\n  static qq_get_image_url(qqimgid, img_type) {\n    if (qqimgid == null) {\n      return '';\n    }\n    let category = '';\n    if (img_type === 'artist') {\n      category = 'T001R300x300M000';\n    }\n    if (img_type === 'album') {\n      category = 'T002R300x300M000';\n    }\n    const s = category + qqimgid;\n    const url = `https://y.gtimg.cn/music/photo_new/${s}.jpg`;\n    return url;\n  }\n\n  static qq_is_playable(song) {\n    const switch_flag = song.switch.toString(2).split('');\n    switch_flag.pop();\n    switch_flag.reverse();\n    // flag switch table meaning:\n    // [\"play_lq\", \"play_hq\", \"play_sq\", \"down_lq\", \"down_hq\", \"down_sq\", \"soso\",\n    //  \"fav\", \"share\", \"bgm\", \"ring\", \"sing\", \"radio\", \"try\", \"give\"]\n    const play_flag = switch_flag[0];\n    const try_flag = switch_flag[13];\n    return play_flag === '1' || (play_flag === '1' && try_flag === '1');\n  }\n\n  static qq_convert_song(song) {\n    const d = {\n      id: `qqtrack_${song.songmid}`,\n      title: this.htmlDecode(song.songname),\n      artist: this.htmlDecode(song.singer[0].name),\n      artist_id: `qqartist_${song.singer[0].mid}`,\n      album: this.htmlDecode(song.albumname),\n      album_id: `qqalbum_${song.albummid}`,\n      img_url: this.qq_get_image_url(song.albummid, 'album'),\n      source: 'qq',\n      source_url: `https://y.qq.com/#type=song&mid=${song.songmid}&tpl=yqq_song_detail`,\n      // url: `qqtrack_${song.songmid}`,\n      url: !qq.qq_is_playable(song) ? '' : undefined,\n    };\n    return d;\n  }\n\n  static qq_convert_song2(song) {\n    const d = {\n      id: `qqtrack_${song.mid}`,\n      title: this.htmlDecode(song.name),\n      artist: this.htmlDecode(song.singer[0].name),\n      artist_id: `qqartist_${song.singer[0].mid}`,\n      album: this.htmlDecode(song.album.name),\n      album_id: `qqalbum_${song.album.mid}`,\n      img_url: this.qq_get_image_url(song.album.mid, 'album'),\n      source: 'qq',\n      source_url: `https://y.qq.com/#type=song&mid=${song.mid}&tpl=yqq_song_detail`,\n      url: '',\n    };\n    return d;\n  }\n\n  static get_toplist_url(id, period, limit) {\n    return `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(\n      JSON.stringify({\n        comm: {\n          cv: 1602,\n          ct: 20,\n        },\n        toplist: {\n          module: 'musicToplist.ToplistInfoServer',\n          method: 'GetDetail',\n          param: {\n            topid: id,\n            num: limit,\n            period,\n          },\n        },\n      })\n    )}`;\n  }\n\n  static get_periods(topid) {\n    const periodUrl = 'https://c.y.qq.com/node/pc/wk_v15/top.html';\n    const regExps = {\n      periodList:\n        /<i class=\"play_cover__btn c_tx_link js_icon_play\" data-listkey=\".+?\" data-listname=\".+?\" data-tid=\".+?\" data-date=\".+?\" .+?<\\/i>/g,\n      period:\n        /data-listname=\"(.+?)\" data-tid=\".*?\\/(.+?)\" data-date=\"(.+?)\" .+?<\\/i>/,\n    };\n    const periods = {};\n    return axios.get(periodUrl).then((response) => {\n      const html = response.data;\n      const pl = html.match(regExps.periodList);\n      if (!pl) return Promise.reject();\n      pl.forEach((p) => {\n        const pr = p.match(regExps.period);\n        if (!pr) return;\n        periods[pr[2]] = {\n          name: pr[1],\n          id: pr[2],\n          period: pr[3],\n        };\n      });\n      const info = periods[topid];\n      return info && info.period;\n    });\n  }\n\n  static qq_toplist(url) {\n    // special thanks to lx-music-desktop solution\n    // https://github.com/lyswhut/lx-music-desktop/blob/24521bf50d80512a44048596639052e3194b2bf1/src/renderer/utils/music/tx/leaderboard.js\n\n    const list_id = Number(getParameterByName('list_id', url).split('_').pop());\n\n    return {\n      success: (fn) => {\n        this.get_periods(list_id).then((listPeriod) => {\n          const limit = 100;\n          // TODO: visit all pages of toplist\n          const target_url = this.get_toplist_url(list_id, listPeriod, limit);\n\n          axios.get(target_url).then((response) => {\n            const { data } = response;\n            const tracks = data.toplist.data.songInfoList.map((song) => {\n              const d = {\n                id: `qqtrack_${song.mid}`,\n                title: this.htmlDecode(song.name),\n                artist: this.htmlDecode(song.singer[0].name),\n                artist_id: `qqartist_${song.singer[0].mid}`,\n                album: this.htmlDecode(song.album.name),\n                album_id: `qqalbum_${song.album.mid}`,\n                img_url: this.qq_get_image_url(song.album.mid, 'album'),\n                source: 'qq',\n                source_url: `https://y.qq.com/#type=song&mid=${song.mid}&tpl=yqq_song_detail`,\n              };\n              return d;\n            });\n            const info = {\n              cover_img_url: data.toplist.data.data.frontPicUrl,\n              title: data.toplist.data.data.title,\n              id: `qqtoplist_${list_id}`,\n              source_url: `https://y.qq.com/n/yqq/toplist/${list_id}.html`,\n            };\n            return fn({\n              tracks,\n              info,\n            });\n          });\n        });\n      },\n    };\n  }\n\n  static qq_get_playlist(url) {\n    // eslint-disable-line no-unused-vars\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n\n    return {\n      success: (fn) => {\n        const target_url =\n          'https://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_' +\n          'byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0' +\n          `&nosign=1&disstid=${list_id}&g_tk=5381&loginUin=0&hostUin=0` +\n          '&format=json&inCharset=GB2312&outCharset=utf-8&notice=0' +\n          '&platform=yqq&needNewCode=0';\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const info = {\n            cover_img_url: data.cdlist[0].logo,\n            title: data.cdlist[0].dissname,\n            id: `qqplaylist_${list_id}`,\n            source_url: `https://y.qq.com/n/ryqq/playlist/${list_id}`,\n          };\n\n          const tracks = data.cdlist[0].songlist.map((item) =>\n            this.qq_convert_song(item)\n          );\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static qq_album(url) {\n    const album_id = getParameterByName('list_id', url).split('_').pop();\n\n    return {\n      success: (fn) => {\n        const target_url =\n          'https://i.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg' +\n          `?platform=h5page&albummid=${album_id}&g_tk=938407465` +\n          '&uin=0&format=json&inCharset=utf-8&outCharset=utf-8' +\n          '&notice=0&platform=h5&needNewCode=1&_=1459961045571';\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const info = {\n            cover_img_url: this.qq_get_image_url(album_id, 'album'),\n            title: data.data.name,\n            id: `qqalbum_${album_id}`,\n            source_url: `https://y.qq.com/#type=album&mid=${album_id}`,\n          };\n\n          const tracks = data.data.list.map((item) =>\n            this.qq_convert_song(item)\n          );\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static qq_artist(url) {\n    const artist_id = getParameterByName('list_id', url).split('_').pop();\n\n    return {\n      success: (fn) => {\n        const target_url = `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&loginUin=0&hostUin=0inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(\n          JSON.stringify({\n            comm: {\n              ct: 24,\n              cv: 0,\n            },\n            singer: {\n              method: 'get_singer_detail_info',\n              param: {\n                sort: 5,\n                singermid: artist_id,\n                sin: 0,\n                num: 50,\n              },\n              module: 'music.web_singer_info_svr',\n            },\n          })\n        )}`;\n\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n\n          const info = {\n            cover_img_url: this.qq_get_image_url(artist_id, 'artist'),\n            title: data.singer.data.singer_info.name,\n            id: `qqartist_${artist_id}`,\n            source_url: `https://y.qq.com/#type=singer&mid=${artist_id}`,\n          };\n\n          const tracks = data.singer.data.songlist.map((item) =>\n            this.qq_convert_song2(item)\n          );\n          return fn({\n            tracks,\n            info,\n          });\n        });\n      },\n    };\n  }\n\n  static search(url) {\n    // eslint-disable-line no-unused-vars\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const searchType = getParameterByName('type', url);\n\n    // API solution from lx-music-desktop\n    // https://github.com/lyswhut/lx-music-desktop/blob/master/src/renderer/utils/music/tx/musicSearch.js\n    const target_url = 'https://u.y.qq.com/cgi-bin/musicu.fcg';\n\n    const searchTypeMapping = {\n      0: 0,\n      1: 3,\n    };\n\n    return {\n      success: (fn) => {\n        const limit = 50;\n        const page = curpage;\n        const query = {\n          comm: {\n            ct: '19',\n            cv: '1859',\n            uin: '0',\n          },\n          req: {\n            method: 'DoSearchForQQMusicDesktop',\n            module: 'music.search.SearchCgiService',\n            param: {\n              grp: 1,\n              num_per_page: limit,\n              page_num: parseInt(page, 10),\n              query: keyword,\n              search_type: searchTypeMapping[searchType],\n            },\n          },\n        };\n        axios.post(target_url, query).then((response) => {\n          const { data } = response;\n          let result = [];\n          let total = 0;\n          if (searchType === '0') {\n            result = data.req.data.body.song.list.map((item) =>\n              this.qq_convert_song2(item)\n            );\n            total = data.req.data.meta.sum;\n          } else if (searchType === '1') {\n            result = data.req.data.body.songlist.list.map((info) => ({\n              id: `qqplaylist_${info.dissid}`,\n              title: this.htmlDecode(info.dissname),\n              source: 'qq',\n              source_url: `https://y.qq.com/n/ryqq/playlist/${info.dissid}`,\n              img_url: info.imgurl,\n              url: `qqplaylist_${info.dissid}`,\n              author: this.UnicodeToAscii(info.creator.name),\n              count: info.song_count,\n            }));\n            total = data.req.data.meta.sum;\n          }\n          return fn({\n            result,\n            total,\n            type: searchType,\n          });\n        });\n      },\n    };\n  }\n\n  static UnicodeToAscii(str) {\n    const result = str.replace(/&#(\\d+);/g, () =>\n      // eslint-disable-next-line prefer-rest-params\n      String.fromCharCode(arguments[1])\n    );\n    return result;\n  }\n\n  // eslint-disable-next-line no-unused-vars\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    const songId = track.id.slice('qqtrack_'.length);\n    const target_url = 'https://u.y.qq.com/cgi-bin/musicu.fcg';\n    // thanks to https://github.com/Rain120/qq-music-api/blob/2b9cb811934888a532545fbd0bf4e4ab2aea5dbe/routers/context/getMusicPlay.js\n    const guid = '10000';\n    const songmidList = [songId];\n    const uin = '0';\n\n    // server won't response with 320kbps request, downgrade to 128kbps\n    const fileType = '128';\n    const fileConfig = {\n      m4a: {\n        s: 'C400',\n        e: '.m4a',\n        bitrate: 'M4A',\n      },\n      128: {\n        s: 'M500',\n        e: '.mp3',\n        bitrate: '128kbps',\n      },\n      320: {\n        s: 'M800',\n        e: '.mp3',\n        bitrate: '320kbps',\n      },\n      ape: {\n        s: 'A000',\n        e: '.ape',\n        bitrate: 'APE',\n      },\n      flac: {\n        s: 'F000',\n        e: '.flac',\n        bitrate: 'FLAC',\n      },\n    };\n    const fileInfo = fileConfig[fileType];\n    const file =\n      songmidList.length === 1 &&\n      `${fileInfo.s}${songId}${songId}${fileInfo.e}`;\n\n    const reqData = {\n      req_1: {\n        module: 'vkey.GetVkeyServer',\n        method: 'CgiGetVkey',\n        param: {\n          filename: file ? [file] : [],\n          guid,\n          songmid: songmidList,\n          songtype: [0],\n          uin,\n          loginflag: 1,\n          platform: '20',\n        },\n      },\n      loginUin: uin,\n      comm: {\n        uin,\n        format: 'json',\n        ct: 24,\n        cv: 0,\n      },\n    };\n    axios.post(target_url, reqData).then((response) => {\n      const { data } = response;\n      const { purl } = data.req_1.data.midurlinfo[0];\n\n      if (purl === '') {\n        // vip\n        failure(sound);\n        return;\n      }\n      const url = data.req_1.data.sip[0] + purl;\n      sound.url = url;\n      const prefix = purl.slice(0, 4);\n      const found = Object.values(fileConfig).filter((i) => i.s === prefix);\n      sound.bitrate = found.length > 0 ? found[0].bitrate : '';\n      sound.platform = 'qq';\n\n      success(sound);\n    });\n  }\n\n  // eslint-disable-next-line no-unused-vars\n  static str2ab(str) {\n    // string to array buffer.\n    const buf = new ArrayBuffer(str.length);\n    const bufView = new Uint8Array(buf);\n    for (let i = 0, strLen = str.length; i < strLen; i += 1) {\n      bufView[i] = str.charCodeAt(i);\n    }\n    return buf;\n  }\n\n  static lyric(url) {\n    // eslint-disable-line no-unused-vars\n    const track_id = getParameterByName('track_id', url).split('_').pop();\n    // use chrome extension to modify referer.\n    const target_url =\n      'https://i.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?' +\n      `songmid=${track_id}&g_tk=5381&format=json&inCharset=utf8&outCharset=utf-8&nobase64=1`;\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const lrc = data.lyric || '';\n          const tlrc = data.trans.replace(/\\/\\//g, '') || '';\n          return fn({\n            lyric: lrc,\n            tlyric: tlrc,\n          });\n        });\n      },\n    };\n  }\n\n  static parse_url(url) {\n    return {\n      success: (fn) => {\n        let result;\n\n        let match = /\\/\\/y.qq.com\\/n\\/yqq\\/playlist\\/([0-9]+)/.exec(url);\n        if (match != null) {\n          const playlist_id = match[1];\n          result = {\n            type: 'playlist',\n            id: `qqplaylist_${playlist_id}`,\n          };\n        }\n        match = /\\/\\/y.qq.com\\/n\\/yqq\\/playsquare\\/([0-9]+)/.exec(url);\n        if (match != null) {\n          const playlist_id = match[1];\n          result = {\n            type: 'playlist',\n            id: `qqplaylist_${playlist_id}`,\n          };\n        }\n        match =\n          /\\/\\/y.qq.com\\/n\\/m\\/detail\\/taoge\\/index.html\\?id=([0-9]+)/.exec(\n            url\n          );\n        if (match != null) {\n          const playlist_id = match[1];\n          result = {\n            type: 'playlist',\n            id: `qqplaylist_${playlist_id}`,\n          };\n        }\n\n        // c.y.qq.com/base/fcgi-bin/u?__=1MsbSLu\n        match = /\\/\\/c.y.qq.com\\/base\\/fcgi-bin\\/u\\?__=([0-9a-zA-Z]+)/.exec(\n          url\n        );\n        if (match != null) {\n          return axios\n            .get(url)\n            .then((response) => {\n              const { responseURL } = response.request;\n              const playlist_id = getParameterByName('id', responseURL);\n              result = {\n                type: 'playlist',\n                id: `qqplaylist_${playlist_id}`,\n              };\n              return fn(result);\n            })\n            .catch(() => fn(undefined));\n        }\n        return fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'qqplaylist':\n        return this.qq_get_playlist(url);\n      case 'qqalbum':\n        return this.qq_album(url);\n      case 'qqartist':\n        return this.qq_artist(url);\n      case 'qqtoplist':\n        return this.qq_toplist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    const target_url =\n      'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_tag_conf.fcg' +\n      `?picmid=1&rnd=${Math.random()}&g_tk=732560869` +\n      '&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8' +\n      '&notice=0&platform=yqq.json&needNewCode=0';\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const { data } = response;\n          const all = [];\n          data.data.categories.forEach((cate) => {\n            const result = { category: cate.categoryGroupName, filters: [] };\n            if (cate.usable === 1) {\n              cate.items.forEach((item) => {\n                result.filters.push({\n                  id: item.categoryId,\n                  name: this.htmlDecode(item.categoryName),\n                });\n              });\n              all.push(result);\n            }\n          });\n          const recommendLimit = 8;\n          const recommend = [\n            { id: '', name: '全部' },\n            { id: 'toplist', name: '排行榜' },\n            ...all[1].filters.slice(0, recommendLimit),\n          ];\n\n          return fn({\n            recommend,\n            all,\n          });\n        });\n      },\n    };\n  }\n\n  static get_user_by_uin(uin, callback) {\n    const infoUrl = `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&&loginUin=${uin}&hostUin=0inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(\n      JSON.stringify({\n        comm: { ct: 24, cv: 0 },\n        vip: {\n          module: 'userInfo.VipQueryServer',\n          method: 'SRFVipQuery_V2',\n          param: { uin_list: [uin] },\n        },\n        base: {\n          module: 'userInfo.BaseUserInfoServer',\n          method: 'get_user_baseinfo_v2',\n          param: { vec_uin: [uin] },\n        },\n      })\n    )}`;\n\n    return axios.get(infoUrl).then((response) => {\n      const { data } = response;\n      const info = data.base.data.map_userinfo[uin];\n      const result = {\n        is_login: true,\n        user_id: uin,\n        user_name: uin,\n        nickname: info.nick,\n        avatar: info.headurl,\n        platform: 'qq',\n        data,\n      };\n      return callback({ status: 'success', data: result });\n    });\n  }\n\n  static get_user_created_playlist(url) {\n    const user_id = getParameterByName('user_id', url);\n    // TODO: load more than size\n    const size = 100;\n\n    const target_url = `https://c.y.qq.com/rsc/fcgi-bin/fcg_user_created_diss?cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=1&uin=${user_id}&hostuin=${user_id}&sin=0&size=${size}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const playlists = [];\n          response.data.data.disslist.forEach((item) => {\n            let playlist = {};\n            if (item.dir_show === 0) {\n              if (item.tid === 0) {\n                return;\n              }\n              if (item.diss_name === '我喜欢') {\n                playlist = {\n                  cover_img_url:\n                    'https://y.gtimg.cn/mediastyle/y/img/cover_love_300.jpg',\n                  id: `qqplaylist_${item.tid}`,\n                  source_url: `https://y.qq.com/n/ryqq/playlist/${item.tid}`,\n                  title: item.diss_name,\n                };\n                playlists.push(playlist);\n              }\n            } else {\n              playlist = {\n                cover_img_url: item.diss_cover,\n                id: `qqplaylist_${item.tid}`,\n                source_url: `https://y.qq.com/n/ryqq/playlist/${item.tid}`,\n                title: item.diss_name,\n              };\n              playlists.push(playlist);\n            }\n          });\n          return fn({\n            status: 'success',\n            data: {\n              playlists,\n            },\n          });\n        });\n      },\n    };\n  }\n\n  static get_user_favorite_playlist(url) {\n    const user_id = getParameterByName('user_id', url);\n    // TODO: load more than size\n    const size = 100;\n    // https://github.com/jsososo/QQMusicApi/blob/master/routes/user.js\n    const target_url = `https://c.y.qq.com/fav/fcgi-bin/fcg_get_profile_order_asset.fcg`;\n    const data = {\n      ct: 20,\n      cid: 205360956,\n      userid: user_id,\n      reqtype: 3,\n      sin: 0,\n      ein: size,\n    };\n    return {\n      success: (fn) => {\n        axios.get(target_url, { params: data }).then((response) => {\n          const playlists = [];\n          response.data.data.cdlist.forEach((item) => {\n            let playlist = {};\n            if (item.dir_show === 0) {\n              return;\n            }\n            playlist = {\n              cover_img_url: item.logo,\n              id: `qqplaylist_${item.dissid}`,\n              source_url: `https://y.qq.com/n/ryqq/playlist/${item.dissid}`,\n              title: item.dissname,\n            };\n            playlists.push(playlist);\n          });\n          return fn({\n            status: 'success',\n            data: {\n              playlists,\n            },\n          });\n        });\n      },\n    };\n  }\n\n  static get_recommend_playlist() {\n    const target_url = `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&&loginUin=0&hostUin=0inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(\n      JSON.stringify({\n        comm: {\n          ct: 24,\n        },\n        recomPlaylist: {\n          method: 'get_hot_recommend',\n          param: {\n            async: 1,\n            cmd: 2,\n          },\n          module: 'playlist.HotRecommendServer',\n        },\n      })\n    )}`;\n\n    return {\n      success: (fn) => {\n        axios.get(target_url).then((response) => {\n          const playlists = [];\n          response.data.recomPlaylist.data.v_hot.forEach((item) => {\n            const playlist = {\n              cover_img_url: item.cover,\n              id: `qqplaylist_${item.content_id}`,\n              source_url: `https://y.qq.com/n/ryqq/playlist/${item.content_id}`,\n              title: item.title,\n            };\n            playlists.push(playlist);\n          });\n          return fn({\n            status: 'success',\n            data: {\n              playlists,\n            },\n          });\n        });\n      },\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => {\n        const domain = 'https://y.qq.com';\n        cookieGet(\n          {\n            url: domain,\n            name: 'uin',\n          },\n          (qqCookie) => {\n            if (qqCookie === null) {\n              return cookieGet(\n                {\n                  url: domain,\n                  name: 'wxuin',\n                },\n                (wxCookie) => {\n                  if (wxCookie == null) {\n                    return fn({ status: 'fail', data: {} });\n                  }\n                  let { value: uin } = wxCookie;\n                  uin = `1${uin.slice('o'.length)}`; // replace prefix o with 1\n                  return this.get_user_by_uin(uin, fn);\n                }\n              );\n            }\n            const { value: uin } = qqCookie;\n            return this.get_user_by_uin(uin, fn);\n          }\n        );\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://y.qq.com/portal/profile.html`;\n  }\n\n  static logout() {\n    cookieRemove(\n      {\n        url: 'https://y.qq.com',\n        name: 'uin',\n      },\n      () => {\n        cookieRemove(\n          {\n            url: 'https://y.qq.com',\n            name: 'wxuin',\n          },\n          () => {}\n        );\n      }\n    );\n  }\n}\n"
  },
  {
    "path": "js/provider/taihe.js",
    "content": "/* eslint-disable no-unused-vars */\n/* global async getParameterByName forge */\nconst axiosTH = axios.create({\n  baseURL: 'https://music.taihe.com/v1',\n});\naxiosTH.interceptors.request.use(\n  (config) => {\n    const params = { ...config.params };\n    params.timestamp = Math.round(Date.now() / 1000);\n    params.appid = '16073360';\n    const q = new URLSearchParams(params);\n    q.sort();\n    const signStr = decodeURIComponent(\n      `${q.toString()}0b50b02fd0d73a9c4c8c3a781c30845f`\n    );\n    params.sign = forge.md5\n      .create()\n      .update(forge.util.encodeUtf8(signStr))\n      .digest()\n      .toHex();\n\n    return { ...config, params };\n  },\n  null,\n  { synchronous: true }\n);\n\nclass taihe {\n  static th_convert_song(song) {\n    const track = {\n      id: `thtrack_${song.id}`,\n      title: song.title,\n      album: song.albumTitle,\n      album_id: `thalbum_${song.albumAssetCode}`,\n      source: 'taihe',\n      source_url: `https://music.taihe.com/song/${song.id}`,\n      img_url: song.pic,\n      lyric_url: song.lyric || '',\n    };\n    if (song.artist && song.artist.length) {\n      track.artist = song.artist[0].name;\n      track.artist_id = `thartist_${song.artist[0].artistCode}`;\n    }\n    return track;\n  }\n\n  static th_render_tracks(url, page, callback) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    axiosTH\n      .get('/tracklist/info', {\n        params: {\n          id: list_id,\n          pageNo: page,\n          pageSize: 100,\n        },\n      })\n      .then((response) => {\n        const data = response.data.data.trackList;\n        const tracks = data.map(this.th_convert_song);\n        return callback(null, tracks);\n      });\n  }\n\n  static search(url) {\n    const keyword = getParameterByName('keywords', url);\n    const curpage = getParameterByName('curpage', url);\n    const searchType = getParameterByName('type', url);\n    if (searchType === '1') {\n      return {\n        success: (fn) =>\n          fn({\n            result: [],\n            total: 0,\n            type: searchType,\n          }),\n      };\n    }\n    return {\n      success: (fn) => {\n        axiosTH\n          .get('/search', {\n            params: {\n              word: keyword,\n              pageNo: curpage || 1,\n              type: 1,\n            },\n          })\n          .then((res) => {\n            const { data } = res;\n            const tracks = data.data.typeTrack.map(this.th_convert_song);\n            return fn({\n              result: tracks,\n              total: data.data.total,\n              type: searchType,\n            });\n          })\n          .catch(() =>\n            fn({\n              result: [],\n              total: 0,\n              type: searchType,\n            })\n          );\n      },\n    };\n  }\n\n  static th_get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n\n    return {\n      success: (fn) => {\n        axiosTH\n          .get('/tracklist/info', {\n            params: {\n              id: list_id,\n            },\n          })\n          .then((response) => {\n            const { data } = response.data;\n\n            const info = {\n              cover_img_url: data.pic,\n              title: data.title,\n              id: `thplaylist_${list_id}`,\n              source_url: `https://music.taihe.com/songlist/${list_id}`,\n            };\n\n            const total = data.trackCount;\n            const page = Math.ceil(total / 100);\n            const page_array = Array.from({ length: page }, (v, k) => k + 1);\n            async.concat(\n              page_array,\n              (item, callback) => this.th_render_tracks(url, item, callback),\n              (err, tracks) => {\n                fn({\n                  tracks,\n                  info,\n                });\n              }\n            );\n          });\n      },\n    };\n  }\n\n  static th_artist(url) {\n    return {\n      success: (fn) => {\n        const artist_id = getParameterByName('list_id', url).split('_').pop();\n        axiosTH\n          .get('/artist/info', {\n            params: {\n              artistCode: artist_id,\n            },\n          })\n          .then((response) => {\n            const info = {\n              cover_img_url: response.data.data.pic,\n              title: response.data.data.name,\n              id: `thartist_${artist_id}`,\n              source_url: `https://music.taihe.com/artist/${artist_id}`,\n            };\n            axiosTH\n              .get('/artist/song', {\n                params: {\n                  artistCode: artist_id,\n                  pageNo: 1,\n                  pageSize: 50,\n                },\n              })\n              .then((res) => {\n                const tracks = res.data.data.result.map(this.th_convert_song);\n                return fn({\n                  tracks,\n                  info,\n                });\n              });\n          });\n      },\n    };\n  }\n\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    const song_id = track.id.slice('thtrack_'.length);\n    axiosTH\n      .get('/song/tracklink', {\n        params: {\n          TSID: song_id,\n        },\n      })\n      .then((response) => {\n        const { data } = response;\n        if (data.data && data.data.path) {\n          sound.url = data.data.path;\n          sound.platform = 'taihe';\n          sound.bitrate = `${data.data.rate}kbps`;\n\n          success(sound);\n        } else {\n          failure(sound);\n        }\n      });\n  }\n\n  static lyric(url) {\n    // eslint-disable-line no-unused-vars\n    const lyric_url = getParameterByName('lyric_url', url);\n\n    return {\n      success: (fn) => {\n        if (lyric_url) {\n          axios.get(lyric_url).then((response) =>\n            fn({\n              lyric: response.data,\n            })\n          );\n        } else {\n          const track_id = getParameterByName('track_id', url).split('_').pop();\n          axiosTH\n            .get('/song/tracklink', {\n              params: {\n                TSID: track_id,\n              },\n            })\n            .then((response) => {\n              axios.get(response.data.data.lyric).then((res) =>\n                fn({\n                  lyric: res.data,\n                })\n              );\n            });\n        }\n      },\n    };\n  }\n\n  static th_album(url) {\n    return {\n      success: (fn) => {\n        const album_id = getParameterByName('list_id', url).split('_').pop();\n\n        axiosTH\n          .get('/album/info', {\n            params: {\n              albumAssetCode: album_id,\n            },\n          })\n          .then((response) => {\n            const { data } = response.data;\n            const info = {\n              cover_img_url: data.pic,\n              title: data.title,\n              id: `thalbum_${album_id}`,\n              source_url: `https://music.taihe.com/album/${album_id}`,\n            };\n\n            const tracks = data.trackList.map((song) => ({\n              id: `thtrack_${song.assetId}`,\n              title: song.title,\n              artist: song.artist ? song.artist[0].name : '',\n              artist_id: song.artist\n                ? `thartist_${song.artist[0].artistCode}`\n                : 'thartist_',\n              album: info.title,\n              album_id: `thalbum_${album_id}`,\n              source: 'taihe',\n              source_url: `https://music.taihe.com/song/${song.assetId}`,\n              img_url: info.cover_img_url,\n              lyric_url: '',\n            }));\n            return fn({\n              tracks,\n              info,\n            });\n          });\n      },\n    };\n  }\n\n  static show_playlist(url) {\n    const offset = Number(getParameterByName('offset', url));\n    const subCate = getParameterByName('filter_id', url);\n    return {\n      success: (fn) => {\n        axiosTH\n          .get('/tracklist/list', {\n            params: {\n              pageNo: offset / 25 + 1,\n              pageSize: 25,\n              subCateId: subCate,\n            },\n          })\n          .then((response) => {\n            const { data } = response.data;\n            const result = data.result.map((item) => ({\n              cover_img_url: item.pic,\n              title: item.title,\n              id: `thplaylist_${item.id}`,\n              source_url: `https://music.taihe.com/songlist/${item.id}`,\n            }));\n\n            return fn({\n              result,\n            });\n          });\n      },\n    };\n  }\n\n  static parse_url(url) {\n    let result;\n    let id = '';\n    let match = /\\/\\/music.taihe.com\\/([a-z]+)\\//.exec(url);\n    if (match) {\n      switch (match[1]) {\n        case 'songlist':\n          match = /\\/\\/music.taihe.com\\/songlist\\/([0-9]+)/.exec(url);\n          id = match ? `thplaylist_${match[1]}` : '';\n          break;\n        case 'artist':\n          match = /\\/\\/music.taihe.com\\/artist\\/(A[0-9]+)/.exec(url);\n          id = match ? `thartist_${match[1]}` : '';\n          break;\n        case 'album':\n          match = /\\/\\/music.taihe.com\\/album\\/(P[0-9]+)/.exec(url);\n          id = match ? `thalbum_${match[1]}` : '';\n          break;\n        default:\n          break;\n      }\n      result = {\n        type: 'playlist',\n        id,\n      };\n    }\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'thplaylist':\n        return this.th_get_playlist(url);\n      case 'thalbum':\n        return this.th_album(url);\n      case 'thartist':\n        return this.th_artist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => {\n        axiosTH.get('/tracklist/category').then((res) =>\n          fn({\n            recommend: [{ id: '', name: '推荐歌单' }],\n            all: res.data.data.map((sub) => ({\n              category: sub.categoryName,\n              filters: sub.subCate.map((i) => ({\n                id: i.id,\n                name: i.categoryName,\n              })),\n            })),\n          })\n        );\n      },\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => {\n        fn({ status: 'fail', data: {} });\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://music.taihe.com`;\n  }\n\n  static logout() {}\n}\n"
  },
  {
    "path": "js/provider/xiami.js",
    "content": "/* eslint-disable radix */\n/* eslint-disable no-use-before-define */\n/* global getParameterByName */\n/* eslint-disable no-param-reassign */\n// eslint-disable-next-line no-unused-vars\nclass xiami {\n  static show_playlist() {\n    return {\n      success: (fn) =>\n        fn({\n          result: [],\n        }),\n    };\n  }\n\n  // eslint-disable-next-line no-unused-vars\n  static bootstrap_track(track, success, failure) {\n    const sound = {};\n    failure(sound);\n  }\n\n  static xm_get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_').pop();\n    return {\n      success: (fn) =>\n        fn({\n          tracks: [],\n          info: {\n            cover_img_url: '',\n            title: '',\n            id: `xmplaylist_${list_id}`,\n            source_url: `https://www.xiami.com/collect/${list_id}`,\n          },\n        }),\n    };\n  }\n\n  static xm_search(url) {\n    const searchType = getParameterByName('type', url);\n\n    return {\n      success: (fn) =>\n        fn({\n          result: [],\n          total: 0,\n          type: searchType,\n        }),\n    };\n  }\n\n  static xm_album(url) {\n    return {\n      success: (fn) => {\n        const album_id = getParameterByName('list_id', url).split('_').pop();\n\n        return fn({\n          tracks: [],\n          info: {\n            cover_img_url: '',\n            title: album_id,\n            id: `xmalbum_${album_id}`,\n            source_url: `https://www.xiami.com/album/${album_id}`,\n          },\n        });\n      },\n    };\n  }\n\n  static xm_artist(url) {\n    return {\n      success: (fn) => {\n        const artist_id = getParameterByName('list_id', url).split('_').pop();\n\n        return fn({\n          tracks: [],\n          info: {\n            cover_img_url: '',\n            title: artist_id,\n            id: `xmartist_${artist_id}`,\n            source_url: `https://www.xiami.com/artist/${artist_id}`,\n          },\n        });\n      },\n    };\n  }\n\n  static lyric() {\n    return {\n      success: (fn) =>\n        fn({\n          lyric: '',\n          tlyric: '',\n        }),\n    };\n  }\n\n  static parse_url() {\n    let result;\n    return {\n      success: (fn) => {\n        fn(result);\n      },\n    };\n  }\n\n  static get_playlist(url) {\n    const list_id = getParameterByName('list_id', url).split('_')[0];\n    switch (list_id) {\n      case 'xmplaylist':\n        return this.xm_get_playlist(url);\n      case 'xmalbum':\n        return this.xm_album(url);\n      case 'xmartist':\n        return this.xm_artist(url);\n      default:\n        return null;\n    }\n  }\n\n  static get_playlist_filters() {\n    return {\n      success: (fn) => fn({ recommend: [], all: [] }),\n    };\n  }\n\n  static get_user() {\n    return {\n      success: (fn) => {\n        fn({ status: 'fail', data: {} });\n      },\n    };\n  }\n\n  static get_login_url() {\n    return `https://www.xiami.com`;\n  }\n\n  static logout() {}\n\n  // return {\n  //   show_playlist: xm_show_playlist,\n  //   get_playlist_filters,\n  //   get_playlist,\n  //   parse_url: xm_parse_url,\n  //   bootstrap_track: xm_bootstrap_track,\n  //   search: xm_search,\n  //   lyric: xm_lyric,\n  //   get_user: xm_get_user,\n  //   get_login_url: xm_get_login_url,\n  //   logout: xm_logout,\n  // };\n}\n"
  },
  {
    "path": "listen1.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"listenone\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags-->\n    <meta name=\"description\" content=\"\" />\n    <meta name=\"author\" content=\"\" />\n    <title ng-bind=\"document_title\">Listen 1</title>\n\n    <link href=\"css/notyf.min.css\" rel=\"stylesheet\" />\n    <link href=\"css/notyf_custom.css\" rel=\"stylesheet\" />\n\n    <link href=\"css/hotkeys.css\" rel=\"stylesheet\" />\n    <link href=\"css/icon.css\" rel=\"stylesheet\" />\n\n    <link href=\"css/origin.css\" rel=\"stylesheet\" id=\"theme-css\" />\n    <link href=\"css/common.css\" rel=\"stylesheet\" id=\"common-css\"/>\n\n    <link href=\"images/logo_16.png\" rel=\"shortcut icon\" />\n    <link href=\"images/logo_16.png\" rel=\"bookmark\" />\n    <script>\n      if (typeof module === 'object') {\n        window.module = module;\n        module = undefined;\n      }\n    </script>\n    \n    <script type=\"text/javascript\" src=\"js/vendor/angular.min.js\"></script>\n    <script src=\"js/vendor/i18next.min.js\"></script>\n    <script src=\"js/vendor/i18nextHttpBackend.min.js\"></script>\n    <script\n      type=\"text/javascript\"\n      src=\"js/vendor/forge_listen1_fork.min.js\"\n    ></script>\n    <script type=\"text/javascript\" src=\"js/vendor/axios.min.js\"></script>\n    <script type=\"text/javascript\" src=\"js/vendor/notyf.min.js\"></script>\n    <script type=\"text/javascript\" src=\"js/vendor/howler.core.min.js\"></script>\n    <script type=\"text/javascript\" src=\"js/vendor/hotkeys.min.js\"></script>\n    <script type=\"text/javascript\" src=\"js/vendor/async.min.js\"></script>\n    <script type=\"text/javascript\" src=\"js/vendor/lru-cache.min.js\"></script>\n\n    <script type=\"text/javascript\" src=\"js/lowebutil.js\"></script>\n    <script type=\"text/javascript\" src=\"js/github.js\"></script>\n    <script type=\"text/javascript\" src=\"js/lastfm.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/xiami.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/qq.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/netease.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/kugou.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/kuwo.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/bilibili.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/migu.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/taihe.js\"></script>\n    <script type=\"text/javascript\" src=\"js/provider/localmusic.js\"></script>\n\n    <script type=\"text/javascript\" src=\"js/bridge.js\"></script>\n    <script type=\"text/javascript\" src=\"js/player_thread.js\"></script>\n    <script type=\"text/javascript\" src=\"js/myplaylist.js\"></script>\n    <script type=\"text/javascript\" src=\"js/loweb.js\"></script>\n    <script type=\"text/javascript\" src=\"js/l1_player.js\"></script>\n    <script type=\"text/javascript\" src=\"js/app.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/profile.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/auth.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/navigation.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/my_playlist.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/platform.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/playlist.js\"></script>\n    <script type=\"text/javascript\" src=\"js/controller/play.js\"></script>\n    <script\n      type=\"text/javascript\"\n      src=\"js/controller/instant_search.js\"\n    ></script>\n    <script>\n      if (window.module) module = window.module;\n    </script>\n\n  </head>\n\n  <body>\n    <div ng-controller=\"ProfileController\" ng-init=\"initProfile()\">\n      <div class=\"body\" ng-if=\"theme==='white' || theme==='black' \">\n        <div\n        id=\"feather-container\"\n        style=\"visibility: hidden; position: absolute; width: 0px; height: 0px\"\n      ></div>\n        <div\n          ng-controller=\"PlayController as playCtrl\"\n          ng-init=\"loadLocalSettings()\"\n        >\n          <div ng-controller=\"AuthController\" ng-init=\"refreshAuthStatus()\">\n            <div>\n              <div ng-controller=\"NavigationController\">\n                <div class=\"wrap\">\n                  <!-- dialog-->\n                  <div class=\"shadow\" ng-hide=\"is_dialog_hidden==1\"></div>\n                  <div\n                    class=\"dialog\"\n                    ng-hide=\"is_dialog_hidden==1\"\n                    ng-style=\"myStyle\"\n                  >\n                    <div class=\"dialog-header\">\n                      <span>{{ dialog_title }}</span\n                      ><span class=\"dialog-close\" ng-click=\"closeDialog()\">×</span>\n                    </div>\n                    <div class=\"dialog-body\">\n                      <!-- choose playlist dialog-->\n                      <ul class=\"dialog-playlist\" ng-show=\"dialog_type==0\">\n                        <li class=\"detail-add\" ng-click=\"newDialogOption(1)\">\n                          <img src=\"images/mycover.jpg\" />\n                          <h2>{{_CREATE_PLAYLIST}}</h2>\n                        </li>\n                        <li\n                          ng-repeat=\"playlist in myplaylist track by $index\"\n                          ng-class-odd=\"'odd'\"\n                          ng-class-even=\"'even'\"\n                          ng-click=\"chooseDialogOption(playlist.info.id)\"\n                        >\n                          <img ng-src=\"{{ playlist.info.cover_img_url }}\" />\n                          <h2>{{ playlist.info.title }}</h2>\n                        </li>\n                      </ul>\n                      <!-- create new playlist dialog-->\n                      <div class=\"dialog-newplaylist\" ng-show=\"dialog_type==1\">\n                        <input\n                          class=\"form-control\"\n                          type=\"text\"\n                          placeholder=\"{{_INPUT_NEW_PLAYLIST_TITLE}}\"\n                          ng-model=\"newlist_title\"\n                        />\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"createAndAddPlaylist()\"\n                          >\n                            {{_CONFIRM}}\n                          </button>\n                          <button\n                            class=\"btn btn-default\"\n                            ng-click=\"cancelNewDialog(0)\"\n                          >\n                            {{_CANCEL}}\n                          </button>\n                        </div>\n                      </div>\n                      <!-- edit playlist dialog-->\n                      <div class=\"dialog-editplaylist\" ng-show=\"dialog_type==3\">\n                        <div class=\"form-group\">\n                          <label>{{_PLAYLIST_TITLE}}</label>\n                          <input\n                            class=\"form-control\"\n                            type=\"text\"\n                            placeholder=\"{{_INPUT_PLAYLIST_TITLE}}\"\n                            ng-model=\"dialog_playlist_title\"\n                          />\n                        </div>\n                        <div class=\"form-group\">\n                          <label>{{_PLAYLIST_COVER_IMAGE_URL}}</label>\n                          <input\n                            class=\"form-control\"\n                            type=\"text\"\n                            placeholder=\"{{_INPUT_PLAYLIST_COVER_IMAGE_URL}}\"\n                            ng-model=\"dialog_cover_img_url\"\n                          />\n                        </div>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"editMyPlaylist(list_id)\"\n                          >\n                            {{_CONFIRM}}\n                          </button>\n                          <button class=\"btn btn-default\" ng-click=\"closeDialog()\">\n                            {{_CANCEL}}\n                          </button>\n                        </div>\n                        <div class=\"dialog-footer\">\n                          <button\n                            class=\"btn btn-danger remove-button\"\n                            ng-click=\"removeMyPlaylist(list_id)\"\n                          >\n                            {{_REMOVE_PLAYLIST}}\n                          </button>\n                        </div>\n                      </div>\n                      <div class=\"dialog-connect-lastfm\" ng-show=\"dialog_type==4\">\n                        <p>{{_OPENING_LASTFM_PAGE}}</p>\n                        <p>{{_CONFIRM_NOTICE_LASTFM}}</p>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"lastfm.updateStatus();closeDialog();\"\n                          >\n                            {{_AUTHORIZED_FINISHED}}\n                          </button>\n                          <button\n                            class=\"btn btn-warning warning-button\"\n                            ng-click=\"lastfm.getAuth();\"\n                          >\n                            {{_AUTHORIZED_REOPEN}}\n                          </button>\n                        </div>\n                      </div>\n                      <!-- open playlist dialog-->\n                      <div class=\"dialog-open-url\" ng-show=\"dialog_type==5\">\n                        <div class=\"form-group\">\n                          <label>{{_PLAYLIST_LINK}}</label>\n                          <input\n                            class=\"form-control\"\n                            type=\"text\"\n                            placeholder=\"{{_EXAMPLE}}https://www.xiami.com/collect/198267231\"\n                            ng-model=\"dialog_url\"\n                          />\n                        </div>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"openUrl(dialog_url);closeDialog();dialog_url='';\"\n                          >\n                            {{_CONFIRM}}\n                          </button>\n                          <button class=\"btn btn-default\" ng-click=\"closeDialog()\">\n                            {{_CANCEL}}\n                          </button>\n                        </div>\n                      </div>\n                      <ul class=\"dialog-merge-playlist\" ng-show=\"dialog_type==6\">\n                        <li\n                          ng-repeat=\"playlist in myplaylist track by $index\"\n                          ng-class-odd=\"'odd'\"\n                          ng-class-even=\"'even'\"\n                          ng-click=\"mergePlaylist(playlist.info.id)\"\n                        >\n                          <img ng-src=\"{{ playlist.info.cover_img_url }}\" />\n                          <h2>{{ playlist.info.title }}</h2>\n                        </li>\n                      </ul>\n                      <div class=\"dialog-connect-github\" ng-show=\"dialog_type==7\">\n                        <p>{{_OPENING_GITHUB_PAGE}}</p>\n                        <p>{{_CONFIRM_NOTICE_GITHUB}}</p>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"updateGithubStatus();closeDialog();\"\n                          >\n                            {{_AUTHORIZED_FINISHED}}\n                          </button>\n                          <button\n                            class=\"btn btn-warning warning-button\"\n                            ng-click=\"openGithubAuth();\"\n                          >\n                            {{_AUTHORIZED_REOPEN}}\n                          </button>\n                        </div>\n                      </div>\n                      <ul class=\"dialog-backuplist\" ng-show=\"dialog_type==8\">\n                        <li class=\"detail-add\" ng-click=\"newDialogOption(9)\">\n                          <img src=\"images/mycover.jpg\" />\n                          <h2>{{_CREATE_PLAYLIST_BACKUP}}</h2>\n                        </li>\n                        <li\n                          ng-repeat=\"backup in myBackup track by $index\"\n                          ng-class-odd=\"'odd'\"\n                          ng-class-even=\"'even'\"\n                          ng-click=\"backupMySettings2Gist(backup.id, backup.public); closeDialog();\"\n                        >\n                          <img ng-src=\"images/mycover.jpg\" />\n                          <h2>\n                            {{ backup.id }}<br />\n                            {{backup.description}}\n                          </h2>\n                        </li>\n                      </ul>\n                      <!-- create new backup dialog-->\n                      <div class=\"dialog-newbackup\" ng-show=\"dialog_type==9\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"backupMySettings2Gist(null, true);closeDialog();\"\n                        >\n                          {{_CREATE_PUBLIC_BACKUP}}\n                        </button>\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"backupMySettings2Gist(null, false);closeDialog();\"\n                        >\n                          {{_CREATE_PRIVATE_BACKUP}}\n                        </button>\n                        <button\n                          class=\"btn btn-default\"\n                          ng-click=\"cancelNewDialog(8)\"\n                        >\n                          {{_CANCEL}}\n                        </button>\n                      </div>\n                      <ul class=\"dialog-backuplist\" ng-show=\"dialog_type==10\">\n                        <li\n                          ng-repeat=\"backup in myBackup track by $index\"\n                          ng-class-odd=\"'odd'\"\n                          ng-class-even=\"'even'\"\n                          ng-click=\"importMySettingsFromGist(backup.id); closeDialog();\"\n                        >\n                          <img ng-src=\"images/mycover.jpg\" />\n                          <h2>{{ backup.id }} {{backup.description}}</h2>\n                        </li>\n                      </ul>\n                      <div class=\"dialog-open-login\" ng-show=\"dialog_type==11\">\n                        <p>{{_LOGIN_DIALOG_NOTICE}}</p>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"closeDialog();refreshAuthStatus();\"\n                          >\n                            {{_LOGIN_SUCCESS}}\n                          </button>\n                          <button\n                            class=\"btn btn-warning warning-button\"\n                            ng-click=\"openLogin(dialog_data);\"\n                          >\n                            {{_LOGIN_FAIL_RETRY}}\n                          </button>\n                        </div>\n                      </div>\n                      <div class=\"dialog-proxy\" ng-show=\"dialog_type==12\">\n                        <select\n                          ng-options=\"mode.displayText for mode in proxyModes\"\n                          ng-model=\"proxyModeInput\"\n                          ng-change=\"changeProxyMode(proxyModeInput)\"\n                        ></select>\n                        <div\n                          ng-show=\"proxyModeInput.name=='custom'\"\n                          class=\"custom-proxy\"\n                        >\n                          <div class=\"rule-input\">\n                            <div class=\"field-name\">{{_PROTOCOL}}</div>\n                            <select\n                              ng-options=\"protocol for protocol in proxyProtocols\"\n                              ng-model=\"proxyProtocol\"\n                              ng-change=\"changeProxyProtocol(proxyProtocol)\"\n                            ></select>\n                            <div class=\"field-name\">{{_HOST}}</div>\n                            <input type=\"text\" id=\"proxy-rules-host\" />\n                            <div class=\"field-name\">{{_PORT}}</div>\n                            <input type=\"text\" id=\"proxy-rules-port\" />\n                          </div>\n                        </div>\n                        <div class=\"buttons\">\n                          <button\n                            class=\"btn btn-primary confirm-button\"\n                            ng-click=\"setProxyConfig();closeDialog();\"\n                          >\n                            {{_CONFIRM}}\n                          </button>\n                          <button\n                            class=\"btn btn-warning warning-button\"\n                            ng-click=\"closeDialog();\"\n                          >\n                            {{_CANCEL}}\n                          </button>\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n    \n                  <div class=\"main\" ng-controller=\"MyPlayListController\">\n                    <div class=\"sidebar\">\n                      <div class=\"flex-scroll-wrapper\">\n                        <div class=\"menu-control\"></div>\n                        <div class=\"menu-title\">\n                          <div class=\"title\">{{_PLATFORM_UNION}}</div>\n                        </div>\n                        <ul class=\"nav masthead-nav\">\n                          <li\n                            ng-class=\"{ 'active':(current_tag==2) && (window_url_stack.length ==0) }\"\n                            ng-click=\"showTag(2)\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <span class=\"icon li-featured-list\"></span\n                              ><a>{{_PLAYLISTS}}</a>\n                            </div>\n                          </li>\n                        </ul>\n                        <div\n                          ng-if=\"!isChrome || is_login('netease') || is_login('qq')\"\n                          class=\"menu-title\"\n                        >\n                          <div class=\"title\">{{_MY_MUSIC}}</div>\n                        </div>\n                        <ul class=\"nav masthead-nav\">\n                          <li\n                            ng-if=\"!isChrome\"\n                            ng-click=\"showPlaylist('lmplaylist_reserve')\"\n                            ng-class=\"{ 'active':window_type=='list' && ( '/playlist?list_id=lmplaylist_reserve' === getCurrentUrl() ) }\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <span class=\"icon li-featured-list\"></span\n                              ><a>{{_LOCAL_MUSIC}}</a>\n                            </div>\n                          </li>\n                          <li\n                            ng-if=\"is_login('netease')\"\n                            ng-click=\"showTag(6, {platform:'netease', user: musicAuth.netease});\"\n                            ng-class=\"{ 'active':(current_tag==6 && tag_params.platform=='netease') && (window_url_stack.length ==0) }\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <svg class=\"feather\">\n                                <use href=\"#globe\"></use>\n                              </svg>\n                              <a>{{_MY_NETEASE}}</a>\n                            </div>\n                          </li>\n                          <li\n                            ng-if=\"is_login('qq')\"\n                            ng-click=\"showTag(6, {platform:'qq', user: musicAuth.qq});\"\n                            ng-class=\"{ 'active':(current_tag==6 && tag_params.platform=='qq') && (window_url_stack.length ==0) }\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <svg class=\"feather\">\n                                <use href=\"#globe\"></use>\n                              </svg>\n                              <a>{{_MY_QQ}}</a>\n                            </div>\n                          </li>\n                        </ul>\n                        <div class=\"menu-title\" ng-init=\"loadMyPlaylist();\">\n                          <div class=\"title\">{{_CREATED_PLAYLIST}}</div>\n                          <svg class=\"feather icon\" ng-click=\"showDialog(5)\">\n                            <use href=\"#plus-square\"></use>\n                          </svg>\n                        </div>\n                        <ul class=\"nav masthead-nav\">\n                          <li\n                            ng-repeat=\"i in myplaylists track by $index\"\n                            ng-class=\"{ 'active':window_type=='list' && ( ('/playlist?list_id='+i.info.id) === getCurrentUrl() ) }\"\n                            ng-click=\"showPlaylist(i.info.id)\"\n                            drag-drop-zone\n                            drag-zone-type=\"'application/listen1-myplaylist'\"\n                            drop-zone-ondrop=\"onSidebarPlaylistDrop('my', i.info.id, arg1, arg2, arg3)\"\n                            draggable=\"true\"\n                            sortable=\"true\"\n                            drag-zone-object=\"i\"\n                            drag-zone-title=\"i.info.title\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <svg class=\"feather\">\n                                <use href=\"#disc\"></use>\n                              </svg>\n                              <a>{{i.info.title}}</a>\n                            </div>\n                          </li>\n                        </ul>\n                        <div class=\"menu-title\" ng-init=\"loadFavoritePlaylist();\">\n                          <div class=\"title\">{{_FAVORITED_PLAYLIST}}</div>\n                        </div>\n                        <ul class=\"nav masthead-nav\">\n                          <li\n                            ng-repeat=\"i in favoriteplaylists track by $index\"\n                            ng-class=\"{ 'active':window_type=='list' && ( ('/playlist?list_id='+i.info.id) === getCurrentUrl() ) }\"\n                            ng-click=\"showPlaylist(i.info.id, {useCache: false})\"\n                            drag-drop-zone\n                            drag-zone-type=\"'application/listen1-favoriteplaylist'\"\n                            drop-zone-ondrop=\"onSidebarPlaylistDrop('favorite', i.info.id, arg1, arg2, arg3)\"\n                            draggable=\"true\"\n                            sortable=\"true\"\n                            drag-zone-object=\"i\"\n                            drag-zone-title=\"i.info.title\"\n                          >\n                            <div class=\"sidebar-block\">\n                              <svg class=\"feather\">\n                                <use href=\"#disc\"></use>\n                              </svg>\n                              <a>{{i.info.title}}</a>\n                            </div>\n                          </li>\n                        </ul>\n                      </div>\n                    </div>\n    \n                    <div class=\"content\" ng-controller=\"InstantSearchController\">\n                      <div class=\"navigation\">\n                        <div class=\"backfront\">\n                          <span class=\"icon li-back\" ng-click=\"popWindow()\"></span>\n                          <span\n                            class=\"icon li-advance\"\n                            ng-click=\"forwardWindow()\"\n                          ></span>\n                        </div>\n                        <div class=\"search\">\n                          <input\n                            class=\"form-control search-input\"\n                            id=\"search-input\"\n                            type=\"text\"\n                            ng-model=\"keywords\"\n                            placeholder=\"{{_SEARCH_PLACEHOLDER}}\"\n                            ng-model-options=\"{debounce: 500}\"\n                            ng-keyup=\"enterEvent($event)\"\n                          />\n                        </div>\n                        <div\n                          ng-class=\"{ 'active': (current_tag==4) && (window_url_stack.length ==0)}\"\n                          ng-click=\"showTag(5)\"\n                          class=\"settings\"\n                        >\n                          <span class=\"icon\">\n                            <svg class=\"feather\">\n                              <use href=\"#users\"></use>\n                            </svg>\n                          </span>\n                        </div>\n                        <div\n                          ng-class=\"{ 'active': (current_tag==4) && (window_url_stack.length ==0)}\"\n                          ng-click=\"showTag(4)\"\n                          class=\"settings\"\n                        >\n                          <span class=\"icon li-setting\"></span>\n                        </div>\n                        <div ng-if=\"!isChrome && !isMac\" class=\"window-control\">\n                          <svg class=\"icon\" window-control=\"window_min\">\n                            <use href=\"#minimize-2\"></use>\n                          </svg>\n                          <svg class=\"icon\" window-control=\"window_max\">\n                            <use href=\"#maximize\"></use>\n                          </svg>\n                          <svg class=\"icon\" window-control=\"window_close\">\n                            <use href=\"#x\"></use>\n                          </svg>\n                        </div>\n                      </div>\n                      <div\n                        class=\"browser flex-scroll-wrapper\"\n                        infinite-scroll=\"scrolling()\"\n                        content-selector=\"'#playlist-content'\"\n                      >\n                        <!-- hot playlist window-->\n                        <div\n                          class=\"page page-hot-playlist\"\n                          ng-show=\"current_tag==2 && is_window_hidden==1\"\n                          ng-controller=\"PlayListController\"\n                          ng-init=\"loadPlaylist();\"\n                        >\n                          <div class=\"source-list\" ng-show=\"is_window_hidden==1\">\n                            <div\n                              ng-repeat-start=\"source in ::sourceList\"\n                              class=\"source-button\"\n                              ng-class=\"{'active':tab === source.name}\"\n                              ng-click=\"changeTab(source.name)\"\n                            >\n                              {{source.displayText}}\n                            </div>\n                            <div\n                              ng-repeat-end\n                              ng-if=\"!$last\"\n                              class=\"splitter\"\n                            ></div>\n                          </div>\n                          <div class=\"playlist-filter\">\n                            <div\n                              class=\"l1-button filter-item\"\n                              ng-repeat=\"filter in playlistFilters[tab] || []\"\n                              ng-click=\"changeFilter(filter.id)\"\n                              ng-class=\"{'active':filter.id === currentFilterId}\"\n                            >\n                              {{filter.name}}\n                            </div>\n                            <div\n                              class=\"l1-button filter-item\"\n                              ng-show=\"playlistFilters[tab] && playlistFilters[tab].length > 0\"\n                              ng-click=\"toggleMorePlaylists()\"\n                            >\n                              更多...\n                            </div>\n                          </div>\n                          <div class=\"all-playlist-filter\" ng-show=\"showMore\">\n                            <div\n                              ng-repeat=\"category in allPlaylistFilters[tab] || []\"\n                              class=\"category\"\n                            >\n                              <div class=\"category-title\">\n                                {{category.category}}\n                              </div>\n                              <div class=\"category-filters\">\n                                <div\n                                  class=\"filter-item\"\n                                  ng-repeat=\"filter in category.filters\"\n                                >\n                                  <span ng-click=\"changeFilter(filter.id)\">\n                                    {{filter.name}}</span\n                                  >\n                                </div>\n                              </div>\n                            </div>\n                          </div>\n                          <div class=\"site-wrapper-innerd\" id=\"hotplaylist\">\n                            <div class=\"cover-container\" id=\"playlist-content\">\n                              <ul class=\"playlist-covers\">\n                                <li ng-repeat=\"i in result \">\n                                  <div class=\"u-cover\">\n                                    <img\n                                      ng-src=\"{{i.cover_img_url}}\"\n                                      ng-click=\"showPlaylist(i.id)\"\n                                    />\n                                    <div\n                                      class=\"bottom\"\n                                      ng-click=\"directplaylist(i.id)\"\n                                    >\n                                      <svg class=\"feather\">\n                                        <use href=\"#play-circle\"></use>\n                                      </svg>\n                                    </div>\n                                  </div>\n                                  <div class=\"desc\">\n                                    <span\n                                      class=\"title\"\n                                      ng-click=\"showPlaylist(i.id)\"\n                                      >{{i.title}}</span\n                                    >\n                                  </div>\n                                </li>\n                                <!-- <div class=\"loading_bottom\">\n                                  <img src=\"images/loading-1.gif\" height=\"40px\" />\n                                </div> -->\n                              </ul>\n                            </div>\n                          </div>\n                        </div>\n                        <!-- my platform window-->\n                        <div\n                          class=\"page page-hot-playlist\"\n                          ng-show=\"current_tag==6 && is_window_hidden==1\"\n                          ng-controller=\"PlatformController\"\n                        >\n                          <div class=\"source-list\" ng-show=\"is_window_hidden==1\">\n                            <div\n                              ng-repeat-start=\"source in ::platformSourceList\"\n                              class=\"source-button\"\n                              ng-class=\"{'active':tab === source.name}\"\n                              ng-click=\"changeTab(source.name)\"\n                            >\n                              {{source.displayText}}\n                            </div>\n                            <div\n                              ng-repeat-end\n                              ng-if=\"!$last\"\n                              class=\"splitter\"\n                            ></div>\n                          </div>\n                          <div class=\"site-wrapper-innerd\" id=\"hotplaylist\">\n                            <div class=\"cover-container\" id=\"playlist-content\">\n                              <ul class=\"playlist-covers\">\n                                <li ng-repeat=\"i in myPlatformPlaylists\">\n                                  <div class=\"u-cover\">\n                                    <img\n                                      ng-src=\"{{i.cover_img_url}}\"\n                                      ng-click=\"showPlaylist(i.id)\"\n                                    />\n                                    <div\n                                      class=\"bottom\"\n                                      ng-click=\"directplaylist(i.id)\"\n                                    >\n                                      <svg class=\"feather\">\n                                        <use href=\"#play-circle\"></use>\n                                      </svg>\n                                    </div>\n                                  </div>\n                                  <div class=\"desc\">\n                                    <span\n                                      class=\"title\"\n                                      ng-click=\"showPlaylist(i.id)\"\n                                      >{{i.title}}</span\n                                    >\n                                  </div>\n                                </li>\n                              </ul>\n                            </div>\n                          </div>\n                        </div>\n    \n                        <!-- content page: 快速搜索 -->\n                        <div\n                          class=\"page\"\n                          ng-show=\"current_tag==3 && is_window_hidden==1\"\n                        >\n                          <div class=\"site-wrapper-innerd\">\n                            <div class=\"cover-container\">\n                              <!-- Initialize a new AngularJS app and associate it with a module named \"instantSearch\"-->\n                              <div class=\"searchbox\">\n                                <ul class=\"source-list\">\n                                  <li\n                                    class=\"source-button\"\n                                    ng-class=\"{'active':tab === 'allmusic'}\"\n                                    ng-click=\"changeSourceTab('allmusic')\"\n                                  >\n                                    <a>{{_ALL_MUSIC}}(Beta)</a>\n                                  </li>\n                                  <div class=\"splitter\"></div>\n                                  <div\n                                    ng-repeat-start=\"source in ::sourceList\"\n                                    class=\"source-button\"\n                                    ng-class=\"{'active':tab === source.name}\"\n                                    ng-click=\"changeSourceTab(source.name)\"\n                                  >\n                                    {{source.displayText}}\n                                  </div>\n                                  <div\n                                    ng-repeat-end\n                                    ng-if=\"!$last\"\n                                    class=\"splitter\"\n                                  ></div>\n                                  <svg\n                                    class=\"searchspinner\"\n                                    ng-show=\"loading\"\n                                    version=\"1.1\"\n                                    id=\"loader-1\"\n                                    xmlns=\"http://www.w3.org/2000/svg\"\n                                    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n                                    x=\"0px\"\n                                    y=\"0px\"\n                                    width=\"40px\"\n                                    height=\"40px\"\n                                    viewBox=\"0 0 40 40\"\n                                    enable-background=\"new 0 0 40 40\"\n                                    xml:space=\"preserve\"\n                                  >\n                                    <path\n                                      opacity=\"0.2\"\n                                      fill=\"#000\"\n                                      d=\"M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946 s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634 c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z\"\n                                    />\n                                    <path\n                                      fill=\"#000\"\n                                      d=\"M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0 C22.32,8.481,24.301,9.057,26.013,10.047z\"\n                                    >\n                                      <animateTransform\n                                        attributeType=\"xml\"\n                                        attributeName=\"transform\"\n                                        type=\"rotate\"\n                                        from=\"0 20 20\"\n                                        to=\"360 20 20\"\n                                        dur=\"0.6s\"\n                                        repeatCount=\"indefinite\"\n                                      />\n                                    </path>\n                                  </svg>\n                                  <div class=\"search-type\">\n                                    <li\n                                      class=\"source-button\"\n                                      ng-class=\"{'active':isSearchType(0)}\"\n                                      ng-click=\"changeSearchType(0)\"\n                                    >\n                                      <a>单曲</a>\n                                    </li>\n                                    <div class=\"splitter\"></div>\n                                    <li\n                                      class=\"source-button\"\n                                      ng-class=\"{'active':isSearchType(1)}\"\n                                      ng-click=\"changeSearchType(1)\"\n                                    >\n                                      <a>歌单</a>\n                                    </li>\n                                  </div>\n                                </ul>\n                                <ul class=\"detail-songlist\">\n                                  <li class=\"head\" ng-if=\"searchType===0 \">\n                                    <div class=\"title\"><a>{{_SONGS}}</a></div>\n                                    <div class=\"artist\"><a>{{_ARTISTS}}</a></div>\n                                    <div class=\"album\"><a>{{_ALBUMS}}</a></div>\n                                    <div class=\"tools\">{{_OPERATION}}</div>\n                                  </li>\n                                  <li class=\"head\" ng-if=\"searchType===1 \">\n                                    <div class=\"title\">\n                                      <a>{{_PLAYLIST_TITLE}}</a>\n                                    </div>\n                                    <div class=\"artist\">\n                                      <a>{{_PLAYLIST_AUTHOR}}</a>\n                                    </div>\n                                    <div class=\"album\">\n                                      <a>{{_PLAYLIST_SONG_COUNT}}</a>\n                                    </div>\n                                  </li>\n                                  <li\n                                    ng-if=\"searchType===0\"\n                                    ng-repeat=\"song in result\"\n                                    ng-class-odd=\"'odd'\"\n                                    ng-class-even=\"'even'\"\n                                    ng-mouseenter=\"options=true\"\n                                    ng-mouseleave=\"options=false\"\n                                    ng-dblclick=\"addAndPlay(song)\"\n                                  >\n                                    <div class=\"title\">\n                                      <!-- <a ng-if=\"song.disabled\" class=\"disabled\" ng-click=\"copyrightNotice()\">{{ song.title |limitTo:30}}</a> -->\n                                      <a add-and-play=\"song\"\n                                        ><span\n                                          ng-if=\"isActiveTab('allmusic')\"\n                                          class=\"source\"\n                                          >{{song.sourceName}}</span\n                                        >{{ song.title |limitTo:30}}</a\n                                      >\n                                    </div>\n                                    <div class=\"artist\">\n                                      <a ng-click=\"showPlaylist(song.artist_id)\"\n                                        >{{ song.artist |limitTo:20}}</a\n                                      >\n                                    </div>\n                                    <div class=\"album\">\n                                      <a ng-click=\"showPlaylist(song.album_id)\"\n                                        >{{ song.album |limitTo:30}}</a\n                                      >\n                                    </div>\n    \n                                    <div class=\"tools\">\n                                      <a\n                                        title=\"{{_ADD_TO_QUEUE}}\"\n                                        class=\"detail-add-button\"\n                                        add-without-play=\"song\"\n                                        ng-show=\"options\"\n                                        ><span class=\"icon li-add\"></span\n                                      ></a>\n                                      <a\n                                        title=\"{{_ADD_TO_PLAYLIST}}\"\n                                        class=\"detail-fav-button\"\n                                        ng-show=\"options\"\n                                        ng-click=\"showDialog(0, song)\"\n                                        ><span class=\"icon li-songlist\"></span\n                                      ></a>\n                                      <a\n                                        title=\"{{_REMOVE_FROM_PLAYLIST}}\"\n                                        class=\"detail-delete-button\"\n                                        ng-click=\"removeSongFromPlaylist(song, list_id)\"\n                                        ng-show=\"options && is_mine=='1' \"\n                                        ><span class=\"icon li-del\"></span\n                                      ></a>\n                                      <a\n                                        title=\"{{_ORIGIN_LINK}}\"\n                                        class=\"source-button\"\n                                        open-url=\"song.source_url\"\n                                        ng-show=\"options\"\n                                        ><span class=\"icon li-link\"></span\n                                      ></a>\n                                    </div>\n                                  </li>\n                                  <li\n                                    ng-if=\"searchType===1\"\n                                    ng-repeat=\"playlist in result\"\n                                    ng-class-odd=\"'odd'\"\n                                    ng-class-even=\"'even'\"\n                                    class=\"playlist-result\"\n                                  >\n                                    <div class=\"title\">\n                                      <a ng-click=\"showPlaylist(playlist.id)\">\n                                        <img\n                                          ng-src=\"{{ playlist.img_url }}\"\n                                          err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                        />\n                                        <div>\n                                          {{ playlist.title |limitTo:30}}<span\n                                            ng-if=\"isActiveTab('allmusic')\"\n                                            class=\"source playlist\"\n                                            >{{playlist.sourceName}}</span\n                                          >\n                                        </div>\n                                      </a>\n                                    </div>\n                                    <div class=\"artist\">\n                                      {{ playlist.author |limitTo:20}}\n                                    </div>\n                                    <div class=\"album\">\n                                      {{ playlist.count |limitTo:30}}\n                                    </div>\n                                  </li>\n                                </ul>\n                                <div\n                                  class=\"search-pagination\"\n                                  ng-show=\"totalpage>1\"\n                                  pagination\n                                ></div>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n    \n                        <!-- content page: 设置 -->\n                        <div\n                          class=\"page\"\n                          ng-show=\"current_tag==4 && is_window_hidden==1\"\n                          ng-init=\"lastfm.updateStatus(); updateGithubStatus();\"\n                        >\n                          <div class=\"site-wrapper-innerd\">\n                            <div class=\"cover-container\">\n                              <div class=\"settings-title\">\n                                <span>{{_LANGUAGE}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div>\n                                  <button\n                                    class=\"language-button\"\n                                    ng-click=\"setLang('zh-CN')\"\n                                  >\n                                    简体中文\n                                  </button>\n                                  <button\n                                    class=\"language-button\"\n                                    ng-click=\"setLang('zh-TC')\"\n                                  >\n                                    繁体中文\n                                  </button>\n                                  <button\n                                    class=\"language-button\"\n                                    ng-click=\"setLang('en-US')\"\n                                  >\n                                    English\n                                  </button>\n                                  <button\n                                    class=\"language-button\"\n                                    ng-click=\"setLang('fr-FR')\"\n                                  >\n                                    French\n                                  </button>\n                                  <button\n                                    class=\"language-button\"\n                                    ng-click=\"setLang('ko-KR')\"\n                                  >\n                                    Korean\n                                  </button>\n                                  <button \n                                    class=\"language-button\" \n                                    ng-click=\"setLang('pt-BR')\"\n                                  >\n                                    Brazilian Portuguese\n                                  </button>\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_THEME}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div>\n                                  <button\n                                    class=\"theme-button\"\n                                    ng-click=\"setTheme('white')\"\n                                  >\n                                    {{_THEME_WHITE}}\n                                  </button>\n                                  <button\n                                    class=\"theme-button\"\n                                    ng-click=\"setTheme('black')\"\n                                  >\n                                    {{_THEME_BLACK}}\n                                  </button>\n                                  <button\n                                  class=\"theme-button\"\n                                  ng-click=\"setTheme('white2')\"\n                                >\n                                  {{_THEME_MODERN_WHITE}}\n                                </button>\n                                <button\n                                  class=\"theme-button\"\n                                  ng-click=\"setTheme('black2')\"\n                                >\n                                  {{_THEME_MODERN_BLACK}}\n                                </button>\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_AUTO_CHOOSE_SOURCE}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div\n                                  class=\"shortcut\"\n                                  class=\"btn btn-primary confirm-button\"\n                                >\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableAutoChooseSource\"\n                                    ng-click=\"setAutoChooseSource(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableAutoChooseSource\"\n                                    ng-click=\"setAutoChooseSource(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{_AUTO_CHOOSE_SOURCE_NOTICE}}\n                                </div>\n                                <div\n                                  class=\"search-description\"\n                                  ng-show=\"enableAutoChooseSource\"\n                                >\n                                  {{_AUTO_CHOOSE_SOURCE_LIST}}\n                                </div>\n                                <div\n                                  class=\"search-source-list\"\n                                  ng-show=\"enableAutoChooseSource\"\n                                >\n                                  <div\n                                    ng-repeat=\"item in sourceList\"\n                                    class=\"search-source\"\n                                  >\n                                    <svg\n                                      class=\"feather\"\n                                      ng-show=\"autoChooseSourceList.indexOf(item.name) === -1\"\n                                      ng-click=\"enableSource(item.name)\"\n                                    >\n                                      <use href=\"#square\"></use>\n                                    </svg>\n                                    <svg\n                                      class=\"feather\"\n                                      ng-show=\"autoChooseSourceList.indexOf(item.name) > -1\"\n                                      ng-click=\"disableSource(item.name)\"\n                                    >\n                                      <use href=\"#check-square\"></use>\n                                    </svg>\n                                    {{item.displayText}}\n                                  </div>\n                                </div>\n                              </div>\n                              <div ng-if=\"isChrome\" class=\"settings-title\">\n                                <span\n                                  >{{_CLOSE_TAB_ACTION}}({{_VALID_AFTER_RESTART}})</span\n                                >\n                              </div>\n                              <div ng-if=\"isChrome\" class=\"settings-content\">\n                                <div class=\"shortcut\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableStopWhenClose\"\n                                    ng-click=\"setStopWhenClose(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableStopWhenClose\"\n                                    ng-click=\"setStopWhenClose(false)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  <span style=\"margin-right: 20px\"\n                                    >{{_QUIT_APPLICATION}}</span\n                                  >\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableStopWhenClose\"\n                                    ng-click=\"setStopWhenClose(false)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableStopWhenClose\"\n                                    ng-click=\"setStopWhenClose(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  <span> {{_MINIMIZE_TO_BACKGROUND}}</span>\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_NOWPLAYING_DISPLAY}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div class=\"shortcut\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableNowplayingCoverBackground\"\n                                    ng-click=\"setNowplayingCoverBackground(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableNowplayingCoverBackground\"\n                                    ng-click=\"setNowplayingCoverBackground(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{_NOWPLAYING_COVER_BACKGROUND_NOTICE}}\n                                </div>\n                                <div class=\"shortcut\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableNowplayingBitrate\"\n                                    ng-click=\"setNowplayingBitrate(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableNowplayingBitrate\"\n                                    ng-click=\"setNowplayingBitrate(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{_NOWPLAYING_BITRATE_NOTICE}}\n                                </div>\n                                <div class=\"shortcut\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableNowplayingPlatform\"\n                                    ng-click=\"setNowplayingPlatform(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableNowplayingPlatform\"\n                                    ng-click=\"setNowplayingPlatform(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{_NOWPLAYING_PLATFORM_NOTICE}}\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_LYRIC_DISPLAY}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div class=\"shortcut\" ng-if=\"!isChrome\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableLyricFloatingWindow\"\n                                    ng-click=\"openLyricFloatingWindow(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableLyricFloatingWindow\"\n                                    ng-click=\"openLyricFloatingWindow(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  <span ng-show=\"enableLyricFloatingWindow\"></span\n                                  >{{_SHOW_DESKTOP_LYRIC}}\n                                </div>\n                                <div class=\"shortcut\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableLyricTranslation\"\n                                    ng-click=\"toggleLyricTranslation()\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableLyricTranslation\"\n                                    ng-click=\"toggleLyricTranslation()\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  <span ng-show=\"enableLyricTranslation\"></span\n                                  >{{_SHOW_LYRIC_TRANSLATION}}\n                                </div>\n                                <div class=\"shortcut\" ng-if=\"!isChrome\">\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableLyricFloatingWindowTranslation\"\n                                    ng-click=\"toggleLyricFloatingWindowTranslation()\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableLyricFloatingWindowTranslation\"\n                                    ng-click=\"toggleLyricFloatingWindowTranslation()\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  <span\n                                    ng-show=\"enableLyricFloatingWindowTranslation\"\n                                  ></span\n                                  >{{_SHOW_DESKTOP_LYRIC_TRANSLATION}}\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_BACKUP_PLAYLIST}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <p>{{_BACKUP_WARNING}}</p>\n                                <div>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-click=\"backupMySettings()\"\n                                  >\n                                    {{_EXPORT_TO_LOCAL_FILE}}\n                                  </button>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-show=\"githubStatus == 2\"\n                                    ng-click=\"showDialog(8)\"\n                                  >\n                                    {{_EXPORT_TO_GITHUB_GIST}}\n                                  </button>\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_RECOVER_PLAYLIST}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <p>{{_RECOVER_WARNING}}</p>\n                                <label class=\"upload-button\" for=\"my-file-selector\">\n                                  <input\n                                    id=\"my-file-selector\"\n                                    type=\"file\"\n                                    style=\"display: none\"\n                                    ng-model=\"myuploadfiles\"\n                                    custom-on-change=\"importMySettings\"\n                                  />{{_RECOVER_FROM_LOCAL_FILE}}\n                                </label>\n                                <button\n                                  class=\"btn btn-warning confirm-button\"\n                                  ng-show=\"githubStatus == 2\"\n                                  ng-click=\"showDialog(10)\"\n                                >\n                                  {{_RECOVER_FROM_GITHUB_GIST}}\n                                </button>\n                              </div>\n    \n                              <div class=\"settings-title\">\n                                <span>{{_CONNECT_TO_GITHUB}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div>\n                                  <p>{{_STATUS}}：{{ githubStatusText }}</p>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-show=\"githubStatus == 0\"\n                                    ng-click=\"openGithubAuth(); showDialog(7);\"\n                                  >\n                                    {{_CONNECT_TO_GITHUB}}\n                                  </button>\n                                  <button\n                                    class=\"btn btn-warning confirm-button\"\n                                    ng-show=\"githubStatus == 1\"\n                                    ng-click=\"showDialog(7);\"\n                                  >\n                                    {{_RECONNECT}}\n                                  </button>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-show=\"githubStatus == 2\"\n                                    ng-click=\"GithubLogout();\"\n                                  >\n                                    {{_CANCEL_CONNECT}}\n                                  </button>\n                                </div>\n                              </div>\n    \n                              <div class=\"settings-title\">\n                                <span>{{_CONNECT_TO_LASTFM}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div>\n                                  <p>{{_STATUS}}：{{ lastfm.getStatusText() }}</p>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-show=\"!lastfm.isAuthRequested()\"\n                                    ng-click=\"lastfm.getAuth(); showDialog(4);\"\n                                  >\n                                    {{_CONNECT_TO_LASTFM}}\n                                  </button>\n                                  <button\n                                    class=\"btn btn-warning confirm-button\"\n                                    ng-show=\"lastfm.isAuthRequested() && !lastfm.isAuthorized()\"\n                                    ng-click=\"lastfm.getAuth(); showDialog(4);\"\n                                  >\n                                    {{_RECONNECT}}\n                                  </button>\n                                  <button\n                                    class=\"btn btn-primary confirm-button\"\n                                    ng-show=\"lastfm.isAuthRequested()\"\n                                    ng-click=\"lastfm.cancelAuth();\"\n                                  >\n                                    {{_CANCEL_CONNECT}}\n                                  </button>\n                                </div>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_SHORTCUTS}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <div class=\"shortcut_table\">\n                                  <div class=\"shortcut_table-header\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_SHORTCUTS_FUNCTION}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">\n                                      {{_SHORTCUTS}}\n                                    </div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      {{_GLOBAL_SHORTCUTS}}\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_PLAY_OR_PAUSE}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">p</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      Ctrl(Cmd) + Alt + {{_KEYBOARD_SPACE}}\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_PREVIOUS_TRACK}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">[</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      Ctrl(Cmd) + Alt + ←\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_NEXT_TRACK}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">]</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      Ctrl(Cmd) + Alt + →\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_VOLUME_UP}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">u</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      {{_SHORTCUTS_NOT_SET}}\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_VOLUME_DOWN}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">d</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      {{_SHORTCUTS_NOT_SET}}\n                                    </div>\n                                  </div>\n                                  <!-- <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      静音/取消静音\n                                    </div>\n                                    <div class=\"shortcut_table-key\">m</div>\n                                    <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                      全局快捷键\n                                    </div>\n                                  </div> -->\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{_QUICK_SEARCH}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">f</div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      {{_SHORTCUTS_NOT_SET}}\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      {{ZOOM_IN_OUT}}\n                                    </div>\n                                    <div class=\"shortcut_table-key\">\n                                      Ctrl(Cmd) + +/-\n                                    </div>\n                                    <div\n                                      ng-if=\"!isChrome\"\n                                      class=\"shortcut_table-globalkey\"\n                                    >\n                                      {{_SHORTCUTS_NOT_SET}}\n                                    </div>\n                                  </div>\n                                  <!-- <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      打开/关闭播放列表\n                                    </div>\n                                    <div class=\"shortcut_table-key\">l</div>\n                                    <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                      全局快捷键\n                                    </div>\n                                  </div>\n                                  <div class=\"shortcut_table-line\">\n                                    <div class=\"shortcut_table-function\">\n                                      切换播放模式\n                                    </div>\n                                    <div class=\"shortcut_table-key\">s</div>\n                                    <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                      全局快捷键\n                                    </div>\n                                  </div> -->\n                                </div>\n                                <div\n                                  class=\"shortcut\"\n                                  ng-if=\"!isChrome\"\n                                  class=\"btn btn-primary confirm-button\"\n                                >\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"!enableGlobalShortCut\"\n                                    ng-click=\"applyGlobalShortcut(true)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"enableGlobalShortCut\"\n                                    ng-click=\"applyGlobalShortcut(true)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{_GLOBAL_SHORTCUTS_NOTICE}}\n                                </div>\n                              </div>\n                              <div class=\"settings-title\" ng-if=\"!isChrome\">\n                                <span>{{_PROXY_CONFIG}}</span>\n                              </div>\n                              <div class=\"settings-content\" ng-if=\"!isChrome\">\n                                <span>{{_PROXY_CONFIG}}:</span>\n                                {{proxyMode.displayText}}\n                                <span ng-show=\"proxyMode.name=='custom'\"\n                                  >{{proxyRules}}</span\n                                >\n                                <button ng-click=\"showDialog(12)\">\n                                  {{_MODIFY}}\n                                </button>\n                              </div>\n                              <div class=\"settings-title\">\n                                <span>{{_ABOUT}}</span>\n                              </div>\n                              <div class=\"settings-content\">\n                                <p>\n                                  Listen 1 {{_HOMEPAGE}}:\n                                  <a\n                                    open-url=\"'https://listen1.github.io/listen1/'\"\n                                  >\n                                    https://listen1.github.io/listen1/\n                                  </a>\n                                </p>\n                                <p>Listen 1 {{_EMAIL}}: githublisten1@gmail.com</p>\n                                <p>\n                                  {{_FEEDBACK}}:\n                                  <a\n                                    ng-if=\"isChrome\"\n                                    open-url=\"'https://github.com/listen1/listen1_chrome_extension/issues'\"\n                                    >https://github.com/listen1/listen1_chrome_extension/issues</a\n                                  >\n                                  <a\n                                    ng-if=\"!isChrome\"\n                                    open-url=\"'https://github.com/listen1/listen1_desktop/issues'\"\n                                    >https://github.com/listen1/listen1_desktop/issues</a\n                                  >\n                                </p>\n                                <p>{{_DESIGNER}} ({{_THEME_WHITE}}): iparanoid </p>\n                                <p>{{_DESIGNER}} ({{_THEME_MODERN_BLACK}}, {{_THEME_MODERN_WHITE}}): 814959822, Antion</p>\n                                <p>{{_VERSION}}: v2.33.0 {{_LICENSE_NOTICE}}</p>\n                                <p ng-show='lastestVersion!=\"\"'>\n                                  {{_LATEST_VERSION}}: {{lastestVersion}}\n                                </p>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                        <!-- content page: 登录 -->\n                        <div\n                          class=\"page\"\n                          ng-show=\"current_tag==5 && is_window_hidden==1\"\n                        >\n                          <div class=\"login\">\n                            <div ng-repeat=\"source in loginSourceList\">\n                              <div ng-show=\"is_login(source)\">\n                                <div class=\"usercard\">\n                                  <img ng-src=\"{{musicAuth[source].avatar}}\" />\n                                  <div class=\"usercard-title\">\n                                    <div class=\"usercard-nickname\">\n                                      {{musicAuth[source].nickname}}\n                                    </div>\n                                    <div class=\"usercard-info\">{{source}}</div>\n                                  </div>\n                                  <button ng-click=\"logout(source)\">\n                                    {{_LOGOUT}}\n                                  </button>\n                                </div>\n                              </div>\n                              <div ng-show=\"!is_login(source)\">\n                                <div class=\"usercard\">\n                                  <img src=\"images/placeholder.png\" />\n    \n                                  <div class=\"usercard-title\">\n                                    <div class=\"usercard-nickname\">\n                                      {{_NOT_LOGIN_NICKNAME}}\n                                    </div>\n                                    <div class=\"usercard-info\">{{source}}</div>\n                                  </div>\n    \n                                  <button\n                                    ng-click=\" openLogin(source);showDialog(11, source);\"\n                                  >\n                                    {{_LOGIN}}\n                                  </button>\n                                </div>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n    \n                        <!-- track list window-->\n                        <div class=\"page\">\n                          <div\n                            class=\"playlist-detail\"\n                            ng-show=\"is_window_hidden!=1 && window_type=='list'\"\n                          >\n                            <div class=\"detail-head\">\n                              <div class=\"detail-head-cover\">\n                                <img\n                                  ng-src=\"{{ cover_img_url }}\"\n                                  err-src=\"https://y.gtimg.cn/mediastyle/global/img/singer_300.png\"\n                                />\n                              </div>\n                              <div class=\"detail-head-title\">\n                                <h2>{{ playlist_title }}</h2>\n                                <div class=\"playlist-button-list\">\n                                  <div class=\"playlist-button playadd-button\">\n                                    <div\n                                      class=\"play-list\"\n                                      ng-click=\"playMylist(list_id)\"\n                                    >\n                                      <span class=\"icon li-play-s\"></span>\n                                      {{_PLAY_ALL}}\n                                    </div>\n                                    <div\n                                      class=\"add-list\"\n                                      ng-click=\"addMylist(list_id)\"\n                                    >\n                                      <span class=\"icon li-add\"></span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button clone-button\"\n                                    ng-show=\"is_local\"\n                                    ng-click=\"addLocalMusic(list_id)\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <span class=\"icon li-songlist\"></span\n                                      ><span>{{_ADD_LOCAL_SONGS}}</span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button clone-button\"\n                                    ng-show=\"!is_mine && !is_local\"\n                                    ng-click=\"clonePlaylist(list_id)\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <span class=\"icon li-songlist\"></span\n                                      ><span>{{_ADD_TO_PLAYLIST}}</span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button edit-button\"\n                                    ng-show=\"is_mine && !is_local\"\n                                    ng-click=\"showDialog(3, {list_id: list_id, playlist_title: playlist_title, cover_img_url: cover_img_url})\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <svg class=\"feather\">\n                                        <use href=\"#edit\"></use>\n                                      </svg>\n                                      <span>{{_EDIT}}</span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button fav-button\"\n                                    ng-show=\"!is_mine && !is_local\"\n                                    ng-click=\"favoritePlaylist(list_id)\"\n                                  >\n                                    <div\n                                      class=\"play-list\"\n                                      ng-class=\"{'favorited':is_favorite,'notfavorite':!is_favorite}\"\n                                    >\n                                      <svg class=\"feather\">\n                                        <use href=\"#star\"></use>\n                                      </svg>\n                                      <span\n                                        >{{is_favorite?_FAVORITED:_FAVORITE}}</span\n                                      >\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button edit-button\"\n                                    ng-show=\"isChrome && is_favorite && !is_local\"\n                                    ng-click=\"closeWindow();showPlaylist(list_id)\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <span class=\"icon li-loop\"></span\n                                      ><span>{{_REFRESH_PLAYLIST}}</span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button edit-button\"\n                                    ng-show=\"!is_mine && !is_local\"\n                                    open-url=\"playlist_source_url\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <span class=\"icon li-link\"></span\n                                      ><span>{{_ORIGIN_LINK}}</span>\n                                    </div>\n                                  </div>\n                                  <div\n                                    class=\"playlist-button edit-button\"\n                                    ng-show=\"is_mine && !is_local\"\n                                    ng-click=\"showDialog(6)\"\n                                  >\n                                    <div class=\"play-list\">\n                                      <svg class=\"feather\">\n                                        <use href=\"#git-merge\"></use>\n                                      </svg>\n                                      <span>{{_IMPORT}}</span>\n                                    </div>\n                                  </div>\n                                </div>\n                              </div>\n                            </div>\n    \n                            <ul class=\"detail-songlist\">\n                              <div class=\"playlist-search\">\n                                <svg class=\"feather playlist-search-icon\">\n                                  <use href=\"#search\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather playlist-clear-icon\"\n                                  ng-show=\"playlistFilter.key!=''\"\n                                  ng-click=\"clearFilter()\"\n                                >\n                                  <use href=\"#x\"></use>\n                                </svg>\n                                <input\n                                  class=\"playlist-search-input\"\n                                  type=\"text\"\n                                  ng-model=\"playlistFilter.key\"\n                                  placeholder=\"{{_SEARCH_PLAYLIST}}\"\n                                />\n                              </div>\n                              <li class=\"head\">\n                                <div class=\"title\">\n                                  <a>{{_SONGS + '(' + songs.length + ')'}}</a>\n                                </div>\n                                <div class=\"artist\"><a>{{_ARTISTS}}</a></div>\n                                <div class=\"album\"><a>{{_ALBUMS}}</a></div>\n                                <div class=\"tools\">{{_OPERATION}}</div>\n                              </li>\n                              <li\n                                ng-repeat=\"song in songs | filter: fieldFilter track by $index\"\n                                ng-class-odd=\"'odd'\"\n                                ng-class-even=\"'even'\"\n                                ng-mouseenter=\"options=true\"\n                                ng-mouseleave=\"options=false\"\n                                ng-dblclick=\"addAndPlay(song)\"\n                                draggable=\"true\"\n                                drag-drop-zone\n                                drag-zone-object=\"song\"\n                                drag-zone-title=\"song.title\"\n                                sortable=\"is_mine || is_local\"\n                                drag-zone-type=\"'application/listen1-song'\"\n                                drop-zone-ondrop=\"onPlaylistSongDrop(list_id, song, arg1, arg2, arg3)\"\n                              >\n                                <div class=\"title\">\n                                  <!-- <a class=\"disabled\" ng-if=\"song.disabled\" ng-click=\"copyrightNotice()\">{{ song.title }}</a> -->\n                                  <a add-and-play=\"song\">{{ song.title }}</a>\n                                </div>\n                                <div class=\"artist\">\n                                  <a ng-click=\"showPlaylist(song.artist_id)\"\n                                    >{{ song.artist }}</a\n                                  >\n                                </div>\n                                <div class=\"album\">\n                                  <a ng-click=\"showPlaylist(song.album_id)\"\n                                    >{{ song.album }}</a\n                                  >\n                                </div>\n                                <div class=\"tools\">\n                                  <a\n                                    title=\"{{_ADD_TO_QUEUE}}\"\n                                    class=\"detail-add-button\"\n                                    add-without-play=\"song\"\n                                    ng-show=\"options\"\n                                    ><span class=\"icon li-add\"></span\n                                  ></a>\n                                  <a\n                                    title=\"{{_ADD_TO_PLAYLIST}}\"\n                                    class=\"detail-fav-button\"\n                                    ng-show=\"options\"\n                                    ng-click=\"showDialog(0, song)\"\n                                    ><span class=\"icon li-songlist\"></span\n                                  ></a>\n                                  <a\n                                    title=\"{{_REMOVE_FROM_PLAYLIST}}\"\n                                    class=\"detail-delete-button\"\n                                    ng-click=\"removeSongFromPlaylist(song, list_id)\"\n                                    ng-show=\"options && (is_mine=='1'||is_local) \"\n                                    ><span class=\"icon li-del\"></span\n                                  ></a>\n                                  <a\n                                    title=\"{{_ORIGIN_LINK}}\"\n                                    class=\"source-button\"\n                                    open-url=\"song.source_url\"\n                                    ng-show=\"options && !is_local\"\n                                    ><span class=\"icon li-link\"></span\n                                  ></a>\n                                </div>\n                              </li>\n                            </ul>\n                          </div>\n                          <!-- now playing window-->\n                          <div\n                            class=\"songdetail-wrapper\"\n                            ng-class=\"{slidedown: window_type!=='track', coverbg: enableNowplayingCoverBackground}\"\n                          >\n                            <div class=\"draggable-zone\"></div>\n                            <div\n                              ng-if=\"enableNowplayingCoverBackground\"\n                              class=\"bg\"\n                              ng-style=\"{'background-image': 'url(' + currentPlaying.img_url + ')'}\"\n                            ></div>\n                            <div\n                              class=\"translate-switch\"\n                              ng-click=\"toggleLyricTranslation()\"\n                              ng-class=\"{selected: enableLyricTranslation}\"\n                            >\n                              译\n                            </div>\n                            <div\n                              class=\"close\"\n                              ng-class=\"isMac? 'mac':'' \"\n                              ng-click=\"popWindow()\"\n                            >\n                              <svg class=\"icon\">\n                                <use href=\"#chevron-down\"></use>\n                              </svg>\n                            </div>\n    \n                            <div ng-if=\"!isChrome && !isMac\" class=\"window-control\">\n                              <svg class=\"icon\" window-control=\"window_min\">\n                                <use href=\"#minimize-2\"></use>\n                              </svg>\n                              <svg class=\"icon\" window-control=\"window_max\">\n                                <use href=\"#maximize\"></use>\n                              </svg>\n                              <svg class=\"icon\" window-control=\"window_close\">\n                                <use href=\"#x\"></use>\n                              </svg>\n                            </div>\n    \n                            <div class=\"playsong-detail\">\n                              <div class=\"detail-head\">\n                                <div class=\"detail-head-cover\">\n                                  <img\n                                    ng-src=\"{{ currentPlaying.img_url  }}\"\n                                    err-src=\"https://y.gtimg.cn/mediastyle/global/img/album_300.png\"\n                                  />\n                                </div>\n                                <div class=\"detail-head-title\">\n                                  <!--<a title=\"加入收藏\" class=\"clone\" ng-click=\"showDialog(0, currentPlaying)\">收藏</a>\n                    <a open-url=\"currentPlaying.source_url\" title=\"原始链接\" class=\"link\">原始链接</a>-->\n                                </div>\n                              </div>\n                              <div class=\"detail-songinfo\">\n                                <div class=\"title\">\n                                  <h2>{{ currentPlaying.title }}</h2>\n                                  <span\n                                    class=\"badge\"\n                                    ng-if=\"enableNowplayingBitrate && currentPlaying.bitrate !== undefined\"\n                                    >{{ currentPlaying.bitrate }}</span\n                                  >\n                                  <span\n                                    class=\"badge platform\"\n                                    ng-if=\"enableNowplayingPlatform && currentPlaying.platform !== undefined\"\n                                    >{{ currentPlaying.platformText }}</span\n                                  >\n                                </div>\n                                <div class=\"info\">\n                                  <div class=\"singer\">\n                                    <span>{{_ARTIST}}： </span\n                                    ><a\n                                      ng-click=\"showPlaylist(currentPlaying.artist_id)\"\n                                      title=\"{{currentPlaying.artist}}\"\n                                      >{{ currentPlaying.artist }}</a\n                                    >\n                                  </div>\n                                  <div class=\"album\">\n                                    <span>{{_ALBUM}}： </span\n                                    ><a\n                                      ng-click=\"showPlaylist(currentPlaying.album_id)\"\n                                      title=\"{{currentPlaying.album}}\"\n                                      >{{ currentPlaying.album }}</a\n                                    >\n                                  </div>\n                                </div>\n                                <div class=\"lyric\">\n                                  <div class=\"placeholder\"></div>\n                                  <p\n                                    ng-repeat=\"line in lyricArray track by $index\"\n                                    data-line=\"{{line.lineNumber}}\"\n                                    ng-class=\"{ 'highlight': (line.lineNumber == lyricLineNumber) || (line.lineNumber == lyricLineNumberTrans) , hide: (line.translationFlag && !enableLyricTranslation), translate: line.translationFlag}\"\n                                  >\n                                    {{ line.content }}\n                                  </p>\n                                  <div class=\"placeholder\"></div>\n                                </div>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n                  <div class=\"footer\">\n                    <div class=\"left-control\">\n                      <span class=\"icon li-previous\" prev-track=\"\"></span>\n                      <span\n                        class=\"icon li-play play\"\n                        ng-class=\"isPlaying? 'li-pause': 'li-play'\"\n                        play-pause-toggle=\"\"\n                      ></span>\n                      <span class=\"icon li-next\" next-track=\"\"></span>\n                    </div>\n                    <div class=\"main-info\">\n                      <div class=\"logo-banner\" ng-if=\"playlist.length == 0\">\n                        <svg\n                          class=\"logo\"\n                          xmlns=\"http://www.w3.org/2000/svg\"\n                          width=\"24\"\n                          height=\"24\"\n                          viewBox=\"0 0 24 24\"\n                          fill=\"#666666\"\n                          stroke=\"#666666\"\n                          stroke-width=\"1\"\n                          stroke-linecap=\"round\"\n                          stroke-linejoin=\"round\"\n                        >\n                          <polygon\n                            points=\"7 4 7 19 16 19 16 16 10 16 10 4\"\n                          ></polygon>\n                          <polygon points=\"13 4 13 13 16 13 16 4\"></polygon>\n                        </svg>\n                      </div>\n                      <div\n                        class=\"cover\"\n                        class=\"cover\"\n                        ng-if=\"playlist.length > 0\"\n                        ng-click=\"toggleNowPlaying()\"\n                      >\n                        <img\n                          ng-src=\"{{ currentPlaying.img_url }}\"\n                          err-src=\"https://y.gtimg.cn/mediastyle/global/img/album_300.png\"\n                        />\n                        <!-- Switching icon when hover -->\n                        <div class=\"mask\">\n                          <svg class=\"feather\" ng-if=\"getCurrentUrl() != '/now_playing'\">\n                            <use href=\"#chevrons-up\"></use>\n                          </svg>\n                          <svg class=\"feather\" ng-if=\"getCurrentUrl() === '/now_playing'\">\n                            <use href=\"#chevrons-down\"></use>\n                          </svg>\n                        </div>\n                      </div>\n                      <div class=\"detail\" ng-if=\"playlist.length > 0\">\n                        <div class=\"ctrl\">\n                          <a\n                            ng-click=\"showDialog(0, currentPlaying)\"\n                            title=\"{{_ADD_TO_PLAYLIST}}\"\n                          >\n                            <span class=\"icon li-songlist\"></span>\n                          </a>\n                          <a\n                            title=\"{{ settings.playmode | playmode_title }}(s)\"\n                            ng-click=\"changePlaymode()\"\n                          >\n                            <span\n                              ng-show=\"settings.playmode == 0\"\n                              class=\"icon li-loop\"\n                            ></span>\n                            <span\n                              ng-show=\"settings.playmode == 1\"\n                              class=\"icon li-random-loop\"\n                            ></span>\n                            <span\n                              ng-show=\"settings.playmode == 2\"\n                              class=\"icon li-single-cycle\"\n                            ></span>\n                          </a>\n                        </div>\n    \n                        <div class=\"title\">\n                          <span\n                            ng-if=\"currentPlaying.source === 'xiami'\"\n                            style=\"color: orange; font-size: medium\"\n                            >⚠️ </span\n                          >{{ currentPlaying.title }}\n                        </div>\n                        <div class=\"more-info\">\n                          <div class=\"current\">{{ currentPosition }}</div>\n                          <div class=\"singer\">\n                            <a ng-click=\"showPlaylist(currentPlaying.artist_id)\"\n                              >{{ currentPlaying.artist }}</a\n                            >\n                            -\n                            <a ng-click=\"showPlaylist(currentPlaying.album_id)\"\n                              >{{ currentPlaying.album }}</a\n                            >\n                          </div>\n                          <div class=\"total\">{{ currentDuration }}</div>\n                        </div>\n                        <div class=\"playbar\">\n                          <div class=\"playbar-clickable\">\n                            <div\n                              class=\"barbg\"\n                              id=\"progressbar\"\n                              mode=\"play\"\n                              draggable-bar=\"\"\n                            >\n                              <div\n                                class=\"cur\"\n                                ng-style=\"{width : myProgress + '%' }\"\n                              >\n                                <span class=\"btn\"><i></i></span>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                    <div class=\"right-control\">\n                      <div class=\"playlist-toggle\">\n                        <span\n                          ng-click=\"togglePlaylist()\"\n                          class=\"icon li-list\"\n                        ></span>\n                      </div>\n                      <div class=\"volume-ctrl\" volume-wheel=\"\">\n                        <span\n                          class=\"icon\"\n                          ng-class=\"mute? 'li-mute': 'li-volume'\"\n                          ng-click=\"toggleMuteStatus()\"\n                        ></span>\n                        <div class=\"m-pbar volume\">\n                          <div\n                            class=\"barbg\"\n                            id=\"volumebar\"\n                            mode=\"volume\"\n                            draggable-bar=\"\"\n                          >\n                            <div class=\"cur\" ng-style=\"{width : volume + '%' }\">\n                              <span class=\"btn\"><i></i></span>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                      <div ng-if=\"!isChrome\" class=\"lyric-toggle\">\n                        <div\n                          ng-click=\"openLyricFloatingWindow(true)\"\n                          class=\"lyric-icon\"\n                          ng-class=\"{'selected': enableLyricFloatingWindow}\"\n                        >\n                          词\n                        </div>\n                      </div>\n                    </div>\n                    <div\n                      class=\"menu-modal\"\n                      ng-class=\"{slideup: !menuHidden}\"\n                      ng-click=\"togglePlaylist()\"\n                    ></div>\n                    <div class=\"menu\" ng-class=\"{slideup: !menuHidden}\">\n                      <div class=\"menu-header\">\n                        <span class=\"menu-title\"\n                          >{{_TOTAL_SONG_PREFIX}}{{playlist.length}}{{_TOTAL_SONG_POSTFIX}}</span\n                        >\n                        <a class=\"add-all\" ng-click=\"showDialog(0, playlist)\">\n                          <span\n                            class=\"icon li-songlist\"\n                            ng-click=\"togglePlaylist()\"\n                          ></span>\n                          <span>{{_ADD_TO_PLAYLIST}}</span></a\n                        >\n                        <a class=\"remove-all\" clear-playlist=\"\"\n                          ><span\n                            class=\"icon li-del\"\n                            ng-click=\"togglePlaylist()\"\n                          ></span\n                          ><span>{{_CLEAR_ALL}}</span></a\n                        >\n    \n                        <a class=\"close\" ng-click=\"togglePlaylist()\"\n                          ><svg class=\"feather\">\n                            <use href=\"#x\"></use>\n                          </svg>\n                        </a>\n                      </div>\n                      <ul class=\"menu-list\">\n                        <li\n                          id=\"song{{ song.id }}\"\n                          ng-repeat=\"song in playlist track by $index\"\n                          ng-class=\"{ playing: currentPlaying.id == song.id }\"\n                          ng-mouseenter=\"playlist_highlight=true\"\n                          ng-mouseleave=\"playlist_highlight=false\"\n                          ng-dblclick=\"playById(song.id)\"\n                          ng-class-odd=\"'odd'\"\n                          ng-class-even=\"'even'\"\n                          draggable=\"true\"\n                          drag-drop-zone\n                          drag-zone-object=\"song\"\n                          drag-zone-title=\"song.title\"\n                          sortable=\"true\"\n                          drag-zone-type=\"'application/listen1-song'\"\n                          drop-zone-ondrop=\"onCurrentPlayingSongDrop(song, arg1, arg2, arg3)\"\n                        >\n                          <div class=\"song-status-icon\">\n                            <svg\n                              class=\"feather play\"\n                              ng-show=\"currentPlaying.id == song.id\"\n                            >\n                              <use href=\"#play\"></use>\n                            </svg>\n                          </div>\n                          <div\n                            class=\"song-title\"\n                            ng-class=\"song.disabled? 'disabled':'' \"\n                          >\n                            <a play-from-playlist=\"song\"\n                              ><span\n                                ng-if=\"song.source === 'xiami'\"\n                                style=\"\n                                  color: orange;\n                                  border-radius: 12px;\n                                  border: solid 1px;\n                                  padding: 0 4px;\n                                \"\n                                >⚠️ 🦐</span\n                              >{{ song.title }}</a\n                            >\n                          </div>\n                          <div class=\"song-singer\">\n                            <a\n                              ng-click=\"showPlaylist(song.artist_id); togglePlaylist();\"\n                              >{{ song.artist }}</a\n                            >\n                          </div>\n                          <div class=\"tools\">\n                            <span\n                              remove-from-playlist=\"song\"\n                              data-index=\"{{$index}}\"\n                              ng-show=\"playlist_highlight\"\n                              class=\"icon li-del\"\n                            ></span>\n                            <span\n                              open-url=\"song.source_url\"\n                              ng-show=\"playlist_highlight\"\n                              class=\"icon li-link\"\n                            ></span>\n                          </div>\n                          <!-- <div class=\"song-time\">00:00</div> -->\n                        </li>\n                      </ul>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div class=\"body\" ng-if=\"theme==='white2' || theme==='black2' \" >\n        <div\n        id=\"feather-container\"\n        style=\"visibility: hidden; position: absolute; width: 0px; height: 0px\"\n      ></div>\n      <div\n        ng-controller=\"PlayController as playCtrl\"\n        ng-init=\"loadLocalSettings()\"\n      >\n        <div ng-controller=\"AuthController\" ng-init=\"refreshAuthStatus()\">\n          <div >\n            <div ng-controller=\"NavigationController\">\n              <div class=\"wrap\">\n                <!-- dialog-->\n                <div class=\"shadow\" ng-hide=\"is_dialog_hidden==1\"></div>\n                <div\n                  class=\"dialog\"\n                  ng-hide=\"is_dialog_hidden==1\"\n                  ng-style=\"myStyle\"\n                >\n                  <div class=\"dialog-header\">\n                    <span>{{ dialog_title }}</span\n                    ><span class=\"dialog-close\" ng-click=\"closeDialog()\">×</span>\n                  </div>\n                  <div class=\"dialog-body\">\n                    <!-- choose playlist dialog-->\n                    <ul class=\"dialog-playlist\" ng-show=\"dialog_type==0\">\n                      <li class=\"detail-add\" ng-click=\"newDialogOption(1)\">\n                        <img src=\"images/mycover.jpg\" />\n                        <h2>{{_CREATE_PLAYLIST}}</h2>\n                      </li>\n                      <li\n                        ng-repeat=\"playlist in myplaylist track by $index\"\n                        ng-class-odd=\"'odd'\"\n                        ng-class-even=\"'even'\"\n                        ng-click=\"chooseDialogOption(playlist.info.id)\"\n                      >\n                        <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" ng-src=\"{{ playlist.info.cover_img_url }}\" />\n                        <h2>{{ playlist.info.title }}</h2>\n                      </li>\n                    </ul>\n                    <!-- create new playlist dialog-->\n                    <div class=\"dialog-newplaylist\" ng-show=\"dialog_type==1\">\n                      <input\n                        class=\"form-control\"\n                        type=\"text\"\n                        placeholder=\"{{_INPUT_NEW_PLAYLIST_TITLE}}\"\n                        ng-model=\"newlist_title\"\n                      />\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"createAndAddPlaylist()\"\n                        >\n                          {{_CONFIRM}}\n                        </button>\n                        <button\n                          class=\"btn btn-default\"\n                          ng-click=\"cancelNewDialog(0)\"\n                        >\n                          {{_CANCEL}}\n                        </button>\n                      </div>\n                    </div>\n                    <!-- edit playlist dialog-->\n                    <div class=\"dialog-editplaylist\" ng-show=\"dialog_type==3\">\n                      <div class=\"form-group\">\n                        <label>{{_PLAYLIST_TITLE}}</label>\n                        <input\n                          class=\"form-control\"\n                          type=\"text\"\n                          placeholder=\"{{_INPUT_PLAYLIST_TITLE}}\"\n                          ng-model=\"dialog_playlist_title\"\n                        />\n                      </div>\n                      <div class=\"form-group\">\n                        <label>{{_PLAYLIST_COVER_IMAGE_URL}}</label>\n                        <input\n                          class=\"form-control\"\n                          type=\"text\"\n                          placeholder=\"{{_INPUT_PLAYLIST_COVER_IMAGE_URL}}\"\n                          ng-model=\"dialog_cover_img_url\"\n                        />\n                      </div>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"editMyPlaylist(list_id)\"\n                        >\n                          {{_CONFIRM}}\n                        </button>\n                        <button class=\"btn btn-default\" ng-click=\"closeDialog()\">\n                          {{_CANCEL}}\n                        </button>\n                      </div>\n                      <div class=\"dialog-footer\">\n                        <button\n                          class=\"btn btn-danger remove-button\"\n                          ng-click=\"removeMyPlaylist(list_id)\"\n                        >\n                          {{_REMOVE_PLAYLIST}}\n                        </button>\n                      </div>\n                    </div>\n                    <div class=\"dialog-connect-lastfm\" ng-show=\"dialog_type==4\">\n                      <p>{{_OPENING_LASTFM_PAGE}}</p>\n                      <p>{{_CONFIRM_NOTICE_LASTFM}}</p>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"lastfm.updateStatus();closeDialog();\"\n                        >\n                          {{_AUTHORIZED_FINISHED}}\n                        </button>\n                        <button\n                          class=\"btn btn-warning warning-button\"\n                          ng-click=\"lastfm.getAuth();\"\n                        >\n                          {{_AUTHORIZED_REOPEN}}\n                        </button>\n                      </div>\n                    </div>\n                    <!-- open playlist dialog-->\n                    <div class=\"dialog-open-url\" ng-show=\"dialog_type==5\">\n                      <div class=\"form-group\">\n                        <label>{{_PLAYLIST_LINK}}</label>\n                        <input\n                          class=\"form-control\"\n                          type=\"text\"\n                          placeholder=\"{{_EXAMPLE}}https://www.xiami.com/collect/198267231\"\n                          ng-model=\"dialog_url\"\n                        />\n                      </div>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"openUrl(dialog_url);closeDialog();dialog_url='';\"\n                        >\n                          {{_CONFIRM}}\n                        </button>\n                        <button class=\"btn btn-default\" ng-click=\"closeDialog()\">\n                          {{_CANCEL}}\n                        </button>\n                      </div>\n                    </div>\n                    <ul class=\"dialog-merge-playlist\" ng-show=\"dialog_type==6\">\n                      <li\n                        ng-repeat=\"playlist in myplaylist track by $index\"\n                        ng-class-odd=\"'odd'\"\n                        ng-class-even=\"'even'\"\n                        ng-click=\"mergePlaylist(playlist.info.id)\"\n                      >\n                        <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" ng-src=\"{{ playlist.info.cover_img_url }}\" />\n                        <h2>{{ playlist.info.title }}</h2>\n                      </li>\n                    </ul>\n                    <div class=\"dialog-connect-github\" ng-show=\"dialog_type==7\">\n                      <p>{{_OPENING_GITHUB_PAGE}}</p>\n                      <p>{{_CONFIRM_NOTICE_GITHUB}}</p>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"updateGithubStatus();closeDialog();\"\n                        >\n                          {{_AUTHORIZED_FINISHED}}\n                        </button>\n                        <button\n                          class=\"btn btn-warning warning-button\"\n                          ng-click=\"openGithubAuth();\"\n                        >\n                          {{_AUTHORIZED_REOPEN}}\n                        </button>\n                      </div>\n                    </div>\n                    <ul class=\"dialog-backuplist\" ng-show=\"dialog_type==8\">\n                      <li class=\"detail-add\" ng-click=\"newDialogOption(9)\">\n                        <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" src=\"images/mycover.jpg\" />\n                        <h2>{{_CREATE_PLAYLIST_BACKUP}}</h2>\n                      </li>\n                      <li\n                        ng-repeat=\"backup in myBackup track by $index\"\n                        ng-class-odd=\"'odd'\"\n                        ng-class-even=\"'even'\"\n                        ng-click=\"backupMySettings2Gist(backup.id, backup.public); closeDialog();\"\n                      >\n                        <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" ng-src=\"images/mycover.jpg\" />\n                        <h2>\n                          {{ backup.id }}<br />\n                          {{backup.description}}\n                        </h2>\n                      </li>\n                    </ul>\n                    <!-- create new backup dialog-->\n                    <div class=\"dialog-newbackup\" ng-show=\"dialog_type==9\">\n                      <button\n                        class=\"btn btn-primary confirm-button\"\n                        ng-click=\"backupMySettings2Gist(null, true);closeDialog();\"\n                      >\n                        {{_CREATE_PUBLIC_BACKUP}}\n                      </button>\n                      <button\n                        class=\"btn btn-primary confirm-button\"\n                        ng-click=\"backupMySettings2Gist(null, false);closeDialog();\"\n                      >\n                        {{_CREATE_PRIVATE_BACKUP}}\n                      </button>\n                      <button\n                        class=\"btn btn-default\"\n                        ng-click=\"cancelNewDialog(8)\"\n                      >\n                        {{_CANCEL}}\n                      </button>\n                    </div>\n                    <ul class=\"dialog-backuplist\" ng-show=\"dialog_type==10\">\n                      <li\n                        ng-repeat=\"backup in myBackup track by $index\"\n                        ng-class-odd=\"'odd'\"\n                        ng-class-even=\"'even'\"\n                        ng-click=\"importMySettingsFromGist(backup.id); closeDialog();\"\n                      >\n                        <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" ng-src=\"images/mycover.jpg\" />\n                        <h2>{{ backup.id }} {{backup.description}}</h2>\n                      </li>\n                    </ul>\n                    <div class=\"dialog-open-login\" ng-show=\"dialog_type==11\">\n                      <p>{{_LOGIN_DIALOG_NOTICE}}</p>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"closeDialog();refreshAuthStatus();\"\n                        >\n                          {{_LOGIN_SUCCESS}}\n                        </button>\n                        <button\n                          class=\"btn btn-warning warning-button\"\n                          ng-click=\"openLogin(dialog_data);\"\n                        >\n                          {{_LOGIN_FAIL_RETRY}}\n                        </button>\n                      </div>\n                    </div>\n                    <div class=\"dialog-proxy\" ng-show=\"dialog_type==12\">\n                      <select\n                        ng-options=\"mode.displayText for mode in proxyModes\"\n                        ng-model=\"proxyModeInput\"\n                        ng-change=\"changeProxyMode(proxyModeInput)\"\n                      ></select>\n                      <div\n                        ng-show=\"proxyModeInput.name=='custom'\"\n                        class=\"custom-proxy\"\n                      >\n                        <div class=\"rule-input\">\n                          <div class=\"field-name\">{{_PROTOCOL}}</div>\n                          <select\n                            ng-options=\"protocol for protocol in proxyProtocols\"\n                            ng-model=\"proxyProtocol\"\n                            ng-change=\"changeProxyProtocol(proxyProtocol)\"\n                          ></select>\n                          <div class=\"field-name\">{{_HOST}}</div>\n                          <input type=\"text\" id=\"proxy-rules-host\" />\n                          <div class=\"field-name\">{{_PORT}}</div>\n                          <input type=\"text\" id=\"proxy-rules-port\" />\n                        </div>\n                      </div>\n                      <div class=\"buttons\">\n                        <button\n                          class=\"btn btn-primary confirm-button\"\n                          ng-click=\"setProxyConfig();closeDialog();\"\n                        >\n                          {{_CONFIRM}}\n                        </button>\n                        <button\n                          class=\"btn btn-warning warning-button\"\n                          ng-click=\"closeDialog();\"\n                        >\n                          {{_CANCEL}}\n                        </button>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n\n                <div class=\"main\" ng-controller=\"MyPlayListController\">\n                  <div class=\"sidebar\">\n                    <div class=\"flex-scroll-wrapper\">\n                      <div class=\"menu-control\"></div>\n                        <div ng-class=\"{'opensidebar':isOpenSidebar,footerdef:playlist.length == 0}\" class=\"sidebar-content\">\n                          <div class=\"logo-content\" ng-click=\"openSidebar()\">\n                            <div class=\"logo-svg\" >\n                              <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"512\" height=\"512.024\" viewBox=\"0 0 780.964 781\">\n                                <path fill=\"currentColor\" d=\"M415,414H595v594H907v186H415V414Zm283,0H884V910H698V414Z\" transform=\"translate(-270.518 -413.5)\"/>\n                              </svg>\n                          </div>\n                            <div class=\"logo-title\">\n                              <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1591.16\" height=\"228\" viewBox=\"0 0 1591.16 228\">\n                                <path fill=\"currentColor\" d=\"M1188,564.251c44.35-.743,73.87,8.929,92,34.07,4.63,6.417,10.28,12.646,11,23.047h-2c-6.83,4.791-27.29,6.16-36,9.019-2.99.981-9.55,1.433-10,1-9.12-9.946-15.51-19.273-31-23.047-22.89-5.579-52.89,6.9-49,26.053,26.63,15.944,65.56,19.033,95,32.067,7.96,3.523,14.47,10.142,21,15.031,13.9,10.41,27.3,34.3,17,59.122-14.37,34.647-71.81,61.089-125,46.1-32.66-9.206-55.68-23.2-67-54.112l49-11.023c12.66,19.852,89.26,30.411,89-7.014-33.95-23.62-85.94-18.965-116-49.1-13.15-13.182-19.73-32.145-11-55.114,3.41-8.97,8.01-17.794,15-23.048,9.43-7.087,20.78-14.024,33-18.037C1170.43,566.821,1181.44,568.262,1188,564.251Zm916,0c13.52-.17,27.96-1.1,39,2,43.32,12.169,70.17,39.15,83,82.169,3.47,11.651,6.73,32.921,3,48.1q-3,11.02-6,22.045c-16.56,39.277-46.84,60.978-93,71.147-20.08,4.423-46.78-4.225-58-10.021-31.59-16.312-52.46-36.926-64-73.151-1.76-5.533-.61-9.837-2-16.033-1.97-8.8-2.22-24.978,0-34.07l6-21.044c10.93-26.763,32.77-50.773,59-62.128l21-7.014C2096.14,565.107,2100.88,566.21,2104,564.251Zm-793,3.006h207v50.1h-78V786.71h-50V617.36h-79v-50.1Zm235,0h172v50.1H1596v36.075h103v47.1H1596v36.074h122v50.1H1546V567.257Zm210,0h56c5.11,8.269,12.51,15.258,18,23.047,19.14,27.184,38.64,53.182,58,80.166,9.45,13.175,22.36,25.422,30,40.083h1v-143.3h50V786.71h-57c-6.61-13.269-18.53-24.292-27-36.075-16.45-22.885-32.55-45.215-49-68.14-7.56-10.538-24.74-27.126-29-39.081h-1v143.3h-50V567.257Zm509,0h56c5.11,8.269,12.51,15.258,18,23.047,19.14,27.184,38.64,53.182,58,80.166,9.45,13.175,22.36,25.422,30,40.083h1v-143.3h50V786.71h-57c-6.61-13.269-18.53-24.292-27-36.075-16.45-22.885-32.55-45.215-49-68.14-7.56-10.538-24.74-27.126-29-39.081h-1v143.3h-50V567.257Zm257,0h172v50.1H2572v36.075h103v47.1H2572v36.074h122v50.1H2522V567.257Zm-414,47.1c-5.43,3.5-13.32,3.046-19,6.012-14.03,7.333-26.79,22-32,38.079-12.54,38.754,15.2,67.758,40,77.159,6.39,2.423,18.33,6.548,29,4.009,23.88-5.682,41.36-18.317,50-39.081,15.97-38.4-11.38-72.647-37-83.172C2130.67,613.938,2119.55,614.2,2108,614.354Z\" transform=\"translate(-1103.34 -563.5)\"/>\n                              </svg>\n                              \n                            </div>\n                          </div>\n                          <div class=\"sidebar-scroll-content\">\n                            <div class=\"menu-title\">\n                        <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"title\">{{_PLATFORM_UNION}}</div>   \n                        \n                      </div>\n                            <ul class=\"nav masthead-nav\">\n                        <li\n                          ng-class=\"{ 'active':(current_tag==2) && (window_url_stack.length ==0) }\"\n                          ng-click=\"showTag(2)\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            <span class=\"icon li-featured-list\"></span\n                            ><a>{{_PLAYLISTS}}</a>\n                          </div>\n                        </li>\n                            </ul>\n                            <div\n                        ng-if=\"!isChrome || is_login('netease') || is_login('qq')\"\n                        class=\"menu-title\"\n                      >\n                        <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"title\">{{_MY_MUSIC}}</div>                     \n                      </div>\n                            <ul class=\"nav masthead-nav\">\n                        <li\n                          ng-if=\"!isChrome\"\n                          ng-click=\"showPlaylist('lmplaylist_reserve')\"\n                          ng-class=\"{ 'active':window_type=='list' && ( '/playlist?list_id=lmplaylist_reserve' === getCurrentUrl() ) }\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            <div>\n                            <svg t=\"1659442896517\"  viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1723\" width=\"512\" height=\"512\"><path fill=\"currentColor\" d=\"M9.25119901 889.06160075c0 69.37933454 56.30786571 125.68720026 125.68720024 125.68720024h754.1232015c69.37933454 0 125.68720026-56.30786571 125.68720024-125.68720024v-502.74880101H9.25119901v502.74880101z m879.81040174-754.1232015H449.05585012A125.68720026 125.68720026 0 0 0 323.36864987 9.25119901H135.03894901C65.55906473 9.25119901 9.25119901 65.55906473 9.25119901 134.93839925v188.63135015h1005.49760198v-62.84360014c0-69.37933454-56.30786571-125.68720026-125.68720024-125.78775001z\" p-id=\"1724\"></path></svg>\n                          </div> \n                            <a>{{_LOCAL_MUSIC}}</a>\n                          </div>\n                        </li>\n                        <li\n                          ng-if=\"is_login('netease')\"\n                          ng-click=\"showTag(6, {platform:'netease', user: musicAuth.netease});\"\n                          ng-class=\"{ 'active':(current_tag==6 && tag_params.platform=='netease') && (window_url_stack.length ==0) }\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            <div>\n                          <svg t=\"1659438696222\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1718\" width=\"512\" height=\"512\"><path d=\"M984.82626711 518.27221813v314.72202442c0 62.83016817-51.40650127 126.23151979-102.24181922 151.93477031-28.55916739 14.27958373-58.8318847 18.27786711-90.81815219 18.27786717-199.34298832-0.57118343-399.25715974-0.57118343-598.60014792-0.57118341-98.8147191 0-181.06512109-90.24696889-180.49393773-180.49393775 1.14236668-205.62600507 0.57118343-411.25200997 0.57118334-617.44919836 0-66.82845164 47.4082179-130.22980311 105.6689192-157.64660389 26.84561727-12.56603358 54.83360135-17.70668378 84.53513544-17.70668381 201.62772161 0.57118343 403.25544315 0.57118343 604.8831648 0.57118335 69.11318498 0 131.37216986 46.83703452 159.93133722 105.09773601 13.13721707 26.84561727 17.70668378 54.83360135 17.70668374 84.5351353-1.7135501 99.95708581-1.14236668 199.34298832-1.14236668 298.72889066z m-358.13195879-108.52483609c1.14236668 0 4.56946676 0.57118343 7.42538351 0.57118345 2.85591675 0.57118343 5.71183343 0.57118343 7.99656689 2.28473345 48.5505845 25.70325061 85.10631875 64.54371822 96.52998569 118.23495291 13.70840029 65.68608496 0.57118343 127.37388647-44.5523011 179.92275434-72.54028509 84.53513538-206.76837169 105.09773596-305.01190745 53.69123456-106.81128602-55.40478468-171.35500425-197.05825477-124.51796969-319.29149101 23.98970064-62.83016817 61.11661813-113.09430275 122.23323632-143.93820357 15.99313367-7.9965669 33.12863417-15.42195035 47.97940128-25.70325061 19.99141721-13.70840029 23.41851719-35.98455091 11.4236668-55.97596809-10.2813003-17.70668378-34.27100084-25.70325061-53.12005125-18.27786708-109.0960193 42.83875106-181.06512109 118.80613625-217.62085533 231.32925566-26.84561727 83.39276864-17.13550042 162.78725409 16.56431709 241.03937253 23.98970064 54.83360135 60.54543483 99.95708581 109.09601931 137.08400339 39.98283428 30.27271734 83.39276864 50.83531791 130.80098651 61.68780143 75.96738523 17.13550042 150.22122028 6.85420015 221.61913876-25.13206727 126.80270307-56.54715136 212.48020515-202.77008831 166.78553744-350.13539188-24.56088395-78.82330201-76.53856848-132.51453654-150.79240368-165.07198726-23.98970064-10.2813003-50.26413461-15.42195035-77.68093528-22.8473339-2.28473334-17.70668378-6.28301684-37.12691754-7.42538348-56.54715145-1.14236668-24.56088395 22.84733386-39.41165101 45.12348441-29.13035063 11.42366688 5.14065014 22.27615056 12.56603358 33.12863422 18.84905041 30.27271734 17.70668378 58.2607014 10.85248359 69.68436843-18.27786716 6.28301684-16.56431707-1.14236668-42.26756771-19.9914172-53.69123455-20.56260047-12.56603358-43.40993431-21.70496722-65.68608503-31.41508414-51.40650127-22.84733386-118.23495283 14.27958373-144.50938688 60.54543471-17.70668378 30.84390082-13.13721707 64.54371822-4.56946675 97.10116907 2.85591675 11.99485033 11.42366688 25.13206727-6.28301677 33.1286342-8.56775021 3.99828343-17.13550042 7.42538352-25.70325061 11.42366692-77.10975185 33.12863417-123.37560301 134.79926988-101.09945263 218.19203866 19.42023388 71.96910175 105.66891925 132.51453654 180.49393788 106.811286 47.4082179-15.99313367 108.524836-73.11146841 101.67063576-134.22808662-3.42710013-39.98283428-12.56603358-80.536852-19.99141717-122.23323647z\" fill=\"#E00000\" p-id=\"1719\"></path><path d=\"M1219.58262286 856.98394298V190.98416012c31.98626742 19.42023388 66.25726829 13.13721707 98.8147191 13.13721695 154.79068717 1.14236668 310.15255761 0 464.94324469 1.14236676 23.41851719 0 47.4082179 5.71183343 69.68436837 12.5660336 28.55916739 8.56775021 38.26928428 33.12863417 39.98283432 60.54543488 1.7135501 25.70325061 0.57118343 51.9776846 0.57118329 78.25211852 0 133.65690317-1.14236668 267.31380653 0.57118338 401.54189319 1.14236668 77.10975185-29.1303507 91.38933559-90.8181522 98.81471896-33.12863417 3.99828343-66.82845164 0.57118343-99.95708583 0.57118352-6.28301684-25.70325061 1.14236668-34.84218421 25.13206732-37.12691767 19.42023388-1.7135501 39.41165101-5.71183343 57.68951808-11.42366702 13.13721707-3.99828343 22.27615056-13.70840029 22.2761505-30.84390081-0.57118343-161.64488734 0-323.86095793-0.57118336-485.50584507 0-23.98970064-18.27786711-39.98283428-46.8370344-39.98283429-57.11833477-0.57118343-113.66548614 0-170.78382091 0-88.53341879 0-177.06683766 0.57118343-265.60025647-0.57118328-17.13550042 0-22.27615056 5.14065014-22.27615055 22.27615047 0.57118343 187.34813795 0 374.69627575 1.14236672 562.0444137 0 18.84905043-5.14065014 23.98970064-23.41851732 22.27615064-19.42023388-2.85591675-39.41165101-1.7135501-60.54543473-1.71355019zM4658.67755692 317.78686327c26.27443397 0 50.83531791-0.57118343 74.82501848 0.57118327 3.42710013 0 9.13893356 8.56775021 9.13893361 13.70840045 0.57118343 37.12691754 1.14236668 74.25383516 0 111.38075265-0.57118343 17.70668378 3.99828343 23.98970064 22.84733384 23.41851716 70.82673508-1.14236668 141.08228678-0.57118343 211.90902183-0.57118337 29.70153417 0 43.40993431 13.70840029 45.12348442 46.26585119-4.56946676 0.57118343-9.13893356 1.14236668-13.13721696 1.14236673-82.25040197 0-164.50080404 0.57118343-246.75120609 0-15.42195035 0-19.99141721 5.14065014-19.99141704 19.99141719 0.57118343 79.39448523 0.57118343 159.36015393 0.57118328 238.75463917 0 58.8318847-17.70668378 79.96566864-75.39620187 84.53513527-37.69810095 2.85591675-75.39620181 0.57118343-113.09430271 0.57118352 5.71183343-31.4150841 11.99485033-35.41336751 42.83875107-38.26928439 15.42195035-1.14236668 31.98626742-4.56946676 45.12348436-11.99485023 8.56775021-5.14065014 14.85076698-20.56260047 14.85076707-30.8439008 1.14236668-82.25040197 0-164.50080404 0.57118332-246.75120595 0-15.99313367-9.71011687-15.42195035-20.56260047-15.42195044-57.68951801 0-115.37903619 0.57118343-173.06855422-0.57118334-17.13550042-0.57118343-35.98455091-3.42710013-51.40650123-10.85248356-25.70325061-11.42366688-35.98455091-37.12691754-34.84218428-62.83016808 4.56946676-81.10803537 11.99485033-162.21607061 18.84905052-245.03765618h119.37731971c153.0771371-1.14236668 306.15427413-1.7135501 458.66022775-3.42710003 14.27958373 0 16.56431707 6.85420015 16.56431705 17.13550043-0.57118343 10.85248359 2.28473334 23.98970064-16.56431705 24.56088392l-390.68940955 8.56775016c-29.70153417 0.57118343-58.8318847 0.57118343-88.53341882 0-10.85248359 0-15.99313367 2.85591675-17.13550039 14.85076705-3.42710013 58.2607014-7.9965669 115.95021947-12.56603362 174.21092089-2.28473334 29.70153417 3.42710013 35.41336751 32.55745073 35.98455085 45.12348447 0 90.81815222-0.57118343 135.94163665 0.57118339 18.27786711 0.57118343 23.41851719-6.28301684 22.8473339-23.98970055 0.57118343-41.69638436 1.14236668-82.82158541 1.14236671-125.66033637zM3644.25593211 504.56381782c24.56088395 1.7135501 46.26585106 3.99828343 67.97081829 3.99828339 129.08743647 0.57118343 258.17487296 0 386.69112624 0 23.41851719 0 46.83703452 1.14236668 69.6843683 2.85591677 36.55573423 2.28473334 62.25898494 29.1303507 62.83016814 72.5402851 1.7135501 85.67750212 0.57118343 171.35500425 1.14236676 257.03250631 0 14.85076698-8.56775021 15.99313367-19.99141715 15.99313359-71.39791843-0.57118343-142.79583693 0-214.19375529 0-87.39105211 0-174.21092095 0.57118343-261.60197302-0.57118318-21.70496722 0-43.40993431-3.99828343-65.11490159-7.42538362-15.42195035-2.85591675-28.55916739-26.84561727-28.55916737-51.97768449v-286.16285701c0.57118343-2.28473334 1.14236668-5.14065014 1.14236669-6.28301686z m293.5882405 200.48535484c-63.40135157 0-126.23151979 0.57118343-189.63287123 0-14.85076698 0-21.13378386 4.56946676-19.99141722 19.99141716 1.14236668 14.27958373 0 28.55916739 0.57118332 43.40993447 0 27.41680065 11.42366688 41.12520094 38.8404676 41.12520097 119.94850288 0.57118343 239.89700583 0.57118343 360.41669215 0.57118332 11.99485033 0 17.13550042-4.56946676 16.56431712-17.13550039-1.14236668-21.13378386-1.7135501-42.83875106 0-63.97253495 1.7135501-18.84905043-3.99828343-25.13206727-23.41851732-24.56088387-61.11661813 1.14236668-122.23323628 0.57118343-183.34985442 0.57118329z m-1.71355005-48.5505844c62.25898494 0 124.51796971-0.57118343 187.34813793 0.57118324 14.85076698 0 21.70496722-3.99828343 20.56260052-19.42023375-1.14236668-16.56431707 0-33.12863417 0-49.12176786 0-28.55916739-4.56946676-34.84218421-33.12863417-34.84218422-120.51968633-0.57118343-241.03937259-0.57118343-361.55905879-0.57118334-14.85076698 0-21.13378386 6.85420015-20.56260057 21.70496718 0.57118343 19.99141721 1.14236668 39.98283428 0 59.40306811-1.7135501 18.27786711 5.14065014 23.41851719 23.41851729 23.41851724 60.54543483-1.7135501 122.23323628-1.14236668 183.92103779-1.1423666zM1982.68357478 783.87247462c47.97940115-34.84218421 96.52998574-68.54200163 143.36702031-105.0977359 47.4082179-37.12691754 86.24868543-82.25040197 124.51796955-135.94163666-40.55401772 0-74.8250185-0.57118343-108.52483596 0.57118334-5.14065014 0-10.2813003 7.9965669-14.8507669 13.13721704-23.41851719 26.84561727-45.12348447 57.11833477-79.96566866 70.25555169-20.56260047 7.42538352-42.83875106 9.71011687-70.25555171 14.850767 27.98798398-27.98798398 55.40478468-47.4082179 71.96910165-74.25383514 16.56431707-26.84561727 22.27615056-59.97425148 32.55745093-91.9605188 23.98970064 26.84561727 57.11833477 22.27615056 88.53341876 22.27615044 142.22465355 0.57118343 283.87812362 1.7135501 426.10277703-0.57118338 60.54543483-1.14236668 95.9588023 34.84218421 106.81128601 90.24696888 10.2813003 54.26241805 7.9965669 108.524836-3.42710015 161.07370403-12.56603358 55.97596803-46.83703452 93.6740689-117.09258616 100.52826902-42.26756771 4.56946676-83.96395206 6.85420015-125.66033637 10.28130025-6.28301684-32.55745077-1.14236668-39.41165101 26.27443389-39.41165093 46.26585106-0.57118343 88.53341879-10.85248359 116.5214029-50.83531788 9.71011687-14.27958373 17.13550042-33.12863417 18.27786719-50.26413463 3.42710013-41.12520094 3.42710013-82.25040197 3.42710005-123.37560292 0-33.6998175-9.71011687-45.12348447-39.98283436-52.54886801-11.99485033-2.85591675-24.56088395-2.28473334-36.55573421-0.57118334-5.71183343 0.57118343-13.13721707 6.85420015-14.85076702 11.42366695-13.13721707 51.9776846-42.26756771 94.81643567-77.10975189 134.22808669-58.2607014 66.25726829-129.08743647 116.52140289-211.33783845 149.07885353-30.27271734 11.99485033-63.40135157 15.42195035-94.81643565 23.41851724-1.14236668-3.42710013-2.28473334-6.28301684-3.42710011-9.71011683 13.70840029-7.42538352 27.41680065-15.42195035 41.69638426-22.84733393 114.23666945-61.11661813 197.05825477-151.93477037 254.17658968-267.31380651 6.85420015-13.70840029 6.28301684-20.56260047-11.42366702-19.99141713-23.98970064 0.57118343-47.97940115-0.57118343-71.96910176 0.57118337-6.85420015 0.57118343-17.13550042 4.56946676-19.99141713 10.28130025-38.26928428 67.97081826-97.10116898 114.80785275-158.78897053 158.7889705-41.12520094 29.1303507-83.96395206 56.54715136-134.2280866 68.54200165-19.42023388 4.56946676-39.41165101 5.71183343-58.83188479 7.99656687-0.57118343 0-1.14236668-1.14236668-1.14236678-2.85591675zM3350.66769158 609.66155379c29.70153417 0 55.40478468-0.57118343 81.67921872 0.57118324 3.99828343 0 9.71011687 5.14065014 10.85248362 9.13893363 12.56603358 42.83875106 26.27443397 85.67750212 36.55573424 129.0874365 7.42538352 32.55745077-4.56946676 84.53513538-36.55573424 96.52998557-17.70668378 6.28301684-36.55573423 11.99485033-54.83360142 11.99485025-155.93305382 1.14236668-311.29492428 1.14236668-467.22797805 0-36.55573423-0.57118343-77.10975185-6.28301684-90.24696885-48.5505843-6.85420015-22.27615056-7.42538352-49.12176788-2.28473351-71.96910185 10.85248359-45.6946678 27.98798398-89.67578557 42.83875118-134.22808662 10.85248359-31.98626742 22.27615056-63.40135157 33.6998174-95.38761902 9.71011687-26.27443397 6.28301684-33.12863417-21.70496708-33.12863413-19.99141721 0-39.98283428 0.57118343-59.4030682 0-29.1303507-0.57118343-40.55401772-11.99485033-42.26756766-43.40993444 3.42710013-0.57118343 6.28301684-1.14236668 9.71011686-1.14236667h662.57268291c33.12863417 0 41.69638436 8.56775021 44.55230112 42.83875104-7.42538352 0.57118343-14.27958373 1.7135501-21.70496729 1.71355007-151.36358702 0-303.29835734 0.57118343-454.66194439-0.57118334-18.84905043 0-26.27443397 9.13893356-30.84390077 23.98970053-18.84905043 57.68951801-36.55573423 115.95021947-55.4047847 173.63973758-6.85420015 21.70496722-15.99313367 43.40993431-23.41851724 64.54371831-14.85076698 41.12520094 7.9965669 74.8250185 52.54886794 74.82501844 128.51625316 0.57118343 256.4613229 0 384.97757608 0.57118339 41.12520094 0 57.11833477-19.99141721 46.26585114-59.97425145-11.42366688-42.26756771-24.56088395-84.53513538-37.12691761-126.23151982-1.7135501-4.56946676-4.56946676-7.42538352-8.5677502-14.85076691zM2654.9663747 462.86743343c-161.0737039 0-319.29149122 0-477.50927839-0.57118331-17.70668378 0-35.98455091-1.7135501-51.97768462-7.99656693-22.84733386-9.13893356-32.55745077-31.98626742-33.12863424-54.26241795-2.28473334-68.54200163-0.57118343-137.08400337-0.57118326-205.62600496 0-1.14236668 1.14236668-2.28473334-0.57118342 0.57118325 19.42023388 2.85591675 37.12691754 7.42538352 54.83360141 7.42538355 130.80098659 1.14236668 261.60197303 0.57118343 392.97414292 2.28473343 25.70325061 0.57118343 51.9776846 6.85420015 77.68093518 13.13721692 26.84561727 6.28301684 37.12691754 30.27271734 38.26928442 52.54886801 1.7135501 63.97253491 0 127.37388647 0 192.48878799z m-280.45102359-111.38075272h-178.7803878c-6.28301684 0-14.85076698 1.14236668-18.277867 5.14065012-10.2813003 13.13721707-1.14236668 46.83703452 14.85076702 53.69123466 10.85248359 4.56946676 23.41851719 7.42538352 35.41336751 7.42538354 98.8147191 0.57118343 197.62943812 0.57118343 296.44415717 0.57118333 49.69295123 0 49.12176788 0 46.83703454-48.55058457-0.57118343-14.27958373-5.14065014-18.84905043-19.42023379-18.27786708-59.40306815 0.57118343-118.23495283 0-177.06683765 0z m-2.85591677-45.12348446c59.40306815 0 119.37731956 0 178.78038775-0.57118324 6.28301684 0 14.85076698-1.7135501 17.70668373-5.71183352 13.13721707-18.27786711-1.7135501-47.97940115-25.13206735-48.5505846-117.66376954-0.57118343-235.32753909-0.57118343-353.56249197 0-5.14065014 0-11.99485033 7.9965669-14.27958359 13.70840038-2.28473334 5.14065014 0 11.99485033-0.57118344 18.2778671-1.7135501 16.56431707 4.56946676 22.84733386 22.2761507 22.27615064 58.2607014 0 116.52140289 0.57118343 174.78210417 0.57118324zM3574.57156369 428.59643259h100.52826917c23.41851719 0 21.70496722 0.57118343 15.99313374-21.70496714-5.14065014-21.13378386-5.71183343-43.40993431-7.99656689-66.82845169h89.10460225v39.411651c0.57118343 32.55745077 17.13550042 49.12176788 48.55058442 49.12176783h234.18517239c29.70153417 0 49.69295123-19.42023388 50.26413462-49.12176783v-39.98283434h89.10460216c-3.99828343 23.98970064-7.42538352 46.26585106-11.99485015 68.54200171-2.85591675 13.13721707-2.85591675 21.70496722 14.27958357 21.13378382 21.70496722-0.57118343 43.98111773 0 65.68608493 0 29.1303507 0.57118343 38.84046764 12.56603358 34.27100081 43.98111777H3624.83569831c-38.84046764 0-42.83875106-3.42710013-50.26413462-44.55230113z\" p-id=\"1720\"></path><path d=\"M1338.38875907 327.49698011c21.13378386 0 40.55401772 1.14236668 59.40306816-0.57118326 12.56603358-0.57118343 13.70840029 3.42710013 15.42195036 14.85076698 4.56946676 42.83875106 11.42366688 85.10631875 17.70668379 127.37388646 0.57118343 2.85591675 3.42710013 5.14065014 9.13893352 14.27958366 15.42195035-53.69123465 23.41851719-103.38418587 25.70325065-154.79068716h76.53856852c-3.99828343 19.42023388-6.28301684 38.84046764-12.56603357 57.1183348-19.42023388 55.97596803-42.83875106 110.80956935-60.54543487 166.78553741-5.14065014 15.99313367 0 38.26928428 7.99656687 53.69123461 15.99313367 31.98626742 38.26928428 60.54543483 54.83360129 91.96051886 7.9965669 14.85076698 7.9965669 34.27100084 8.56775023 52.54886805-42.83875106-35.98455091-75.39620181-78.25211851-102.24181917-129.08743658-35.41336751 45.6946678-69.11318498 89.67578557-103.3841859 133.65690335-2.28473334-1.14236668-5.14065014-2.85591675-7.42538345-3.99828339 1.14236668-17.70668378-3.42710013-35.41336751 7.99656685-53.69123475 24.56088395-38.26928428 46.83703452-77.10975185 68.5420017-117.0925862 3.42710013-6.28301684 2.85591675-17.13550042 0.57118333-24.5608838-13.13721707-40.55401772-29.1303507-79.96566864-41.69638438-120.51968649-9.71011687-30.84390082-16.56431707-62.83016817-24.56088393-94.24525219v-13.70840036zM1777.62875312 754.17094063c-13.70840029-15.42195035-29.1303507-29.70153417-41.69638435-46.26585117-17.70668378-23.41851719-33.12863417-47.97940115-50.26413458-71.96910184-3.42710013-4.56946676-7.9965669-7.9965669-12.56603359-11.99485029-2.85591675 3.99828343-6.28301684 7.9965669-9.13893351 11.99485029-24.56088395 42.26756771-54.26241805 81.67921862-92.53170232 117.09258631-6.28301684-19.99141721-6.28301684-37.12691754 3.99828348-53.69123469 22.84733386-37.12691754 44.55230111-75.39620181 66.82845162-112.52311944 11.99485033-19.99141721-2.85591675-35.41336751-8.56775021-51.97768461-14.27958373-41.12520094-29.70153417-82.25040197-43.98111771-123.37560296-8.56775021-26.27443397-14.85076698-53.12005121-23.41851728-83.39276876 26.27443397 0 48.5505845-0.57118343 70.25555174 0.57118332 2.85591675 0 7.42538352 8.56775021 8.56775008 13.70840043 6.85420015 39.41165101 11.99485033 79.39448523 18.8490506 119.37731949 1.14236668 5.71183343 6.28301684 10.85248359 9.71011686 15.99313379 2.85591675-5.71183343 7.9965669-10.85248359 8.56775023-16.56431705 6.28301684-43.98111773 12.56603358-87.39105211 18.27786717-131.94335324h73.11146835c-17.70668378 78.82330201-35.98455091 155.36187039-70.25555172 227.33097223-9.13893356 18.84905043 4.56946676 33.12863417 11.9948503 47.40821777 15.42195035 29.70153417 32.55745077 58.8318847 52.54886802 86.24868552 14.85076698 18.27786711 17.70668378 38.26928428 9.71011682 63.9725349zM3596.84771427 238.3923779c5.14065014-0.57118343 10.2813003-1.14236668 15.4219504-1.14236658 85.67750212 0 171.35500425-0.57118343 257.03250623 0.57118328 20.56260047 0 29.70153417-5.14065014 26.27443395-26.27443395-1.14236668-7.9965669 0-15.99313367 0-26.84561727 26.84561727 0 51.9776846-0.57118343 77.10975196 0.57118329 2.85591675 0 7.42538352 6.85420015 7.9965669 10.85248367 1.14236668 6.85420015 0.57118343 13.70840029 0 20.56260049-1.7135501 15.99313367 5.14065014 21.70496722 21.13378378 21.13378377 79.39448523-0.57118343 158.78897057-0.57118343 238.75463923-0.57118328 30.27271734 0 41.12520094 12.56603358 37.69810086 45.12348433H3637.97291523c-32.55745077-1.7135501-38.84046764-8.56775021-41.12520096-43.98111775zM3479.18394471 260.6685285h-17.70668379c-206.19718841 0-412.39437671 0-619.1627484 0.57118329-21.13378386 0-31.4150841-6.28301684-37.69810095-27.41680066-5.14065014-18.27786711 1.7135501-18.27786711 15.42195047-18.27786703H3428.91981014c37.69810095 1.14236668 39.98283428 3.42710013 50.26413457 45.1234844zM4363.37576636 828.99595911c11.99485033-26.84561727 26.84561727-52.54886791 36.55573425-79.96566869 15.99313367-47.97940115 28.55916739-96.52998574 42.83875098-145.08057021 1.7135501-5.14065014 5.14065014-14.27958373 7.99656685-14.27958373 26.84561727-1.14236668 53.69123465-0.57118343 82.25040204-0.57118333-22.84733386 106.81128602-62.83016817 196.48707141-162.78725396 245.03765601-2.85591675-1.7135501-5.14065014-3.42710013-6.85420016-5.14065005zM5033.37383267 830.13832574c-63.40135157 2.28473334-166.78553742-149.65003708-165.07198737-242.18173924H4951.69461405c15.99313367 83.39276864 37.12691754 166.78553742 81.67921862 242.18173924z\" p-id=\"1721\"></path><path d=\"M626.69430832 409.74738204c7.42538352 42.26756771 16.56431707 82.25040197 21.13378383 123.37560309 6.85420015 61.11661813-54.26241805 118.23495283-101.67063569 134.22808662-74.8250185 25.70325061-161.0737039-35.41336751-180.4939379-106.81128589-22.27615056-83.39276864 23.98970064-185.06340457 101.0994526-218.19203864 8.56775021-3.99828343 17.13550042-7.42538352 25.7032506-11.42366702 17.70668378-7.9965669 9.13893356-21.13378386 6.2830168-33.12863412-7.9965669-33.12863417-12.56603358-66.25726829 4.56946678-97.10116903 25.70325061-46.26585106 92.53170228-82.82158541 144.50938681-60.54543479 22.27615056 9.71011687 45.12348447 18.84905043 65.68608504 31.41508405 18.84905043 11.42366688 26.27443397 37.12691754 19.99141706 53.69123469-10.85248359 28.55916739-39.41165101 35.98455091-69.68436838 18.27786711-10.85248359-6.28301684-21.70496722-13.70840029-33.12863408-18.84905053-22.27615056-10.2813003-46.26585106 4.56946676-45.12348441 29.1303507 0.57118343 19.42023388 4.56946676 38.84046764 7.42538346 56.54715142 27.41680065 7.9965669 53.69123465 12.56603358 77.68093523 22.84733391 73.68265177 32.55745077 126.23151979 85.67750212 150.79240371 165.07198738 45.6946678 147.36530365-39.98283428 294.15942384-166.78553736 350.13539193-71.39791843 31.98626742-145.65175349 42.26756771-221.61913879 25.1320672-47.4082179-10.85248359-90.81815222-31.4150841-130.80098652-61.68780145-48.5505845-37.12691754-85.10631875-81.67921862-109.09601933-137.08400335-34.27100084-77.68093526-43.40993431-157.07542049-16.56431707-241.03937257 35.98455091-112.5231194 107.95365264-189.061688 217.62085528-231.32925567 19.42023388-7.42538352 42.83875106 0.57118343 53.12005131 18.27786715 11.99485033 19.99141721 7.9965669 42.26756771-11.42366689 55.975968-14.85076698 10.2813003-31.98626742 17.13550042-47.97940123 25.70325063-60.54543483 30.84390082-98.24353568 81.10803537-122.23323631 143.93820341-46.83703452 122.23323628 17.70668378 263.88670651 124.51796968 319.29149121 98.24353568 51.40650127 232.47162227 30.84390082 305.01190742-53.69123465 45.12348447-52.54886791 58.2607014-114.80785275 44.55230121-179.92275432-11.42366688-53.69123465-47.97940115-91.96051893-96.5299858-118.23495295-2.28473334-1.14236668-5.14065014-1.7135501-7.99656677-2.28473333-3.99828343-1.7135501-7.42538352-1.7135501-8.56775029-1.71355019z m-93.10288551 0c-71.96910175 16.56431707-104.52655253 89.67578557-73.11146845 148.50767041 10.85248359 20.56260047 37.69810095 31.98626742 57.11833466 26.27443395 26.84561727-7.9965669 45.6946678-27.41680065 41.69638431-52.54886789-6.28301684-41.12520094-17.13550042-81.67921862-25.70325052-122.23323647z\" fill=\"#FFFFFF\" p-id=\"1722\"></path><path d=\"M533.59142281 409.74738204c8.56775021 40.55401772 19.42023388 81.10803537 25.70325052 122.23323647 3.99828343 25.13206727-14.85076698 44.55230111-41.69638431 52.54886789-19.42023388 5.71183343-46.26585106-5.71183343-57.11833466-26.27443395-31.4150841-59.40306815 0.57118343-132.51453654 73.11146845-148.50767041z\" fill=\"#E00000\" p-id=\"1723\"></path></svg>\n                          </div> \n                          <a>{{_MY_NETEASE}}</a>\n                          </div>\n                        </li>\n                        <li\n                          ng-if=\"is_login('qq')\"\n                          ng-click=\"showTag(6, {platform:'qq', user: musicAuth.qq});\"\n                          ng-class=\"{ 'active':(current_tag==6 && tag_params.platform=='qq') && (window_url_stack.length ==0) }\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            <div>\n                          <svg t=\"1659438737503\"  viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1876\" width=\"512\" height=\"512\"><path d=\"M2193.94246832 793.5246942c-29.08469133-9.08896607-58.16938267-18.1779321-80.34645979-24.7219877-61.80496903 6.18049689-120.33791038 15.99658023-178.87085163 16.36013891-91.98033632 0.72711727-172.32679609-68.71258326-181.7793208-160.32936094-6.90761419-63.98632093-8.3618488-130.5175523 2.18135187-193.4131973 6.54405559-38.17365737 33.447395-79.98290114 63.25920364-106.15912335 78.16510793-69.07614191 172.32679609-67.25834869 262.48933916-28.35757407 84.70916351 36.71942275 117.0658826 110.5218271 111.97606163 201.04792887-3.63558638 68.71258326 10.90675924 139.97007703-31.99316045 202.5021633-3.99914507 5.45337963 1.81779322 20.35928391 6.54405555 28.72113268 10.17964193 17.45081478 22.54063577 33.08383638 34.17451232 49.80753391-2.90846912 5.089821-5.45337963 9.81608331-7.6347315 14.54234569z m-75.25663884-141.78787027c17.81437341-77.43799067 21.44995987-153.42174675 5.81693834-229.76906141-1.09067596-5.81693827-4.72626233-11.63387652-7.9982902-17.08725619-41.44568518-71.25749377-161.78359548-98.16083323-239.94870339-53.07956164-42.53636106 24.35842898-67.62190732 60.71429314-68.71258327 111.24894426-0.72711727 36.35586418 1.09067596 72.71172832-1.09067588 109.06759249-3.99914507 62.895645 17.08725611 115.24808937 74.89308016 141.0607529 63.25920361 28.35757405 129.42687641 26.90333948 195.23099051-9.45252465-27.99401539-39.62789198-52.71600306-74.89308016-78.16510795-110.52182701 65.07699684-26.17622215 86.89051531 21.81351849 119.97435168 58.53294125zM1683.50613559 792.07045962c-29.81180863-8.72540737-59.98717587-17.81437341-102.88709558-30.53892596-57.44226538 31.99316048-135.24381463 36.71942275-212.68180536 12.72455252-77.43799067-24.35842898-122.51926218-81.07357708-130.51755227-163.2378301-3.27202778-35.9923055 0.3635587-72.71172832-2.18135183-108.70403378-11.63387652-160.69291958 109.79470977-253.76393182 277.39524353-224.67924054 101.06930237 17.45081478 163.23783004 86.52695668 165.41918182 189.05049367 1.81779322 77.43799067 16.72369751 158.51156768-29.08469125 231.22329599-4.36270369 6.90761419 1.81779322 23.26775306 7.27117279 33.08383636 9.45252465 17.45081478 22.17707715 33.08383638 33.81095363 49.44397527-2.18135187 3.63558638-4.36270369 7.63473149-6.54405548 11.63387657z m-82.52781166-140.69719431c6.54405559-0.72711727 12.72455248-1.81779322 19.268608-2.54491051 0-55.98803084 0.3635587-111.97606156 0-167.96409235-0.72711727-72.71172832-33.81095362-122.15570357-94.52524677-141.42431163-78.89222523-25.0855463-166.14629922-6.54405559-202.86572202 47.98974071-14.90590428 22.17707715-24.35842898 51.26176849-27.26689809 78.16510796-4.72626233 40.35500923-1.09067596 81.80069437-1.45423459 122.88282079-0.3635587 62.1685277 29.08469133 107.97691655 85.79983939 128.3362005 61.07785177 21.81351849 123.97349676 22.90419443 183.23355538-14.54234564-27.26689812-40.35500923-51.26176849-75.25663877-74.52952152-109.79470973 66.53123138-29.08469133 82.52781165 27.26689812 112.33962022 58.8964999zM3022.12905377 509.94895372v-121.79214487c42.89991974-9.08896607 60.35073451 4.72626233 56.35158945 45.08127153-2.18135187 23.26775306-0.3635587 47.26262341-0.36355861 75.62019745h165.05562324v48.35329933h-167.23697515v227.95126825c-96.70659865 16.36013886-113.06673753 10.17964193-116.70232387-42.53636108 18.5414907-1.45423455 36.71942275-3.27202778 59.26005852-5.08982096v-179.96152759c-49.08041659 0-97.43371596-1.45423455-145.78701524 0.36355866-35.9923055 1.45423455-53.07956163-9.81608331-48.71685799-49.08041661 7.27117279-67.62190732 11.99743516-135.24381463 18.54149075-210.13689479h378.46454588v42.89991966h-318.47737004c-4.72626233 52.35244436-10.17964193 97.07015727-11.9974351 142.15142885-0.3635587 8.3618488 14.90590428 23.99487037 23.99487029 24.72198765 34.17451231 3.27202778 68.71258326 1.45423455 107.61335787 1.45423452zM2312.09902683 785.16284541v-246.85631761c122.15570357 0 241.76649664-0.72711727 361.37728969 1.45423458 10.90675924 0.3635587 30.53892588 19.99572529 30.90248457 31.26604314 2.90846912 70.53037644 1.45423455 141.06075292 1.45423454 214.13603989H2312.09902683z m58.53294127-147.9683671h275.94100892c-1.45423455-18.1779321-2.18135187-32.72027775-3.63558633-48.71685803h-272.30542259v48.71685803z m-2.18135187 53.80667891c1.45423455 18.1779321 2.18135187 31.62960183 3.27202779 45.80838884h274.12321577c-0.72711727-17.08725611-1.45423455-31.26604318-2.54491051-45.80838884H2368.45061623zM2374.63111322 456.14227477c-10.17964193-27.6304567-16.72369751-45.08127157-23.99487032-65.0769968 32.72027775-8.72540737 59.26005857-10.17964193 69.43970048 29.81180863 6.90761419 27.26689812 24.72198764 36.71942275 52.71600303 34.53807091 25.0855463-2.18135187 51.26176849 2.90846912 75.62019742-1.81779322 16.72369751-3.27202778 39.26433326-14.90590428 44.3541543-28.72113271 13.81522839-35.26518823 34.53807098-45.80838883 72.3481696-33.08383632-6.54405559 18.5414907-13.08811108 36.71942275-21.81351845 61.44141039h111.24894429c1.81779322 18.90504942 2.90846912 31.99316048 4.36270373 48.71685795H2259.38302378V456.50583343c36.71942275-0.3635587 72.34816969-0.3635587 115.24808944-0.36355866zM2286.28636329 365.61617306v-50.5346512H2470.2470359v-46.17194746c25.44910489 7.27117279 60.35073451 8.3618488 64.34987956 19.99572529 10.90675924 30.90248452 31.26604318 25.81266358 52.35244437 26.17622217 45.4448302 0.3635587 91.25321901 0 138.51584242 0 6.54405559 26.90333948 17.08725611 50.53465115-23.63131166 50.5346512H2286.28636329z\" p-id=\"1877\"></path><path d=\"M2800.7218411 773.89252751c23.63131168-54.89735487 40.71856786-95.25236414 57.80582397-135.24381463 18.5414907-42.89991974 18.5414907-42.89991974 77.80154928-33.81095364-21.81351849 50.89820983-40.35500923 102.15997829-66.1676727 149.05904299-7.27117279 12.36099385-37.81009875 11.63387652-69.43970055 19.99572528zM3141.37628821 604.83775924c40.35500923-10.90675924 65.80411413-3.63558638 79.25578387 36.71942273 13.81522839 41.44568518 33.08383638 80.71001841 49.80753389 120.70146905-46.5355061 16.36013886-69.4397005-1.81779322-82.89137024-43.62703701-11.99743516-38.53721602-29.81180863-74.89308016-46.17194752-113.79385477z\" p-id=\"1878\"></path><path d=\"M755.34092368 39.86763025s-0.72711727 0.3635587-1.45423459 1.09067588c0.72711727-0.3635587 1.45423455-1.09067596 1.45423459-1.09067588z\" fill=\"#FFDC00\" p-id=\"1879\"></path><path d=\"M770.24682802 130.7572906l-14.90590434-90.88966035s-9.81608331 91.98033632-118.15655847 190.50472812c-49.80753385 45.4448302-137.78872515 95.25236414-137.78872518 95.25236413l206.864867 358.10526193s19.63216668 59.26005857-6.54405552 114.88453071c-30.53892588 65.07699684-91.98033632 105.0684474-210.13689483 108.3404752-105.0684474 2.90846912-177.41661707-59.26005857-190.50472811-144.33278068-13.08811108-84.34560482 41.80924379-149.42260172 105.06844738-174.14458938 59.26005857-22.90419443 157.78445038-13.08811108 157.78445046-13.08811107l-229.76906152-387.55351188 3.27202777-39.26433328s73.80240421 9.45252465 203.59283929-16.3601389c59.26005857-11.99743516 109.06759251-31.62960183 146.15057394-49.80753387-56.71514804-24.72198764-119.61079312-38.53721602-185.41490716-38.53721603-255.58172505 0-463.17370937 207.2284257-463.1737094 463.17370936 0 255.58172505 207.2284257 463.17370937 463.1737094 463.17370937 255.58172505 0 463.17370937-207.2284257 463.17370928-463.17370937-0.72711727-155.23953995-76.3473147-291.93758914-192.68607999-376.28319401z\" fill=\"#FFDC00\" p-id=\"1880\"></path><path d=\"M755.34092368 39.86763025s-85.4362808 65.80411413-216.68095035 91.98033629c-129.79043508 25.81266358-203.59283923 16.36013886-203.59283925 16.36013886l-3.27202778 39.2643333L561.56416771 575.38950921s-98.52439189-9.81608331-157.78445044 13.08811107c-63.25920361 24.35842898-118.15655847 89.79898447-105.06844736 174.14458938 13.08811108 85.4362808 85.4362808 147.24124981 190.50472819 144.33278068 118.15655847-3.27202778 179.59796889-43.2634783 210.13689478-108.3404752 26.17622215-55.98803084 6.54405559-114.88453074 6.54405555-114.88453071L499.03208139 325.6247225s87.98119123-49.80753385 137.78872514-95.25236413c108.70403381-98.52439189 118.52011714-190.5047282 118.52011715-190.50472812z\" fill=\"#0AC094\" p-id=\"1881\"></path></svg>\n                          </div>\n                          <a>{{_MY_QQ}}</a>\n                          </div>\n                        </li>\n                            </ul>\n                            <div class=\"menu-title created-title\" ng-init=\"loadMyPlaylist();\">\n                        <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"title\">{{_CREATED_PLAYLIST}}</div>\n                        <svg ng-click=\"showDialog(5)\" t=\"1659442568393\"  viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1663\" width=\"512\" height=\"512\"><path d=\"M842 62H182c-66 0-120 54-120 120v660c0 66 54 120 120 120h660c66 0 120-54 120-120V182c0-66-54-120-120-120z m30 750c0 33-27 60-60 60H212c-33 0-60-27-60-60V212c0-33 27-60 60-60h600c33 0 60 27 60 60v600z\" fill=\"currentColor\" p-id=\"1664\"></path><path d=\"M737 467H557V287c0-24.8-20.2-45-45-45s-45 20.2-45 45v180H287c-24.8 0-45 20.2-45 45s20.2 45 45 45h180v180c0 24.8 20.2 45 45 45s45-20.2 45-45V557h180c24.8 0 45-20.2 45-45s-20.2-45-45-45z\" fill=\"currentColor\" p-id=\"1665\"></path></svg>\n                            </div>\n                            <ul class=\"nav masthead-nav\">\n                        <li\n                          ng-repeat=\"i in myplaylists track by $index\"\n                          ng-class=\"{ 'active':window_type=='list' && ( ('/playlist?list_id='+i.info.id) === getCurrentUrl() ) }\"\n                          ng-click=\"showPlaylist(i.info.id)\"\n                          drag-drop-zone\n                          drag-zone-type=\"'application/listen1-myplaylist'\"\n                          drop-zone-ondrop=\"onSidebarPlaylistDrop('my', i.info.id, arg1, arg2, arg3)\"\n                          draggable=\"true\"\n                          sortable=\"true\"\n                          drag-zone-object=\"i\"\n                          drag-zone-title=\"i.info.title\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            \n                            <a>{{i.info.title}}</a>\n                          </div>\n                        </li>\n                            </ul>\n                            <div class=\"menu-title\" ng-init=\"loadFavoritePlaylist();\">\n                        <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"title\">{{_FAVORITED_PLAYLIST}}</div>\n                            </div>\n                            <ul class=\"nav masthead-nav\">\n                        <li\n                          ng-repeat=\"i in favoriteplaylists track by $index\"\n                          ng-class=\"{ 'active':window_type=='list' && ( ('/playlist?list_id='+i.info.id) === getCurrentUrl() ) }\"\n                          ng-click=\"showPlaylist(i.info.id, {useCache: false})\"\n                          drag-drop-zone\n                          drag-zone-type=\"'application/listen1-favoriteplaylist'\"\n                          drop-zone-ondrop=\"onSidebarPlaylistDrop('favorite', i.info.id, arg1, arg2, arg3)\"\n                          draggable=\"true\"\n                          sortable=\"true\"\n                          drag-zone-object=\"i\"\n                          drag-zone-title=\"i.info.title\"\n                        >\n                          <div ng-class=\"{'opensidebar':isOpenSidebar}\" class=\"sidebar-block\">\n                            <a>{{i.info.title}}</a>\n                          </div>\n                        </li>\n                            </ul>\n                          </div>\n                        </div>\n                    </div>\n                  </div>\n\n                  <div class=\"content\" ng-controller=\"InstantSearchController\">\n                    <div class=\"navigation\">\n                      <div class=\"backfront\">\n                        <svg  fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"angle-left\" class=\"svg-inline--fa fa-angle-left fa-w-8 icon li-back\" ng-click=\"popWindow()\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path fill=\"currentColor\" d=\"M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z\"></path></svg>\n                        <!-- <span class=\"icon li-back\" ng-click=\"popWindow()\"></span> -->\n                        <svg fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"angle-right\" class=\"svg-inline--fa fa-angle-right fa-w-8 icon li-advance\" ng-click=\"forwardWindow()\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path fill=\"currentColor\" d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"></path></svg>\n                        <!-- <span\n                          class=\"icon li-advance\"\n                          ng-click=\"forwardWindow()\"\n                        ></span> -->\n                      </div>\n                      <div class=\"search\">\n                        <svg fill=\"currentColor\" style=\"opacity: 0.28;margin-right: 4px;width: 15px;height: 15px;cursor: default;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"search\" class=\"svg-inline--fa fa-search fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z\"></path></svg>\n                        <input\n                          class=\"form-control search-input\"\n                          id=\"search-input\"\n                          type=\"text\"\n                          ng-model=\"keywords\"\n                          placeholder=\"{{_SEARCH_PLACEHOLDER}}\"\n                          ng-model-options=\"{debounce: 500}\"\n                          ng-keyup=\"enterEvent($event)\"\n                        />\n                      </div>\n                      <div\n                        ng-class=\"{ 'active': (current_tag==4) && (window_url_stack.length ==0)}\"\n                        ng-click=\"showTag(5)\"\n                        class=\"settings\"\n                      >\n                        <span class=\"icon\">\n                          <svg t=\"1660120656679\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2962\" width=\"512\" height=\"512\"><path d=\"M617.386667 896a42.666667 42.666667 0 0 1-42.666667-42.666667v-184.746666a59.306667 59.306667 0 0 0-59.306667-59.306667H209.92a59.306667 59.306667 0 0 0-59.306667 59.306667V853.333333a42.666667 42.666667 0 0 1-85.333333 0v-184.746666a144.64 144.64 0 0 1 144.64-144.64h305.493333a144.64 144.64 0 0 1 144.64 144.64V853.333333a42.666667 42.666667 0 0 1-42.666666 42.666667zM922.026667 896a42.666667 42.666667 0 0 1-42.666667-42.666667v-184.746666a59.306667 59.306667 0 0 0-59.306667-59.306667h-128a42.666667 42.666667 0 1 1 0-85.333333h128a144.64 144.64 0 0 1 144.64 144.64V853.333333a42.666667 42.666667 0 0 1-42.666666 42.666667zM362.666667 491.093333a195.84 195.84 0 1 1 195.413333-195.413333 195.84 195.84 0 0 1-195.413333 195.413333z m0-305.92a110.506667 110.506667 0 1 0 110.08 110.506667A110.506667 110.506667 0 0 0 362.666667 185.173333zM659.2 491.093333a42.666667 42.666667 0 0 1 0-85.333333 110.506667 110.506667 0 0 0 0-220.586667 42.666667 42.666667 0 0 1 0-85.333333 195.84 195.84 0 0 1 0 391.253333z\" fill=\"currentColor\" p-id=\"2963\"></path></svg>\n                        </span>\n                      </div>\n                      <div\n                        ng-class=\"{ 'active': (current_tag==4) && (window_url_stack.length ==0)}\"\n                        ng-click=\"showTag(4)\"\n                        class=\"settings is-setting\"\n                      >\n                      <svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"cog\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z\"></path></svg>\n                        <!-- <span class=\"icon li-setting\"></span> -->\n                      </div>\n                      <div ng-if=\"!isChrome && !isMac\" class=\"window-control\">\n                        <svg class=\"icon\" window-control=\"window_min\">\n                          <use href=\"#minimize-2\"></use>\n                        </svg>\n                        <svg class=\"icon\" window-control=\"window_max\">\n                          <use href=\"#maximize\"></use>\n                        </svg>\n                        <svg class=\"icon\" window-control=\"window_close\">\n                          <use href=\"#x\"></use>\n                        </svg>\n                      </div>\n                    </div>\n                    <div\n                    style=\"overflow-y: scroll;\"\n                      class=\"browser flex-scroll-wrapper\"\n                      infinite-scroll=\"scrolling()\"\n                      content-selector=\"'#playlist-content'\"\n                    >\n                      <div style=\"height: 64px;\"></div>\n                      <!-- hot playlist window-->\n                      <div\n                        class=\"page page-hot-playlist\"\n                        ng-show=\"current_tag==2 && is_window_hidden==1\"\n                        ng-controller=\"PlayListController\"\n                        ng-init=\"loadPlaylist();\"\n                      >\n                        <div class=\"source-list\" ng-show=\"is_window_hidden==1\">\n                          <div\n                            ng-repeat-start=\"source in ::sourceList\"\n                            class=\"source-button\"\n                            ng-class=\"{'active':tab === source.name}\"\n                            ng-click=\"changeTab(source.name)\"\n                          >\n                          <div class=\"buttontext\">\n                            {{source.displayText}}\n                          </div>\n                          </div>\n                          <div\n                            ng-repeat-end\n                            ng-if=\"!$last\"\n                            class=\"splitter\"\n                          ></div>\n                        </div>\n                        <div class=\"playlist-filter\">\n                          <div\n                            class=\"l1-button filter-item\"\n                            ng-repeat=\"filter in playlistFilters[tab] || []\"\n                            ng-click=\"changeFilter(filter.id)\"\n                            ng-class=\"{'active':filter.id === currentFilterId}\"\n                          >\n                            {{filter.name}}\n                          </div>\n                          <div\n                            class=\"l1-button filter-item\"\n                            ng-show=\"playlistFilters[tab] && playlistFilters[tab].length > 0\"\n                            ng-click=\"toggleMorePlaylists()\"\n                            ng-class=\"{'active':showMore}\"\n                          >\n                          <svg fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"ellipsis-h\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-ellipsis-h fa-w-16 fa-9x\"><path fill=\"currentColor\" d=\"M304 256c0 26.5-21.5 48-48 48s-48-21.5-48-48 21.5-48 48-48 48 21.5 48 48zm120-48c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48zm-336 0c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48z\" class=\"\"></path></svg>\n                          </div>\n                        </div>\n                        <div class=\"all-playlist-filter\" ng-show=\"showMore\">\n                          <div\n                            ng-repeat=\"category in allPlaylistFilters[tab] || []\"\n                            class=\"category\"\n                          >\n                            <div class=\"category-title\">\n                              {{category.category}}\n                            </div>\n                            <div class=\"category-filters\">\n                              <div\n                                class=\"filter-item\"\n                                ng-repeat=\"filter in category.filters\"\n                              >\n                                <span ng-click=\"changeFilter(filter.id)\" ng-class=\"{'active':filter.id === currentFilterId}\">\n                                  {{filter.name}}</span\n                                >\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                        <div class=\"site-wrapper-innerd\" id=\"hotplaylist\">\n                          <div class=\"cover-container\" id=\"playlist-content\">\n                            <ul  ng-style=\"{'padding-bottom':playlist.length == 0?'0px':'120px'}\" class=\"playlist-covers\">\n                              <li ng-repeat=\"i in result \">\n                                <div class=\"u-cover\">\n                                  <img\n                                  err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                    ng-src=\"{{i.cover_img_url}}\"\n                                    ng-click=\"showPlaylist(i.id)\"\n                                  />\n                                  <div\n                                      class=\"covershadow\"\n                                      style=\"background-image:url({{i.cover_img_url}});\"\n                                    ></div>\n                                  <div\n                                    class=\"bottom\"\n                                    ng-click=\"directplaylist(i.id)\"\n                                  >  \n                                  <svg  fill=\"currentColor\" style=\"height: 44%;width:44%;margin-left: 4px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" class=\"svg-inline--fa fa-play fa-w-14\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                                  </div>\n                                </div>\n                                <div class=\"desc\">\n                                  <span\n                                    class=\"title\"\n                                    ng-click=\"showPlaylist(i.id)\"\n                                    ><a href=\"\">{{i.title}}</a></span\n                                  >\n                                </div>\n                              </li>\n                              <!-- <div class=\"loading_bottom\">\n                                <img src=\"images/loading-1.gif\" height=\"40px\" />\n                              </div> -->\n                            </ul>\n                          </div>\n                        </div>\n                      </div>\n                      <!-- my platform window-->\n                      <div\n                        class=\"page page-hot-playlist\"\n                        ng-show=\"current_tag==6 && is_window_hidden==1\"\n                        ng-controller=\"PlatformController\"\n                      >\n                        <div class=\"source-list\" ng-show=\"is_window_hidden==1\">\n                          <div\n                            ng-repeat-start=\"source in ::platformSourceList\"\n                            class=\"source-button\"\n                            ng-class=\"{'active':tab === source.name}\"\n                            ng-click=\"changeTab(source.name)\"\n                          >\n                          <div class=\"buttontext\">\n                            {{source.displayText}}\n                          </div>\n                          </div>\n                          <div\n                            ng-repeat-end\n                            ng-if=\"!$last\"\n                            class=\"splitter\"\n                          ></div>\n                        </div>\n                        <div class=\"site-wrapper-innerd\" id=\"hotplaylist\">\n                          <div class=\"cover-container\" id=\"playlist-content\">\n                            <ul ng-style=\"{'padding-bottom':playlist.length == 0?'0px':'140px'}\"  class=\"playlist-covers\">\n                              <li ng-repeat=\"i in myPlatformPlaylists\">\n                                <div class=\"u-cover\">\n                                  <img\n                                  err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                    ng-src=\"{{i.cover_img_url}}\"\n                                    ng-click=\"showPlaylist(i.id)\"\n                                  />\n                                  <div\n                                      class=\"covershadow\"\n                                      style=\"background-image:url({{i.cover_img_url}});\"\n                                    ></div>\n                                  <div\n                                    class=\"bottom\"\n                                    ng-click=\"directplaylist(i.id)\"\n                                  >\n                                  <svg style=\"height: 44%;width:44%;margin-left: 4px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" class=\"svg-inline--fa fa-play fa-w-14\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                                  </div>\n                                </div>\n                                <div class=\"desc\">\n                                  <span\n                                    class=\"title\"\n                                    ng-click=\"showPlaylist(i.id)\"\n                                    >{{i.title}}</span\n                                  >\n                                </div>\n                              </li>\n                            </ul>\n                          </div>\n                        </div>\n                      </div>\n\n                      <!-- content page: 快速搜索 -->\n                      <div\n                        class=\"page\"\n                        ng-show=\"current_tag==3 && is_window_hidden==1\"\n                      >\n                        <div class=\"site-wrapper-innerd\">\n                          <div class=\"cover-container\">\n                            <!-- Initialize a new AngularJS app and associate it with a module named \"instantSearch\"-->\n                            <div ng-class=\"{footerdef:playlist.length == 0}\" class=\"searchbox\">\n                              <ul class=\"source-list\">\n                                <li\n                                  class=\"source-button\"\n                                  ng-class=\"{'active':tab === 'allmusic'}\"\n                                  ng-click=\"changeSourceTab('allmusic')\"\n                                >\n                                <div class=\"buttontext\">\n                                  <a>{{_ALL_MUSIC}}(Beta)</a>\n                                </div>\n                                </li>\n                                <div class=\"splitter\"></div>\n                                <div\n                                  ng-repeat-start=\"source in ::sourceList\"\n                                  class=\"source-button\"\n                                  ng-class=\"{'active':tab === source.name}\"\n                                  ng-click=\"changeSourceTab(source.name)\"\n                                >\n                                <div class=\"buttontext\">\n                                  {{source.displayText}}\n                                </div>\n                                </div>\n                                <div\n                                  ng-repeat-end\n                                  ng-if=\"!$last\"\n                                  class=\"splitter\"\n                                ></div>\n                                <svg\n                                  fill=\"currentColor\"\n                                  class=\"searchspinner\"\n                                  ng-show=\"loading\"\n                                  version=\"1.1\"\n                                  id=\"loader-1\"\n                                  xmlns=\"http://www.w3.org/2000/svg\"\n                                  xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n                                  x=\"0px\"\n                                  y=\"0px\"\n                                  width=\"40px\"\n                                  height=\"40px\"\n                                  viewBox=\"0 0 40 40\"\n                                  enable-background=\"new 0 0 40 40\"\n                                  xml:space=\"preserve\"\n                                >\n                                  <path\n                                    opacity=\"0.2\"\n                                    fill=\"currentColor\"\n                                    d=\"M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946 s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634 c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z\"\n                                  />\n                                  <path\n                                  fill=\"currentColor\"\n                                    d=\"M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0 C22.32,8.481,24.301,9.057,26.013,10.047z\"\n                                  >\n                                    <animateTransform\n                                      attributeType=\"xml\"\n                                      attributeName=\"transform\"\n                                      type=\"rotate\"\n                                      from=\"0 20 20\"\n                                      to=\"360 20 20\"\n                                      dur=\"0.6s\"\n                                      repeatCount=\"indefinite\"\n                                    />\n                                  </path>\n                                </svg>\n                                <div class=\"search-type\">\n                                  <li\n                                    class=\"source-button\"\n                                    ng-class=\"{'active':isSearchType(0)}\"\n                                    ng-click=\"changeSearchType(0)\"\n                                  >\n                                  <div class=\"buttontext\">\n                                    <a>单曲</a>\n                                  </div>\n                                  </li>\n                                  <div class=\"splitter\"></div>\n                                  <li\n                                    class=\"source-button\"\n                                    ng-class=\"{'active':isSearchType(1)}\"\n                                    ng-click=\"changeSearchType(1)\"\n                                  >\n                                  <div class=\"buttontext\">\n                                    <a>歌单</a>\n                                  </div>\n                                  </li>\n                                </div>\n                              </ul>\n                              <h1 style=\"\n                              margin-top: 32px;\n                              margin-bottom: 28px;\n                              margin-left: 26px;\n                              margin-right: 26px;\n                          \"><span style=\"opacity: .58;user-select: none;\">搜索 <span ng-show=\"isSearchType(0)\">歌曲</span><span ng-show=\"isSearchType(1)\">歌单</span> </span>\"{{keywords}}\"</h1>\n                              <ul ng-class=\"{'isSearch':isSearchType(1),isSearchOne:isSearchType(0)}\" class=\"detail-songlist\">\n                                <!-- <li class=\"head\" ng-if=\"searchType===0 \">\n                                  <div class=\"title\"><a>{{_SONGS}}</a></div>\n                                  <div class=\"artist\"><a>{{_ARTISTS}}</a></div>\n                                  <div class=\"album\"><a>{{_ALBUMS}}</a></div>\n                                  <div class=\"tools\">{{_OPERATION}}</div>\n                                </li> -->\n                                <!-- <li class=\"head\" ng-if=\"searchType===1 \">\n                                  <div class=\"title\">\n                                    <a>{{_PLAYLIST_TITLE}}</a>\n                                  </div>\n                                  <div class=\"artist\">\n                                    <a>{{_PLAYLIST_AUTHOR}}</a>\n                                  </div>\n                                  <div class=\"album\">\n                                    <a>{{_PLAYLIST_SONG_COUNT}}</a>\n                                  </div>\n                                </li> -->\n                                <li\n                                  ng-class=\"{ 'isSearchType':isSearchType(0),'playing': currentPlaying.id == song.id }\"\n                                  ng-if=\"searchType===0\"\n                                  ng-repeat=\"song in result\"\n                                  ng-mouseenter=\"options=true\"\n                                  ng-mouseleave=\"options=false\"\n                                  ng-dblclick=\"addAndPlay(song)\"\n                                >\n                                <img\n                                add-and-play=\"song\"\n                                        ng-src=\"{{ song.img_url }}?param=224y224\"\n                                        err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                      />\n                                      <div class=\"title-and-artist\">\n                                        <div class=\"container\">\n                                  <div class=\"title\">\n                                    <!-- <a ng-if=\"song.disabled\" class=\"disabled\" ng-click=\"copyrightNotice()\">{{ song.title |limitTo:30}}</a> -->\n                                    <a add-and-play=\"song\"\n                                      ><span\n                                        ng-if=\"isActiveTab('allmusic')\"\n                                        class=\"source\"\n                                        >{{song.sourceName}}</span\n                                      >{{ song.title |limitTo:30}}</a\n                                    >\n                                  </div>\n                                  <div class=\"artist\">\n                                    <a ng-click=\"showPlaylist(song.artist_id)\"\n                                      >{{ song.artist |limitTo:20}}</a\n                                    >\n                                  </div>\n                                </div>\n                              </div>\n                                  <div class=\"album\">\n                                    <a ng-click=\"showPlaylist(song.album_id)\"\n                                      >{{ song.album |limitTo:30}}</a\n                                    >\n                                  </div>\n\n                                  <div class=\"tools\">\n                                    <a\n                                      title=\"{{_ADD_TO_QUEUE}}\"\n                                      class=\"detail-add-button\"\n                                      add-without-play=\"song\"\n                                      ng-show=\"options\"\n                                      >\n                                      <svg t=\"1659670196701\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2178\" width=\"512\" height=\"512\"><path d=\"M938.516167 597.182834 85.483833 597.182834C38.527925 597.182834 0 559.256908 0 511.699001c0-46.955908 37.925926-85.483833 85.483833-85.483833l853.032334 0c46.955908 0 85.483833 37.925926 85.483833 85.483833C1024 559.256908 986.074074 597.182834 938.516167 597.182834L938.516167 597.182834 938.516167 597.182834zM512.300999 1024c-46.955908 0-85.483833-37.925926-85.483833-85.483833L426.817166 85.483833C426.817166 37.925926 464.743092 0 512.300999 0c46.955908 0 85.483833 37.925926 85.483833 85.483833l0 853.634333C597.182834 985.472075 559.256908 1024 512.300999 1024L512.300999 1024 512.300999 1024zM512.300999 1024\" fill=\"currentColor\" p-id=\"2179\"></path></svg>\n                                      </a>\n                                    <a\n                                      title=\"{{_ADD_TO_PLAYLIST}}\"\n                                      class=\"detail-fav-button\"\n                                      ng-show=\"options\"\n                                      ng-click=\"showDialog(0, song)\"\n                                      >\n                                      <svg t=\"1659670240206\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2332\" width=\"512\" height=\"512\"><path d=\"M768 768v192h-128v-192h-192v-128h192v-192h128v192h192v128h-192z m64-576H192v640h192v128H64V64h896v320h-128V192z\" fill=\"currentColor\" p-id=\"2333\"></path></svg></a>\n                                    <a\n                                      title=\"{{_REMOVE_FROM_PLAYLIST}}\"\n                                      class=\"detail-delete-button\"\n                                      ng-click=\"removeSongFromPlaylist(song, list_id)\"\n                                      ng-show=\"options && is_mine=='1' \"\n                                      >\n                                      <svg t=\"1659670296999\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2486\" width=\"512\" height=\"512\"><path d=\"M895.464448 119.006208 677.967872 119.006208c0 0-32.8448 1.020928-58.648576-26.943488-10.395648-12.050432-27.804672-23.795712-56.4224-24.799232l-41.183232 0-6.280192 0-41.182208 0c-28.618752 1.004544-46.031872 12.749824-56.4224 24.799232-25.807872 27.964416-58.6496 26.943488-58.6496 26.943488L141.682688 119.006208c-13.99296 0-25.33888 11.34592-25.33888 25.33888l0 93.090816c-0.053248 26.927104 26.083328 26.396672 26.083328 26.396672l49.83808 0L192.265216 913.65376c0 0-3.966976 44.084224 40.121344 46.45888l269.31712 0 33.738752 0 30.808064 0.238592 38.500352 0 174.934016 0 24.297472 0 0.782336-0.238592c44.080128-2.374656 40.117248-46.45888 40.117248-46.45888L844.88192 263.832576l49.842176 0c0 0 26.133504 0.530432 26.083328-26.396672l0-93.090816C920.8064 130.353152 909.46048 119.006208 895.464448 119.006208zM430.539776 803.171328c0 17.042432-13.828096 30.865408-30.865408 30.865408-17.042432 0-30.865408-13.824-30.865408-30.865408L368.80896 320.736256c0-17.042432 13.824-30.865408 30.865408-30.865408 17.038336 0 30.865408 13.824 30.865408 30.865408L430.539776 803.171328zM663.436288 803.171328c0 17.042432-13.824 30.865408-30.865408 30.865408-17.038336 0-30.865408-13.824-30.865408-30.865408L601.705472 320.736256c0-17.042432 13.828096-30.865408 30.865408-30.865408 17.041408 0 30.865408 13.824 30.865408 30.865408L663.436288 803.171328z\" fill=\"currentColor\" p-id=\"2487\"></path></svg>\n                                    </a>\n                                    <a\n                                      title=\"{{_ORIGIN_LINK}}\"\n                                      class=\"source-button\"\n                                      open-url=\"song.source_url\"\n                                      ng-show=\"options\"\n                                      >\n                                      <svg t=\"1659670349346\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2640\" width=\"512\" height=\"512\"><path d=\"M475.66342293 318.38570027A54.23369707 54.23369707 0 0 1 399.7362464 241.91618667L512 128.56775893a271.16848747 271.16848747 0 0 1 383.43224107 383.43224107L779.372128 625.8907648a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l113.89076373-113.8907648a162.70109227 162.70109227 0 0 0-229.9508768-229.9508768z m74.8425024 385.60158826A54.23369707 54.23369707 0 1 1 626.97543893 779.372128l-114.43310186 114.43310187A271.16848747 271.16848747 0 0 1 128.56775893 512L243.0008608 399.7362464a54.23369707 54.23369707 0 1 1 77.01185067 74.30016533l-114.97543894 114.43310187a162.70109227 162.70109227 0 0 0 229.9508768 229.9508768z m-135.0419072-17.89712a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l268.45680213-268.45680213a54.23369707 54.23369707 0 0 1 76.4695136 76.4695136z\" fill=\"currentColor\" p-id=\"2641\"></path></svg>\n                                      </a>\n                                    <!-- <a\n                                      title=\"{{_ORIGIN_LINK}}\"\n                                      class=\"source-button\"\n                                      ng-click=\"download_music(song.artist,song.title,song.url)\"\n                                      ng-show=\"options\" \n                                    >\n                                      <svg t=\"1659785735308\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2358\" width=\"512\" height=\"512\"><path d=\"M828.975746 894.125047 190.189132 894.125047c-70.550823 0-127.753639-57.18542-127.753639-127.752616L62.435493 606.674243c0-17.634636 14.308891-31.933293 31.93227-31.933293l63.889099 0c17.634636 0 31.93227 14.298658 31.93227 31.933293l0 95.821369c0 35.282574 28.596292 63.877843 63.87682 63.877843L765.098927 766.373455c35.281551 0 63.87682-28.595268 63.87682-63.877843l0-95.821369c0-17.634636 14.298658-31.933293 31.943526-31.933293l63.877843 0c17.634636 0 31.933293 14.298658 31.933293 31.933293l0 159.699212C956.729385 836.939627 899.538849 894.125047 828.975746 894.125047L828.975746 894.125047zM249.938957 267.509636c12.921287-12.919241 33.884738-12.919241 46.807049 0l148.97087 148.971893L445.716876 94.89323c0-17.634636 14.300704-31.94762 31.933293-31.94762l63.875796 0c17.637706 0 31.945573 14.312984 31.945573 31.94762l0 321.588299 148.97087-148.971893c12.921287-12.919241 33.875528-12.919241 46.796816 0l46.814212 46.818305c12.921287 12.922311 12.921287 33.874505 0 46.807049L552.261471 624.930025c-1.140986 1.137916-21.664416 13.68365-42.315758 13.69286-20.87647 0.010233-41.878806-12.541641-43.020816-13.69286L203.121676 361.13499c-12.922311-12.933567-12.922311-33.884738 0-46.807049L249.938957 267.509636 249.938957 267.509636z\" fill=\"currentColor\" p-id=\"2359\"></path></svg>\n                                    </a> -->\n                                  </div>\n                                </li>\n                                <li\n                                  ng-if=\"searchType===1\"\n                                  ng-repeat=\"playlist in result\"\n                                  ng-class-odd=\"'odd'\"\n                                  ng-class-even=\"'even'\"\n                                  class=\"playlist-result isSearchGeDan\"\n                                >\n                                <!-- <a ng-click=\"showPlaylist(playlist.id)\"> -->\n                                  <div class=\"u-cover\">\n                                  <img\n                                      ng-click=\"showPlaylist(playlist.id)\"\n                                      ng-src=\"{{ playlist.img_url }}?param=512y512\"\n                                      err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                      />\n                                      <div class=\"covershadow\" style=\"background-image:url({{playlist.img_url}}?param=512y512);\"></div>\n                                      <div \n                                      ng-click=\"directplaylist(playlist.id)\"\n                                      class=\"bottom\" style=\"height:50px;width:50px\">\n                                        <svg fill=\"currentColor\" style=\"height: 44%;width:44%;margin-left: 4px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                                      </div>\n                                    </div>\n                                  <div class=\"desc\">\n                                      <div\n                                      ng-click=\"showPlaylist(playlist.id)\"\n                                      class=\"title\">\n                                        <a href=\"\">{{ playlist.title |limitTo:30}}</a>  <span\n                                          ng-if=\"isActiveTab('allmusic')\"\n                                          class=\"source playlist\"\n                                          >  {{playlist.sourceName}}</span\n                                        >\n                                      </div>\n                                      <div class=\"artist\">\n                                        {{ playlist.author |limitTo:20}}\n                                      </div>\n                                  </div>\n                                  \n                                  <!-- <div class=\"album\">\n                                    {{ playlist.count |limitTo:30}}\n                                  </div> -->\n                                <!-- </a> -->\n                                </li>\n                              </ul>\n                              <div\n                                class=\"search-pagination\"\n                                ng-show=\"totalpage>1\"\n                                pagination\n                              ></div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n\n                      <!-- content page: 设置 -->\n                      <div\n                        class=\"page\"\n                        ng-show=\"current_tag==4 && is_window_hidden==1\"\n                        ng-init=\"lastfm.updateStatus(); updateGithubStatus();\"\n                      >\n                        <div class=\"site-wrapper-innerd\">\n                          <div class=\"cover-container\">\n                            <div class=\"settings-title\">\n                              <span>{{_LANGUAGE}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div>\n                                <button\n                                  class=\"language-button\"\n                                  ng-click=\"setLang('zh-CN')\"\n                                >\n                                  简体中文\n                                </button>\n                                <button\n                                  class=\"language-button\"\n                                  ng-click=\"setLang('zh-TC')\"\n                                >\n                                  繁体中文\n                                </button>\n                                <button\n                                  class=\"language-button\"\n                                  ng-click=\"setLang('en-US')\"\n                                >\n                                  English\n                                </button>\n                                <button\n                                  class=\"language-button\"\n                                  ng-click=\"setLang('fr-FR')\"\n                                >\n                                  French\n                                </button>\n                                <button\n                                  class=\"language-button\"\n                                  ng-click=\"setLang('ko-KR')\"\n                                >\n                                  Korean\n                                </button>\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_THEME}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div>\n                                <button\n                                  class=\"theme-button\"\n                                  ng-click=\"setTheme('white')\"\n                                >\n                                  {{_THEME_WHITE}}\n                                </button>\n                                <button\n                                  class=\"theme-button\"\n                                  ng-click=\"setTheme('black')\"\n                                >\n                                  {{_THEME_BLACK}}\n                                </button>\n                                <button\n                                class=\"theme-button\"\n                                ng-click=\"setTheme('white2')\"\n                              >\n                                {{_THEME_MODERN_WHITE}}\n                              </button>\n                              <button\n                                class=\"theme-button\"\n                                ng-click=\"setTheme('black2')\"\n                              >\n                                {{_THEME_MODERN_BLACK}}\n                              </button>\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_AUTO_CHOOSE_SOURCE}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div\n                                class=\"shortcut\"\n                                class=\"btn btn-primary confirm-button\"\n                              >\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableAutoChooseSource\"\n                                  ng-click=\"setAutoChooseSource(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableAutoChooseSource\"\n                                  ng-click=\"setAutoChooseSource(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                {{_AUTO_CHOOSE_SOURCE_NOTICE}}\n                              </div>\n                              <div\n                                class=\"search-description\"\n                                ng-show=\"enableAutoChooseSource\"\n                              >\n                                {{_AUTO_CHOOSE_SOURCE_LIST}}\n                              </div>\n                              <div\n                                class=\"search-source-list\"\n                                ng-show=\"enableAutoChooseSource\"\n                              >\n                                <div\n                                  ng-repeat=\"item in sourceList\"\n                                  class=\"search-source\"\n                                >\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"autoChooseSourceList.indexOf(item.name) === -1\"\n                                    ng-click=\"enableSource(item.name)\"\n                                  >\n                                    <use href=\"#square\"></use>\n                                  </svg>\n                                  <svg\n                                    class=\"feather\"\n                                    ng-show=\"autoChooseSourceList.indexOf(item.name) > -1\"\n                                    ng-click=\"disableSource(item.name)\"\n                                  >\n                                    <use href=\"#check-square\"></use>\n                                  </svg>\n                                  {{item.displayText}}\n                                </div>\n                              </div>\n                            </div>\n                            <div ng-if=\"isChrome\" class=\"settings-title\">\n                              <span\n                                >{{_CLOSE_TAB_ACTION}}({{_VALID_AFTER_RESTART}})</span\n                              >\n                            </div>\n                            <div ng-if=\"isChrome\" class=\"settings-content\">\n                              <div class=\"shortcut\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableStopWhenClose\"\n                                  ng-click=\"setStopWhenClose(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableStopWhenClose\"\n                                  ng-click=\"setStopWhenClose(false)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                <span style=\"margin-right: 20px\"\n                                  >{{_QUIT_APPLICATION}}</span\n                                >\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableStopWhenClose\"\n                                  ng-click=\"setStopWhenClose(false)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableStopWhenClose\"\n                                  ng-click=\"setStopWhenClose(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                <span> {{_MINIMIZE_TO_BACKGROUND}}</span>\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_NOWPLAYING_DISPLAY}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div class=\"shortcut\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableNowplayingCoverBackground\"\n                                  ng-click=\"setNowplayingCoverBackground(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableNowplayingCoverBackground\"\n                                  ng-click=\"setNowplayingCoverBackground(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                {{_NOWPLAYING_COVER_BACKGROUND_NOTICE}}\n                              </div>\n                              <div class=\"shortcut\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableNowplayingBitrate\"\n                                  ng-click=\"setNowplayingBitrate(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableNowplayingBitrate\"\n                                  ng-click=\"setNowplayingBitrate(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                {{_NOWPLAYING_BITRATE_NOTICE}}\n                              </div>\n                              <div class=\"shortcut\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableNowplayingPlatform\"\n                                  ng-click=\"setNowplayingPlatform(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableNowplayingPlatform\"\n                                  ng-click=\"setNowplayingPlatform(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                {{_NOWPLAYING_PLATFORM_NOTICE}}\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_LYRIC_DISPLAY}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div class=\"shortcut\" ng-if=\"!isChrome\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableLyricFloatingWindow\"\n                                  ng-click=\"openLyricFloatingWindow(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableLyricFloatingWindow\"\n                                  ng-click=\"openLyricFloatingWindow(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                <span ng-show=\"enableLyricFloatingWindow\"></span\n                                >{{_SHOW_DESKTOP_LYRIC}}\n                              </div>\n                              <div class=\"shortcut\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableLyricTranslation\"\n                                  ng-click=\"toggleLyricTranslation()\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableLyricTranslation\"\n                                  ng-click=\"toggleLyricTranslation()\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                <span ng-show=\"enableLyricTranslation\"></span\n                                >{{_SHOW_LYRIC_TRANSLATION}}\n                              </div>\n                              <div class=\"shortcut\" ng-if=\"!isChrome\">\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableLyricFloatingWindowTranslation\"\n                                  ng-click=\"toggleLyricFloatingWindowTranslation()\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableLyricFloatingWindowTranslation\"\n                                  ng-click=\"toggleLyricFloatingWindowTranslation()\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                <span\n                                  ng-show=\"enableLyricFloatingWindowTranslation\"\n                                ></span\n                                >{{_SHOW_DESKTOP_LYRIC_TRANSLATION}}\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_BACKUP_PLAYLIST}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <p>{{_BACKUP_WARNING}}</p>\n                              <div>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-click=\"backupMySettings()\"\n                                >\n                                  {{_EXPORT_TO_LOCAL_FILE}}\n                                </button>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-show=\"githubStatus == 2\"\n                                  ng-click=\"showDialog(8)\"\n                                >\n                                  {{_EXPORT_TO_GITHUB_GIST}}\n                                </button>\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_RECOVER_PLAYLIST}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <p>{{_RECOVER_WARNING}}</p>\n                              <label class=\"upload-button\" for=\"my-file-selector\">\n                                <input\n                                  id=\"my-file-selector\"\n                                  type=\"file\"\n                                  style=\"display: none\"\n                                  ng-model=\"myuploadfiles\"\n                                  custom-on-change=\"importMySettings\"\n                                />{{_RECOVER_FROM_LOCAL_FILE}}\n                              </label>\n                              <button\n                                class=\"btn btn-warning confirm-button\"\n                                ng-show=\"githubStatus == 2\"\n                                ng-click=\"showDialog(10)\"\n                              >\n                                {{_RECOVER_FROM_GITHUB_GIST}}\n                              </button>\n                            </div>\n\n                            <div class=\"settings-title\">\n                              <span>{{_CONNECT_TO_GITHUB}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div>\n                                <p>{{_STATUS}}：{{ githubStatusText }}</p>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-show=\"githubStatus == 0\"\n                                  ng-click=\"openGithubAuth(); showDialog(7);\"\n                                >\n                                  {{_CONNECT_TO_GITHUB}}\n                                </button>\n                                <button\n                                  class=\"btn btn-warning confirm-button\"\n                                  ng-show=\"githubStatus == 1\"\n                                  ng-click=\"showDialog(7);\"\n                                >\n                                  {{_RECONNECT}}\n                                </button>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-show=\"githubStatus == 2\"\n                                  ng-click=\"GithubLogout();\"\n                                >\n                                  {{_CANCEL_CONNECT}}\n                                </button>\n                              </div>\n                            </div>\n\n                            <div class=\"settings-title\">\n                              <span>{{_CONNECT_TO_LASTFM}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div>\n                                <p>{{_STATUS}}：{{ lastfm.getStatusText() }}</p>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-show=\"!lastfm.isAuthRequested()\"\n                                  ng-click=\"lastfm.getAuth(); showDialog(4);\"\n                                >\n                                  {{_CONNECT_TO_LASTFM}}\n                                </button>\n                                <button\n                                  class=\"btn btn-warning confirm-button\"\n                                  ng-show=\"lastfm.isAuthRequested() && !lastfm.isAuthorized()\"\n                                  ng-click=\"lastfm.getAuth(); showDialog(4);\"\n                                >\n                                  {{_RECONNECT}}\n                                </button>\n                                <button\n                                  class=\"btn btn-primary confirm-button\"\n                                  ng-show=\"lastfm.isAuthRequested()\"\n                                  ng-click=\"lastfm.cancelAuth();\"\n                                >\n                                  {{_CANCEL_CONNECT}}\n                                </button>\n                              </div>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_SHORTCUTS}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <div class=\"shortcut_table\">\n                                <div class=\"shortcut_table-header\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_SHORTCUTS_FUNCTION}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">\n                                    {{_SHORTCUTS}}\n                                  </div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    {{_GLOBAL_SHORTCUTS}}\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_PLAY_OR_PAUSE}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">p</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    Ctrl(Cmd) + Alt + {{_KEYBOARD_SPACE}}\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_PREVIOUS_TRACK}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">[</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    Ctrl(Cmd) + Alt + ←\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_NEXT_TRACK}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">]</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    Ctrl(Cmd) + Alt + →\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_VOLUME_UP}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">u</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    {{_SHORTCUTS_NOT_SET}}\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_VOLUME_DOWN}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">d</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    {{_SHORTCUTS_NOT_SET}}\n                                  </div>\n                                </div>\n                                <!-- <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    静音/取消静音\n                                  </div>\n                                  <div class=\"shortcut_table-key\">m</div>\n                                  <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                    全局快捷键\n                                  </div>\n                                </div> -->\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{_QUICK_SEARCH}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">f</div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    {{_SHORTCUTS_NOT_SET}}\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    {{ZOOM_IN_OUT}}\n                                  </div>\n                                  <div class=\"shortcut_table-key\">\n                                    Ctrl(Cmd) + +/-\n                                  </div>\n                                  <div\n                                    ng-if=\"!isChrome\"\n                                    class=\"shortcut_table-globalkey\"\n                                  >\n                                    {{_SHORTCUTS_NOT_SET}}\n                                  </div>\n                                </div>\n                                <!-- <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    打开/关闭播放列表\n                                  </div>\n                                  <div class=\"shortcut_table-key\">l</div>\n                                  <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                    全局快捷键\n                                  </div>\n                                </div>\n                                <div class=\"shortcut_table-line\">\n                                  <div class=\"shortcut_table-function\">\n                                    切换播放模式\n                                  </div>\n                                  <div class=\"shortcut_table-key\">s</div>\n                                  <div ng-if=\"!isChrome\" class=\"shortcut_table-globalkey\">\n                                    全局快捷键\n                                  </div>\n                                </div> -->\n                              </div>\n                              <div\n                                class=\"shortcut\"\n                                ng-if=\"!isChrome\"\n                                class=\"btn btn-primary confirm-button\"\n                              >\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"!enableGlobalShortCut\"\n                                  ng-click=\"applyGlobalShortcut(true)\"\n                                >\n                                  <use href=\"#square\"></use>\n                                </svg>\n                                <svg\n                                  class=\"feather\"\n                                  ng-show=\"enableGlobalShortCut\"\n                                  ng-click=\"applyGlobalShortcut(true)\"\n                                >\n                                  <use href=\"#check-square\"></use>\n                                </svg>\n                                {{_GLOBAL_SHORTCUTS_NOTICE}}\n                              </div>\n                            </div>\n                            <div class=\"settings-title\" ng-if=\"!isChrome\">\n                              <span>{{_PROXY_CONFIG}}</span>\n                            </div>\n                            <div class=\"settings-content\" ng-if=\"!isChrome\">\n                              <span>{{_PROXY_CONFIG}}:</span>\n                              {{proxyMode.displayText}}\n                              <span ng-show=\"proxyMode.name=='custom'\"\n                                >{{proxyRules}}</span\n                              >\n                              <button ng-click=\"showDialog(12)\">\n                                {{_MODIFY}}\n                              </button>\n                            </div>\n                            <div class=\"settings-title\">\n                              <span>{{_ABOUT}}</span>\n                            </div>\n                            <div class=\"settings-content\">\n                              <p>\n                                Listen 1 {{_HOMEPAGE}}:\n                                <a\n                                  open-url=\"'https://listen1.github.io/listen1/'\"\n                                >\n                                  https://listen1.github.io/listen1/\n                                </a>\n                              </p>\n                              <p>Listen 1 {{_EMAIL}}: githublisten1@gmail.com</p>\n                              <p>\n                                {{_FEEDBACK}}:\n                                <a\n                                  ng-if=\"isChrome\"\n                                  open-url=\"'https://github.com/listen1/listen1_chrome_extension/issues'\"\n                                  >https://github.com/listen1/listen1_chrome_extension/issues</a\n                                >\n                                <a\n                                  ng-if=\"!isChrome\"\n                                  open-url=\"'https://github.com/listen1/listen1_desktop/issues'\"\n                                  >https://github.com/listen1/listen1_desktop/issues</a\n                                >\n                              </p>\n                              <p>{{_DESIGNER}} ({{_THEME_WHITE}}): iparanoid </p>\n                              <p>{{_DESIGNER}} ({{_THEME_MODERN_BLACK}}, {{_THEME_MODERN_WHITE}}): 814959822, Antion</p>\n                              <p>{{_VERSION}}: v2.33.0 {{_LICENSE_NOTICE}}</p>\n                              <p ng-show='lastestVersion!=\"\"'>\n                                {{_LATEST_VERSION}}: {{lastestVersion}}\n                              </p>\n                            </div>\n                          <div style=\"transition: 0.3s;\" ng-style=\"{'padding-bottom':playlist.length == 0?'13px':'120px'}\"></div>\n                          </div>\n                        </div>\n                      </div>\n                      <!-- content page: 登录 -->\n                      <div\n                        class=\"page\"\n                        ng-show=\"current_tag==5 && is_window_hidden==1\"\n                      >\n                        <div class=\"login\">\n                          <div ng-repeat=\"source in loginSourceList\">\n                            <div ng-show=\"is_login(source)\">\n                              <div style=\"width: 500px;background-color: var(--theme-color-hover);\n                              color: var(--theme-color);cursor: default;\" class=\"usercard\">\n                                <img \n                                err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                ng-src=\"{{musicAuth[source].avatar}}\" />\n                                <div class=\"usercard-title\">\n                                  <div class=\"usercard-nickname\">\n                                    {{musicAuth[source].nickname}}\n                                  </div>\n                                  <div ng-if=\"source=='netease'\" class=\"usercard-info\">网易云音乐</div>\n                                  <div ng-if=\"source=='qq'\" class=\"usercard-info\">QQ音乐</div>\n                                  <div ng-if=\"source=='migu'\" class=\"usercard-info\">咪咕音乐</div>\n                                </div>\n                                <button ng-click=\"logout(source)\">\n                                  {{_LOGOUT}}\n                                </button>\n                              </div>\n                            </div>\n                            <div ng-show=\"!is_login(source)\">\n                              <div ng-click=\" openLogin(source);showDialog(11, source);\" class=\"usercard\">\n                                <div class=\"logoin-icon\">\n                                <svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"sign-in-alt\" class=\"svg-inline--fa fa-sign-in-alt fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M416 448h-84c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h84c17.7 0 32-14.3 32-32V160c0-17.7-14.3-32-32-32h-84c-6.6 0-12-5.4-12-12V76c0-6.6 5.4-12 12-12h84c53 0 96 43 96 96v192c0 53-43 96-96 96zm-47-201L201 79c-15-15-41-4.5-41 17v96H24c-13.3 0-24 10.7-24 24v96c0 13.3 10.7 24 24 24h136v96c0 21.5 26 32 41 17l168-168c9.3-9.4 9.3-24.6 0-34z\"></path></svg>\n                              </div>\n                                <!-- <img \n                                err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\"\n                                src=\"images/placeholder.png\" /> -->\n\n                                <div class=\"usercard-title\">\n                                  <div class=\"usercard-nickname\">\n                                    {{_NOT_LOGIN_NICKNAME}}\n                                  </div>\n                                  <div ng-if=\"source=='netease'\" class=\"usercard-info\">网易云音乐</div>\n                                  <div ng-if=\"source=='qq'\" class=\"usercard-info\">QQ音乐</div>\n                                  <div ng-if=\"source=='migu'\" class=\"usercard-info\">咪咕音乐</div>\n                                </div>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n\n                      <!-- track list window-->\n                      <div class=\"page\">\n                        <div\n                          class=\"playlist-detail\"\n                          ng-show=\"is_window_hidden!=1 && window_type=='list'\"\n                        >\n                          <div class=\"detail-head\">\n                            <div class=\"detail-head-cover\">\n                              <img\n                                ng-src=\"{{ cover_img_url.replace('/300/','/500/').replace('/240/','/512/').replace('/T002R300x300M','/T002R800x800M') }}\"\n                                err-src=\"https://y.gtimg.cn/mediastyle/global/img/singer_300.png\"\n                              />\n                              <div\n                              class=\"covershadow\"\n                              style=\"background-image:url({{cover_img_url}});opacity: 1;\"\n                            ></div>\n                            <div\n                            class=\"bottom\"\n                            ng-click=\"playMylist(list_id)\"\n                          >  \n                          <svg style=\"height: 44%;width:44%;margin-left: 4px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                          </div>\n                            </div>\n                            <div class=\"detail-head-title\">\n                              <h2>{{ playlist_title }}</h2>\n                              <div class=\"playlist-button-list\">\n                                <div style=\"padding: 0;height: auto;background-color: var(--theme-color-hover);color: var(--theme-color);\" class=\"playlist-button playadd-button\">\n                                  <div\n                                  style=\"padding: 8px 0 8px 16px;width: 100px;\"\n                                    class=\"play-list\"\n                                    ng-click=\"playMylist(list_id)\"\n                                  >\n                                  <svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" class=\"icon\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                                    {{_PLAY_ALL}}\n                                  </div>\n                                  <div\n                                  style=\"padding: 8px 16px 8px 0px;\"\n                                    class=\"add-list\"\n                                    ng-click=\"addMylist(list_id)\"\n                                  >\n                                    <svg t=\"1659628019588\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1723\" width=\"512\" height=\"512\"><path d=\"M938.516167 597.182834 85.483833 597.182834C38.527925 597.182834 0 559.256908 0 511.699001c0-46.955908 37.925926-85.483833 85.483833-85.483833l853.032334 0c46.955908 0 85.483833 37.925926 85.483833 85.483833C1024 559.256908 986.074074 597.182834 938.516167 597.182834L938.516167 597.182834 938.516167 597.182834zM512.300999 1024c-46.955908 0-85.483833-37.925926-85.483833-85.483833L426.817166 85.483833C426.817166 37.925926 464.743092 0 512.300999 0c46.955908 0 85.483833 37.925926 85.483833 85.483833l0 853.634333C597.182834 985.472075 559.256908 1024 512.300999 1024L512.300999 1024 512.300999 1024zM512.300999 1024\" fill=\"currentColor\" p-id=\"1724\"></path></svg>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button clone-button\"\n                                  ng-show=\"is_local\"\n                                  ng-click=\"addLocalMusic(list_id)\"\n                                >\n                                  <div class=\"play-list\">\n                                    <span class=\"icon li-songlist\"></span\n                                    ><span>{{_ADD_LOCAL_SONGS}}</span>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button clone-button\"\n                                  ng-show=\"!is_mine && !is_local\"\n                                  ng-click=\"clonePlaylist(list_id)\"\n                                >\n                                  <div class=\"play-list\">\n                                    <span class=\"icon li-songlist\"></span\n                                    ><span>{{_ADD_TO_PLAYLIST}}</span>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button edit-button\"\n                                  ng-show=\"is_mine && !is_local\"\n                                  ng-click=\"showDialog(3, {list_id: list_id, playlist_title: playlist_title, cover_img_url: cover_img_url})\"\n                                >\n                                  <div class=\"play-list\">\n                                    <svg t=\"1659668764539\" class=\"icon feather\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1964\" width=\"512\" height=\"512\"><path d=\"M943.104 216.064q-8.192 9.216-15.36 16.384l-12.288 12.288q-6.144 6.144-11.264 10.24l-138.24-139.264q8.192-8.192 20.48-19.456t20.48-17.408q20.48-16.384 44.032-14.336t37.888 9.216q15.36 8.192 34.304 28.672t29.184 43.008q5.12 14.336 6.656 33.792t-15.872 36.864zM551.936 329.728l158.72-158.72 138.24 138.24q-87.04 87.04-158.72 157.696-30.72 29.696-59.904 58.88t-53.248 52.224-39.424 38.4l-18.432 18.432q-7.168 7.168-16.384 14.336t-20.48 12.288-31.232 12.288-41.472 13.824-40.96 12.288-29.696 6.656q-19.456 2.048-20.992-3.584t1.536-25.088q1.024-10.24 5.12-30.208t8.192-40.448 8.704-38.4 7.68-25.088q5.12-11.264 10.752-19.456t15.872-18.432zM899.072 478.208q21.504 0 40.96 10.24t19.456 41.984l0 232.448q0 28.672-10.752 52.736t-29.184 41.984-41.984 27.648-48.128 9.728l-571.392 0q-24.576 0-48.128-10.752t-41.472-29.184-29.184-43.52-11.264-53.76l0-570.368q0-20.48 11.264-42.496t29.184-39.936 40.448-29.696 45.056-11.776l238.592 0q28.672 0 40.448 20.992t11.776 42.496-11.776 41.472-40.448 19.968l-187.392 0q-21.504 0-34.816 14.848t-13.312 36.352l0 481.28q0 20.48 13.312 34.304t34.816 13.824l474.112 0q21.504 0 36.864-13.824t15.36-34.304l0-190.464q0-14.336 6.656-24.576t16.384-16.384 21.504-8.704 23.04-2.56z\" fill=\"currentColor\" p-id=\"1965\"></path></svg>\n                                    \n                                    <span>{{_EDIT}}</span>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button fav-button\"\n                                  ng-show=\"!is_mine && !is_local\"\n                                  ng-click=\"favoritePlaylist(list_id)\"\n                                  ng-class=\"{'favorited':is_favorite}\"\n                                >\n                                  <div\n                                    class=\"play-list\"\n                                    ng-class=\"{'favorited':is_favorite,'notfavorite':!is_favorite}\"\n                                  >\n                                  <svg ng-if=\"!is_favorite\" t=\"1659664836199\" class=\"icon feather\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1992\" width=\"512\" height=\"512\"><path d=\"M550.315 822.582l229.558 125.937-43.042-266.223c-4.783-30.289 4.782-62.172 25.506-82.896l183.328-188.11-256.659-39.853c-28.694-3.188-54.2-23.912-68.548-51.013l-111.59-242.31L395.682 318.83c-12.754 28.695-38.26 47.825-68.549 51.013L70.476 411.29l184.921 189.704c22.319 20.724 30.29 52.607 25.507 82.895l-41.448 264.63 229.558-125.938c23.912-15.942 55.795-15.942 81.301 0z m-49.418 65.36L271.339 1013.88c-36.665 19.13-79.708 6.376-97.243-31.883-7.97-14.348-11.16-31.883-7.97-47.825l43.041-264.629c1.594-6.377-1.594-12.753-4.782-17.536L22.652 465.492c-28.695-28.695-28.695-76.52-1.595-106.808 11.16-11.16 25.507-19.13 41.448-22.318l256.658-39.854c4.783 0 11.16-6.377 14.348-9.565l113.184-242.31c17.536-38.26 60.578-52.608 97.244-33.478 14.347 7.97 25.506 19.13 31.883 33.477l111.59 242.311c1.594 6.377 7.97 9.565 14.348 9.565l256.658 39.854c39.853 6.376 66.954 43.042 60.577 84.49-1.594 17.535-9.564 31.883-22.318 43.042L814.944 653.602c-3.188 3.188-4.782 11.159-4.782 17.535l43.042 266.223c7.97 41.448-19.13 79.708-58.984 86.084-15.941 1.595-31.883 0-44.636-7.97L520.026 889.536c-6.376-3.188-14.347-3.188-19.13-1.594z\"  fill=\"currentColor\" p-id=\"1993\"></path></svg>\n                                  <svg ng-if=\"is_favorite\"  t=\"1659665365620\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2146\" width=\"512\" height=\"512\"><path d=\"M961.161 334.715l-255.883-39.982c-7.996 0-12.794-4.798-14.393-9.596L578.935 42.048c-7.996-14.393-19.19-27.187-31.985-33.585-36.783-19.19-79.963-4.797-97.555 33.585l-113.548 241.49c-4.798 4.798-7.997 9.596-14.394 9.596L62.372 333.115c-15.993 4.798-30.387 11.195-41.581 22.39-27.188 30.386-27.188 79.964 1.599 107.151L204.707 651.37c4.797 4.798 6.397 11.195 6.397 15.993l-43.18 265.479c-4.798 14.393 0 31.985 6.396 47.978 17.592 36.783 60.773 51.176 97.556 31.985L502.17 886.463c6.397-1.6 12.794-1.6 19.191 0l228.696 126.342c12.794 7.996 28.787 9.596 44.78 7.996 39.981-7.996 67.169-44.78 60.771-86.36l-43.18-265.479c0-6.397 1.6-12.794 4.798-15.993l185.515-188.714c12.795-11.194 20.79-27.187 20.79-43.18 4.799-41.58-22.389-79.963-62.37-86.36z\" fill=\"currentColor\"  p-id=\"2147\"></path></svg>  \n                                \n                                    <span\n                                      >{{is_favorite?_FAVORITED:_FAVORITE}}</span\n                                    >\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button edit-button\"\n                                  ng-show=\"isChrome && is_favorite && !is_local\"\n                                  ng-click=\"closeWindow();showPlaylist(list_id)\"\n                                >\n                                  <div class=\"play-list\">\n                                    \n                                    <svg t=\"1659669143423\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2024\" width=\"512\" height=\"512\"><path d=\"M684.032 403.456q-17.408-8.192-15.872-22.016t11.776-22.016q3.072-2.048 19.968-15.872t41.472-33.28q-43.008-49.152-102.4-77.312t-129.024-28.16q-64.512 0-120.832 24.064t-98.304 66.048-66.048 98.304-24.064 120.832q0 63.488 24.064 119.808t66.048 98.304 98.304 66.048 120.832 24.064q53.248 0 100.864-16.896t87.04-47.616 67.584-72.192 41.472-90.624q7.168-23.552 26.624-38.912t46.08-15.36q31.744 0 53.76 22.528t22.016 53.248q0 14.336-5.12 27.648-21.504 71.68-63.488 132.096t-99.84 103.936-128.512 68.096-148.48 24.576q-95.232 0-179.2-35.84t-145.92-98.304-98.304-145.92-36.352-178.688 36.352-179.2 98.304-145.92 145.92-98.304 179.2-36.352q105.472 0 195.584 43.52t153.6 118.272q23.552-17.408 39.424-30.208t19.968-15.872q6.144-5.12 13.312-7.68t13.312 0 10.752 10.752 6.656 24.576q1.024 9.216 2.048 31.232t2.048 51.2 1.024 60.416-1.024 58.88q-1.024 34.816-16.384 50.176-8.192 8.192-24.576 9.216t-34.816-3.072q-27.648-6.144-60.928-13.312t-63.488-14.848-53.248-14.336-29.184-9.728z\" fill=\"currentColor\" p-id=\"2025\"></path></svg>\n                                    <span>{{_REFRESH_PLAYLIST}}</span>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button edit-button\"\n                                  ng-show=\"!is_mine && !is_local\"\n                                  open-url=\"playlist_source_url\"\n                                >\n                                  <div class=\"play-list\">\n                                    \n                                    <svg t=\"1659628952356\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1783\" width=\"512\" height=\"512\"><path d=\"M475.66342293 318.38570027A54.23369707 54.23369707 0 0 1 399.7362464 241.91618667L512 128.56775893a271.16848747 271.16848747 0 0 1 383.43224107 383.43224107L779.372128 625.8907648a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l113.89076373-113.8907648a162.70109227 162.70109227 0 0 0-229.9508768-229.9508768z m74.8425024 385.60158826A54.23369707 54.23369707 0 1 1 626.97543893 779.372128l-114.43310186 114.43310187A271.16848747 271.16848747 0 0 1 128.56775893 512L243.0008608 399.7362464a54.23369707 54.23369707 0 1 1 77.01185067 74.30016533l-114.97543894 114.43310187a162.70109227 162.70109227 0 0 0 229.9508768 229.9508768z m-135.0419072-17.89712a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l268.45680213-268.45680213a54.23369707 54.23369707 0 0 1 76.4695136 76.4695136z\" fill=\"currentColor\" p-id=\"1784\"></path></svg>\n                                    <span>{{_ORIGIN_LINK}}</span>\n                                  </div>\n                                </div>\n                                <div\n                                  class=\"playlist-button edit-button\"\n                                  ng-show=\"is_mine && !is_local\"\n                                  ng-click=\"showDialog(6)\"\n                                >\n                                  <div class=\"play-list\">\n                                    <svg t=\"1659668939470\" class=\"icon feather\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2273\" width=\"512\" height=\"512\"><path d=\"M725.333333 938.666667H298.666667a256 256 0 0 1-256-256V341.333333h128v298.666667a170.666667 170.666667 0 0 0 170.666666 170.666667h341.333334a170.666667 170.666667 0 0 0 170.666666-170.666667V341.333333h128v341.333334a256 256 0 0 1-256 256zM256 426.666667h128V213.333333h256v213.333334h128l-256 256z m128-341.333334h256v85.333334H384V85.333333z\" fill=\"currentColor\" p-id=\"2274\"></path></svg>\n                                  \n                                    <span>{{_IMPORT}}</span>\n                                  </div>\n                                </div>\n                              </div>\n                            </div>\n                          </div>\n\n                          <ul class=\"detail-songlist playlist-songlist\"  ng-style=\"{'padding-bottom':playlist.length == 0?'13px':'120px'}\">\n                            <div class=\"playlist-search\">\n                              <svg  fill=\"currentColor\" style=\"opacity: 0.28;margin-right: 4px;width: 15px;height: 15px;cursor: default;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"search\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z\"></path></svg>\n                              <input\n                                class=\"form-control playlist-search-input\"\n                                id=\"search-input\"\n                                type=\"text\"\n                                ng-model=\"playlistFilter.key\"\n                                placeholder=\"{{_SEARCH_PLAYLIST}}\"\n                              />\n                            </div>\n                            <!-- <div class=\"playlist-search\">\n                              <svg class=\"feather playlist-search-icon\">\n                                <use href=\"#search\"></use>\n                              </svg>\n                              <svg\n                                class=\"feather playlist-clear-icon\"\n                                ng-show=\"playlistFilter.key!=''\"\n                                ng-click=\"clearFilter()\"\n                              >\n                                <use href=\"#x\"></use>\n                              </svg>\n                              <input\n                                class=\"playlist-search-input\"\n                                type=\"text\"\n                                ng-model=\"playlistFilter.key\"\n                                placeholder=\"{{_SEARCH_PLAYLIST}}\"\n                              />\n                            </div> -->\n                            <!-- <li class=\"head\">\n                              <div class=\"title\">\n                                <a>{{_SONGS + '(' + songs.length + ')'}}</a>\n                              </div>\n                              <div class=\"artist\"><a>{{_ARTISTS}}</a></div>\n                              <div class=\"album\"><a>{{_ALBUMS}}</a></div>\n                              <div class=\"tools\">{{_OPERATION}}</div>\n                            </li> -->\n                            <li\n                              class=\"isSearchType\"\n                              ng-class=\"{ 'playing': currentPlaying.id == song.id }\"\n                              ng-repeat=\"song in songs | filter: fieldFilter track by $index\"\n                              ng-mouseenter=\"options=true\"\n                              ng-mouseleave=\"options=false\"\n                              ng-dblclick=\"addAndPlay(song)\"\n                              draggable=\"true\"\n                              drag-drop-zone\n                              drag-zone-object=\"song\"\n                              drag-zone-title=\"song.title\"\n                              sortable=\"is_mine || is_local\"\n                              drag-zone-type=\"'application/listen1-song'\"\n                              drop-zone-ondrop=\"onPlaylistSongDrop(list_id, song, arg1, arg2, arg3)\"\n                            >\n                            <img err-src=\"https://y.gtimg.cn/mediastyle/global/img/playlist_300.png\" add-and-play=\"song\" ng-src=\"{{ song.img_url }}?param=224y224\" alt=\"\">\n                            <div class=\"title-and-artist\">\n                            <div class=\"container\">\n                              <div class=\"title\">\n                                <!-- <a class=\"disabled\" ng-if=\"song.disabled\" ng-click=\"copyrightNotice()\">{{ song.title }}</a> -->\n                                <a add-and-play=\"song\">{{ song.title }}</a>\n                              </div>\n                              <div class=\"artist\">\n                                <a ng-click=\"showPlaylist(song.artist_id)\"\n                                  >{{ song.artist }}</a\n                                >\n                              </div>\n                            </div>\n                          </div>\n                              <div class=\"album\">\n                                <a ng-click=\"showPlaylist(song.album_id)\"\n                                  >{{ song.album }}</a\n                                >\n                              </div>\n                              <div class=\"tools\">\n                                <a\n                                  title=\"{{_ADD_TO_QUEUE}}\"\n                                  class=\"detail-add-button\"\n                                  add-without-play=\"song\"\n                                  ng-show=\"options\"\n                                  >\n                                <svg t=\"1659628019588\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1723\" width=\"512\" height=\"512\"><path d=\"M938.516167 597.182834 85.483833 597.182834C38.527925 597.182834 0 559.256908 0 511.699001c0-46.955908 37.925926-85.483833 85.483833-85.483833l853.032334 0c46.955908 0 85.483833 37.925926 85.483833 85.483833C1024 559.256908 986.074074 597.182834 938.516167 597.182834L938.516167 597.182834 938.516167 597.182834zM512.300999 1024c-46.955908 0-85.483833-37.925926-85.483833-85.483833L426.817166 85.483833C426.817166 37.925926 464.743092 0 512.300999 0c46.955908 0 85.483833 37.925926 85.483833 85.483833l0 853.634333C597.182834 985.472075 559.256908 1024 512.300999 1024L512.300999 1024 512.300999 1024zM512.300999 1024\" fill=\"currentColor\" p-id=\"1724\"></path></svg>\n                              </a>\n                                <a\n                                  title=\"{{_ADD_TO_PLAYLIST}}\"\n                                  class=\"detail-fav-button\"\n                                  ng-show=\"options\"\n                                  ng-click=\"showDialog(0, song)\"\n                                  >\n                                  <svg t=\"1659628805392\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"6942\" width=\"512\" height=\"512\"><path d=\"M768 768v192h-128v-192h-192v-128h192v-192h128v192h192v128h-192z m64-576H192v640h192v128H64V64h896v320h-128V192z\" fill=\"currentColor\" p-id=\"6943\"></path></svg>\n                                  </a>\n                                <a\n                                  title=\"{{_REMOVE_FROM_PLAYLIST}}\"\n                                  class=\"detail-delete-button\"\n                                  ng-click=\"removeSongFromPlaylist(song, list_id)\"\n                                  ng-show=\"options && (is_mine=='1'||is_local) \"\n                                  >\n                                  <svg t=\"1659670048675\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2024\" width=\"512\" height=\"512\"><path d=\"M895.464448 119.006208 677.967872 119.006208c0 0-32.8448 1.020928-58.648576-26.943488-10.395648-12.050432-27.804672-23.795712-56.4224-24.799232l-41.183232 0-6.280192 0-41.182208 0c-28.618752 1.004544-46.031872 12.749824-56.4224 24.799232-25.807872 27.964416-58.6496 26.943488-58.6496 26.943488L141.682688 119.006208c-13.99296 0-25.33888 11.34592-25.33888 25.33888l0 93.090816c-0.053248 26.927104 26.083328 26.396672 26.083328 26.396672l49.83808 0L192.265216 913.65376c0 0-3.966976 44.084224 40.121344 46.45888l269.31712 0 33.738752 0 30.808064 0.238592 38.500352 0 174.934016 0 24.297472 0 0.782336-0.238592c44.080128-2.374656 40.117248-46.45888 40.117248-46.45888L844.88192 263.832576l49.842176 0c0 0 26.133504 0.530432 26.083328-26.396672l0-93.090816C920.8064 130.353152 909.46048 119.006208 895.464448 119.006208zM430.539776 803.171328c0 17.042432-13.828096 30.865408-30.865408 30.865408-17.042432 0-30.865408-13.824-30.865408-30.865408L368.80896 320.736256c0-17.042432 13.824-30.865408 30.865408-30.865408 17.038336 0 30.865408 13.824 30.865408 30.865408L430.539776 803.171328zM663.436288 803.171328c0 17.042432-13.824 30.865408-30.865408 30.865408-17.038336 0-30.865408-13.824-30.865408-30.865408L601.705472 320.736256c0-17.042432 13.828096-30.865408 30.865408-30.865408 17.041408 0 30.865408 13.824 30.865408 30.865408L663.436288 803.171328z\" fill=\"currentColor\" p-id=\"2025\"></path></svg>\n                                  </a>\n                                <a\n                                  title=\"{{_ORIGIN_LINK}}\"\n                                  class=\"source-button\"\n                                  open-url=\"song.source_url\"\n                                  ng-show=\"options && !is_local\"\n                                  >\n                                  <svg t=\"1659628952356\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1783\" width=\"512\" height=\"512\"><path d=\"M475.66342293 318.38570027A54.23369707 54.23369707 0 0 1 399.7362464 241.91618667L512 128.56775893a271.16848747 271.16848747 0 0 1 383.43224107 383.43224107L779.372128 625.8907648a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l113.89076373-113.8907648a162.70109227 162.70109227 0 0 0-229.9508768-229.9508768z m74.8425024 385.60158826A54.23369707 54.23369707 0 1 1 626.97543893 779.372128l-114.43310186 114.43310187A271.16848747 271.16848747 0 0 1 128.56775893 512L243.0008608 399.7362464a54.23369707 54.23369707 0 1 1 77.01185067 74.30016533l-114.97543894 114.43310187a162.70109227 162.70109227 0 0 0 229.9508768 229.9508768z m-135.0419072-17.89712a54.23369707 54.23369707 0 1 1-76.46951253-76.4695136l268.45680213-268.45680213a54.23369707 54.23369707 0 0 1 76.4695136 76.4695136z\" fill=\"currentColor\" p-id=\"1784\"></path></svg>\n                                </a>\n                              </div>\n                              <div class=\"time\">{{song.duration}}</div>\n                            </li>\n                          </ul>\n                        </div>\n                        <!-- now playing window-->\n                      \n                      </div>\n                    </div>\n                  </div>\n                </div>\n                <div class=\"footer\" ng-class=\"{footerdef:playlist.length == 0}\">\n                  \n                  <div class=\"footer-main\" ng-style=\"{background}\"  ng-class=\"{slidedown: window_type=='track'}\">\n                    <div class=\"bgwrapper\" ng-class=\"{slidedown: window_type=='track'}\" style=\"overflow:hidden ;\">\n                      <div\n                      ng-if=\"enableNowplayingCoverBackground\"\n                      class=\"bg\"\n                      ng-style=\"{'background-image': 'url(' + currentPlaying.img_url + ')'}\"\n                    ></div>\n                    </div>\n                    <div\n                  ng-class=\"{slidedown: window_type!='track'}\"\n                  class=\"songdetail-wrapper\"\n                >\n                  <div class=\"draggable-zone\"></div>\n                  \n                  <div\n                    class=\"close\"\n                    ng-class=\"isMac? 'mac':'' \"\n                    ng-click=\"popWindow()\"\n                  >\n                  <svg style=\"width: 19px;height:19px;\" enable-background=\"new 0 0 32 32\" height=\"32px\" version=\"1.1\" viewBox=\"0 0 32 32\" width=\"32px\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><path d=\"M14.77,23.795L5.185,14.21c-0.879-0.879-0.879-2.317,0-3.195l0.8-0.801c0.877-0.878,2.316-0.878,3.194,0  l7.315,7.315l7.316-7.315c0.878-0.878,2.317-0.878,3.194,0l0.8,0.801c0.879,0.878,0.879,2.316,0,3.195l-9.587,9.585  c-0.471,0.472-1.104,0.682-1.723,0.647C15.875,24.477,15.243,24.267,14.77,23.795z\" fill=\"currentColor\"/></svg>\n                  </div>\n\n                  <div ng-if=\"!isChrome && !isMac\" class=\"window-control\">\n                    <svg class=\"icon\" window-control=\"window_min\">\n                      <use href=\"#minimize-2\"></use>\n                    </svg>\n                    <svg class=\"icon\" window-control=\"window_max\">\n                      <use href=\"#maximize\"></use>\n                    </svg>\n                    <svg class=\"icon\" window-control=\"window_close\">\n                      <use href=\"#x\"></use>\n                    </svg>\n                  </div>\n\n                  <div class=\"playsong-detail\">\n                    <div class=\"detail-head\">\n                      <div>\n                      <div class=\"detail-head-cover\">\n                        <div ng-if=\"window_type=='track'\"  class=\"covershadow\" style=\"background-image:url({{currentPlaying.img_url}}?param=224y224);opacity: 1;z-index: -1;\"></div>\n                        <img\n                      \n                        id=\"trackbackground\" \n                          ng-src=\"{{ currentPlaying.img_url.replace('224y224','1024y1024').replace('/120/','/1024/').replace('/T002R300x300M','/T002R800x800M') }}?param=1024y1024\"\n                          err-src=\"https://y.gtimg.cn/mediastyle/global/img/album_300.png\"\n                        />\n                      </div>\n                      <div class=\"detail-head-title\">\n                        <div class=\"title\">\n                          <h2>{{ currentPlaying.title }}</h2>\n                          <span\n                            class=\"badge\"\n                            ng-if=\"enableNowplayingBitrate && currentPlaying.bitrate !== undefined\"\n                            >{{ currentPlaying.bitrate }}</span\n                          >\n                          <span\n                            class=\"badge platform\"\n                            ng-if=\"enableNowplayingPlatform && currentPlaying.platform !== undefined\"\n                            >{{ currentPlaying.platformText }}</span\n                          >\n                        </div>\n                        <div class=\"info\">\n                          <div class=\"singer\">\n                            <a\n                              ng-click=\"showPlaylist(currentPlaying.artist_id)\"\n                              title=\"{{currentPlaying.artist}}\"\n                              >{{ currentPlaying.artist }}</a\n                            >\n                          </div>\n                          <span>-</span>\n                          <div class=\"album\">\n                            <a\n                              ng-click=\"showPlaylist(currentPlaying.album_id)\"\n                              title=\"{{currentPlaying.album}}\"\n                              >{{ currentPlaying.album }}</a\n                            >\n                          </div>\n                        </div>\n                      </div>\n                        <!-- <a title=\"加入收藏\" class=\"clone\" ng-click=\"showDialog(0, currentPlaying)\">收藏</a>\n          <a open-url=\"currentPlaying.source_url\" title=\"原始链接\" class=\"link\">原始链接</a> -->\n                      </div>\n                    </div>\n                    <div class=\"detail-songinfo\">\n                      \n                      <div class=\"lyric\">\n                        <div class=\"placeholder\"></div>\n                        <p\n                          ng-repeat=\"line in lyricArray track by $index\"\n                          data-line=\"{{line.lineNumber}}\"\n                          ng-class=\"{ 'highlight': (line.lineNumber == lyricLineNumber) || (line.lineNumber == lyricLineNumberTrans) , hide: (line.translationFlag && !enableLyricTranslation), translate: line.translationFlag}\"\n                        >\n                          {{ line.content }}\n                        </p>\n                        <div class=\"placeholder\"></div>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n                    <div class=\"footerwrap\">\n                    <div class=\"left-control\" ng-class=\"{slidedown: window_type=='track'}\">\n                  \n                    <div class=\"playlist-toggle\">\n                      <span\n                      ng-class=\"{playlistactive: !menuHidden}\"\n                        ng-click=\"togglePlaylist()\"\n                        class=\"icon\"\n                      >\n                      <svg style=\"width: 19px;height: 19px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"list-music\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" fill=\"currentColor\"><path fill=\"currentColor\" d=\"M16 256h256a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm0-128h256a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16H16A16 16 0 0 0 0 80v32a16 16 0 0 0 16 16zm128 192H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM470.94 1.33l-96.53 28.51A32 32 0 0 0 352 60.34V360a148.76 148.76 0 0 0-48-8c-61.86 0-112 35.82-112 80s50.14 80 112 80 112-35.82 112-80V148.15l73-21.39a32 32 0 0 0 23-30.71V32a32 32 0 0 0-41.06-30.67z\" class=\"\"></path></svg>\n                    </span>\n                    </div>\n\n                    <div class=\"splitter\"></div>\n                    \n                    <div class=\"detail\" ng-if=\"playlist.length > 0\">\n\n                      <div class=\"title\">\n                        <span\n                          ng-if=\"currentPlaying.source === 'xiami'\"\n                          style=\"color: orange; font-size: medium\"\n                          >⚠️ </span\n                        >{{ currentPlaying.title }}\n                      </div>\n                      <div class=\"more-info\">\n                        <div class=\"singer\">\n                          <a ng-click=\"showPlaylist(currentPlaying.artist_id)\"\n                            >{{ currentPlaying.artist }}</a\n                          >\n                          -\n                          <a ng-click=\"showPlaylist(currentPlaying.album_id)\"\n                            >{{ currentPlaying.album }}</a\n                          >\n                        </div>\n                      </div>\n                    \n                    </div>\n                  </div>\n                  <div class=\"main-info\">   \n                    <div\n                      class=\"cover\"\n                      class=\"cover\"\n                      ng-if=\"playlist.length > 0\"\n                    >\n                    <!-- <div class=\"logo-banner\" ng-if=\"playlist.length == 0\">\n                      <svg\n                        class=\"logo\"\n                        xmlns=\"http://www.w3.org/2000/svg\"\n                        width=\"24\"\n                        height=\"24\"\n                        viewBox=\"0 0 24 24\"\n                        fill=\"#666666\"\n                        stroke=\"#666666\"\n                        stroke-width=\"1\"\n                        stroke-linecap=\"round\"\n                        stroke-linejoin=\"round\"\n                      >\n                        <polygon\n                          points=\"7 4 7 19 16 19 16 16 10 16 10 4\"\n                        ></polygon>\n                        <polygon points=\"13 4 13 13 16 13 16 4\"></polygon>\n                      </svg>\n                    </div> -->\n                  \n                    <ul id=\"cover-list\" class=\"cover-list\">\n                      <li ng-class=\"{\n                        lipause:!isPlaying,\n                        liplay:isPlaying,\n                        rotatecircl:song.stageId === getSongIdByIndex(currentIndex),\n                        a:song.stageId === getSongIdByIndex(currentIndex-1),\n                        b:song.stageId === getSongIdByIndex(currentIndex),\n                        c:song.stageId === getSongIdByIndex(currentIndex+1),\n                        def:song.stageId === getSongIdByIndex(currentIndex+2)||song.stageId === getSongIdByIndex(currentIndex-2)}\" class=\"hid\"\n                            id=\"song{{ song.id }}\" ng-repeat=\"song in staged_playlist track by song.stageId\" draggable=\"true\">\n                    <img\n                    err-src=\"https://y.gtimg.cn/mediastyle/global/img/album_300.png\"\n                    ng-src=\"{{song.img_url.replace('/120/','/300/')}}?param=300y300\" alt=\"\">\n                    </li>\n                    </ul>\n                    <div class=\"cover-list\">\n                      <span class=\"a\" ng-class=\"{show: settings.playmode===1}\" prev-track=\"\">\n                        <svg style=\"width:20px ;height: 20px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"step-backward\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M64 468V44c0-6.6 5.4-12 12-12h48c6.6 0 12 5.4 12 12v176.4l195.5-181C352.1 22.3 384 36.6 384 64v384c0 27.4-31.9 41.7-52.5 24.6L136 292.7V468c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12z\"></path></svg>\n                      </span>\n                    <span\n                      class=\"b\"\n                      play-pause-toggle=\"\"\n                    >\n                    <svg style=\"width:30px ;height: 30px;\" ng-show=\"isPlaying\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"pause\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z\"></path></svg>\n                    <svg style=\"width:30px ;height: 30px;\" ng-show=\"!isPlaying\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                  </span>\n                    <span class=\"c\" ng-class=\"{show: settings.playmode===1}\" next-track=\"\">\n                      <svg style=\"width:20px ;height: 20px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"step-forward\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M384 44v424c0 6.6-5.4 12-12 12h-48c-6.6 0-12-5.4-12-12V291.6l-195.5 181C95.9 489.7 64 475.4 64 448V64c0-27.4 31.9-41.7 52.5-24.6L312 219.3V44c0-6.6 5.4-12 12-12h48c6.6 0 12 5.4 12 12z\"></path></svg>\n                    </span>\n                    </div>\n                    \n                      <!-- <img\n                      play-pause-toggle=\"\"\n                        ng-src=\"{{ currentPlaying.img_url.replace('/120/','/500/').replace('/T002R300x300M','/T002R500x500M')  }}?param=512y512\"\n                        err-src=\"https://y.gtimg.cn/mediastyle/global/img/album_300.png\"\n                      /> -->\n                      <div id=\"rotatemark\"\n                      \n                      class=\"circlemark\">\n                        <div id=\"circlmark\" ng-style=\"{transform :'rotate(-'+ myProgress/100*180  + 'deg)' }\" class=\"circle\">\n                          <div class=\"topmark\">\n                            <div class=\"top\"></div>\n                          </div>\n                          <div class=\"bottom\">\n                            <div class=\"bottomcircle\"></div>\n                          </div>\n                        </div>\n                      </div>    \n                    </div>\n                  \n                    <div ng-show=\"playlist.length != 0\" class=\"footertime\">\n                      <div class=\"bottomprogressbar\">    \n                        <div class=\"playbar\">\n                          <span class=\"icon\">\n                            <svg style=\"width:18px ;height:18px;\" t=\"1659244374154\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1286\" width=\"512\" height=\"512\"><path d=\"M776.877181 172.523285l-3.41170299-3.38305C706.25063399 102.782921 613.924879 61.763692 511.992325 61.763692c-101.81385001 0-194.07923001 40.930201-261.276678 107.181091l-3.790326 3.773953c-66.236564 67.228147-107.181091 159.44850199-107.18109101 261.292028 0 101.93255399 41.004903 194.243983 107.37654301 261.473153l3.3984 3.384074 261.486456 261.516132 261.66860499-261.651208 3.02182301-3.052522c66.477041-67.257823 107.556645-159.628604 107.55664499-261.667581C884.253724 332.092537 843.249845 239.782132 776.877181 172.523285zM512.006651 539.79402c-71.934333 0-130.247436-58.32742999-130.247436-130.247436 0-29.728068 9.924024-57.078996 26.64587-78.986959 23.80210201-31.17092999 61.349253-51.290153 103.601566-51.290153 71.935356 0 130.247436 58.34278 130.247436 130.278135C642.254088 481.46659001 583.942007 539.79402 512.006651 539.79402z\" fill=\"currentColor\" p-id=\"1287\"></path></svg>   \n                          </span>\n                          <div \n                            class=\"playbar-clickable\"\n                            id=\"progressbar\"\n                            mode=\"play\"\n                            draggable-bar=\"\"\n                          >\n                            <div class=\"barbg\">\n                              <div\n                                class=\"cur\"\n                                ng-style=\"{width :( myProgress)+ '%' }\"\n                              >\n                                <span class=\"btn\"><i></i></span>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                        <div class=\"volume-ctrl\" volume-wheel=\"\">\n                          <span\n                            class=\"icon\"\n                            ng-click=\"toggleMuteStatus()\"\n                          >\n                            <svg ng-show=\"mute\" style=\"width:18px ;height:18px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"volume-mute\"  fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM461.64 256l45.64-45.64c6.3-6.3 6.3-16.52 0-22.82l-22.82-22.82c-6.3-6.3-16.52-6.3-22.82 0L416 210.36l-45.64-45.64c-6.3-6.3-16.52-6.3-22.82 0l-22.82 22.82c-6.3 6.3-6.3 16.52 0 22.82L370.36 256l-45.63 45.63c-6.3 6.3-6.3 16.52 0 22.82l22.82 22.82c6.3 6.3 16.52 6.3 22.82 0L416 301.64l45.64 45.64c6.3 6.3 16.52 6.3 22.82 0l22.82-22.82c6.3-6.3 6.3-16.52 0-22.82L461.64 256z\"></path></svg>\n                            <svg ng-show=\"!mute\" style=\"width:18px ;height:18px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"volume\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 480 512\" ><path fill=\"currentColor\" d=\"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.53 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z\" class=\"\"></path></svg>\n                          </span>\n                          <div \n                            class=\"m-pbar volume\"\n                            id=\"volumebar\"\n                            mode=\"volume\"\n                            draggable-bar=\"\">\n                            <div class=\"barbg\">\n                              <div class=\"cur\" ng-style=\"{width : volume + '%' }\">\n                                <span class=\"btn\"><i></i></span>\n                              </div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                      <div class=\"timeswitch\">\n                      <span class=\"current\">{{ currentPosition }}</span>\n                      <span style=\"font-weight: 700;\"> / </span>\n                      <span class=\"total\">{{ currentDuration }}</span>\n                    </div>\n                    </div>  \n                  </div>\n                  <div class=\"right-control\">\n                    \n                    <div class=\"ctrl\">\n                      <a\n                        ng-click=\"showDialog(0, currentPlaying)\"\n                        title=\"{{_ADD_TO_PLAYLIST}}\"\n                      >\n                        <span class=\"icon\">\n                          <svg style=\"width: 18px;height: 18px;\"  fill=\"currentColor\"  t=\"1659233308084\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1133\" width=\"512\" height=\"512\"><path d=\"M938.516167 597.182834 85.483833 597.182834C38.527925 597.182834 0 559.256908 0 511.699001c0-46.955908 37.925926-85.483833 85.483833-85.483833l853.032334 0c46.955908 0 85.483833 37.925926 85.483833 85.483833C1024 559.256908 986.074074 597.182834 938.516167 597.182834L938.516167 597.182834 938.516167 597.182834zM512.300999 1024c-46.955908 0-85.483833-37.925926-85.483833-85.483833L426.817166 85.483833C426.817166 37.925926 464.743092 0 512.300999 0c46.955908 0 85.483833 37.925926 85.483833 85.483833l0 853.634333C597.182834 985.472075 559.256908 1024 512.300999 1024L512.300999 1024 512.300999 1024zM512.300999 1024\"  fill=\"currentColor\" p-id=\"1134\"></path></svg>\n                        </span>\n                      </a>\n                      <a\n                        title=\"{{ settings.playmode | playmode_title }}(s)\"\n                        ng-click=\"changePlaymode()\"\n                      >\n                        <span\n                          ng-show=\"settings.playmode == 0\"\n                          class=\"icon\"\n                        >\n                        <span  class=\"dn color-inherit link hover-pink\">\n                          <svg style=\"width: 18px;height: 18px;\"  aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"repeat\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"  fill=\"currentColor\"><path fill=\"currentColor\" d=\"M512 256c0 88.224-71.775 160-160 160H170.067l34.512 32.419c9.875 9.276 10.119 24.883.539 34.464l-10.775 10.775c-9.373 9.372-24.568 9.372-33.941 0l-92.686-92.686c-9.373-9.373-9.373-24.568 0-33.941l92.686-92.686c9.373-9.373 24.568-9.373 33.941 0l10.775 10.775c9.581 9.581 9.337 25.187-.539 34.464L170.067 352H352c52.935 0 96-43.065 96-96 0-13.958-2.996-27.228-8.376-39.204-4.061-9.039-2.284-19.626 4.723-26.633l12.183-12.183c11.499-11.499 30.965-8.526 38.312 5.982C505.814 205.624 512 230.103 512 256zM72.376 295.204C66.996 283.228 64 269.958 64 256c0-52.935 43.065-96 96-96h181.933l-34.512 32.419c-9.875 9.276-10.119 24.883-.539 34.464l10.775 10.775c9.373 9.372 24.568 9.372 33.941 0l92.686-92.686c9.373-9.373 9.373-24.568 0-33.941l-92.686-92.686c-9.373-9.373-24.568-9.373-33.941 0L306.882 29.12c-9.581 9.581-9.337 25.187.539 34.464L341.933 96H160C71.775 96 0 167.776 0 256c0 25.897 6.186 50.376 17.157 72.039 7.347 14.508 26.813 17.481 38.312 5.982l12.183-12.183c7.008-7.008 8.786-17.595 4.724-26.634z\" class=\"\"></path></svg></span>\n                      </span>\n                        <span\n                          ng-show=\"settings.playmode == 1\"\n                          class=\"icon\"\n                        >\n                        <svg style=\"width: 18px;height: 18px;\"  aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"random\" fill=\"currentColor\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M504.971 359.029c9.373 9.373 9.373 24.569 0 33.941l-80 79.984c-15.01 15.01-40.971 4.49-40.971-16.971V416h-58.785a12.004 12.004 0 0 1-8.773-3.812l-70.556-75.596 53.333-57.143L352 336h32v-39.981c0-21.438 25.943-31.998 40.971-16.971l80 79.981zM12 176h84l52.781 56.551 53.333-57.143-70.556-75.596A11.999 11.999 0 0 0 122.785 96H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12zm372 0v39.984c0 21.46 25.961 31.98 40.971 16.971l80-79.984c9.373-9.373 9.373-24.569 0-33.941l-80-79.981C409.943 24.021 384 34.582 384 56.019V96h-58.785a12.004 12.004 0 0 0-8.773 3.812L96 336H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h110.785c3.326 0 6.503-1.381 8.773-3.812L352 176h32z\"></path></svg>\n                      </span>\n                        <span\n                          ng-show=\"settings.playmode == 2\"\n                          class=\"icon\"\n                        >\n                        <span  class=\"dn color-inherit link hover-indigo\"><svg style=\"width: 18px;height: 18px;\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"repeat-1\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"  fill=\"currentColor\"><path fill=\"currentColor\" d=\"M512 256c0 88.224-71.775 160-160 160H170.067l34.512 32.419c9.875 9.276 10.119 24.883.539 34.464l-10.775 10.775c-9.373 9.372-24.568 9.372-33.941 0l-92.686-92.686c-9.373-9.373-9.373-24.568 0-33.941l80.269-80.27c9.373-9.373 24.568-9.373 33.941 0l10.775 10.775c9.581 9.581 9.337 25.187-.539 34.464l-22.095 20H352c52.935 0 96-43.065 96-96 0-13.958-2.996-27.228-8.376-39.204-4.061-9.039-2.284-19.626 4.723-26.633l12.183-12.183c11.499-11.499 30.965-8.526 38.312 5.982C505.814 205.624 512 230.103 512 256zM72.376 295.204C66.996 283.228 64 269.958 64 256c0-52.935 43.065-96 96-96h181.933l-22.095 20.002c-9.875 9.276-10.119 24.883-.539 34.464l10.775 10.775c9.373 9.372 24.568 9.372 33.941 0l80.269-80.27c9.373-9.373 9.373-24.568 0-33.941l-92.686-92.686c-9.373-9.373-24.568-9.373-33.941 0l-10.775 10.775c-9.581 9.581-9.337 25.187.539 34.464L341.933 96H160C71.775 96 0 167.776 0 256c0 25.897 6.186 50.376 17.157 72.039 7.347 14.508 26.813 17.481 38.312 5.982l12.183-12.183c7.008-7.008 8.786-17.595 4.724-26.634zm154.887 4.323c0-7.477 3.917-11.572 11.573-11.572h15.131v-39.878c0-5.163.534-10.503.534-10.503h-.356s-1.779 2.67-2.848 3.738c-4.451 4.273-10.504 4.451-15.666-1.068l-5.518-6.231c-5.342-5.341-4.984-11.216.534-16.379l21.72-19.939c4.449-4.095 8.366-5.697 14.42-5.697h12.105c7.656 0 11.749 3.916 11.749 11.572v84.384h15.488c7.655 0 11.572 4.094 11.572 11.572v8.901c0 7.477-3.917 11.572-11.572 11.572h-67.293c-7.656 0-11.573-4.095-11.573-11.572v-8.9z\" class=\"\"></path></svg></span>\n                      </span>\n                      </a>\n                    </div>\n                    <div ng-if=\"!isChrome\" class=\"lyric-toggle\">\n                    \n                      <div\n                        ng-click=\"openLyricFloatingWindow(true)\"\n                        class=\"lyric-icon\"\n                        ng-show=\"!enableLyricFloatingWindow\"\n                      >\n                        <svg style=\"width: 18px;height: 18px;\" t=\"1659234265155\"  fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1436\" width=\"512\" height=\"512\"><path d=\"M842.32474169 18.93626311H181.79953398c-88.67069345 0-160.81272225 72.1420288-160.81272225 160.81272226v660.58734553c0 88.67069345 72.1420288 160.81272225 160.81272225 160.81272225h660.58734554c88.67069345 0 160.81272225-72.1420288 160.81272226-160.81272225V179.74898537c-0.06213783-88.67069345-72.20416663-160.81272225-160.87486009-160.81272226z m86.24731781 821.40006779c0 47.53544533-38.71187248 86.24731781-86.24731781 86.24731781H181.79953398c-47.53544533 0-86.24731781-38.71187248-86.2473178-86.24731781V179.74898537c0-47.53544533 38.71187248-86.24731781 86.2473178-86.24731782h660.58734554c47.53544533 0 86.24731781 38.71187248 86.24731781 86.24731782v660.58734553z\"  fill=\"currentColor\" p-id=\"1437\"></path><path d=\"M714.25865955 193.66786085H441.5356928c-20.56762405 0-37.28270222 16.71507817-37.28270222 37.28270223s16.71507817 37.28270222 37.28270222 37.28270222h272.66082892c10.99839715 0 20.25693488 12.05474038 20.25693487 26.28430507v462.30550755c0 7.64295395-6.2137837 13.85673765-13.85673766 13.85673766h-29.14264556c-20.56762405 0-37.28270222 16.71507817-37.28270222 37.28270223s16.71507817 37.28270222 37.28270222 37.28270222h29.14264556c48.71606423 0 88.4221421-39.64394003 88.4221421-88.42214211V294.51757037c0.06213783-55.67550198-42.50228053-100.84970951-94.76020148-100.84970952z\" fill=\"currentColor\" p-id=\"1438\"></path><path d=\"M465.08593303 425.75268219h170.56836267c20.56762405 0 37.28270222-16.71507817 37.28270222-37.28270222s-16.71507817-37.28270222-37.28270222-37.28270222H465.08593303c-20.56762405 0-37.28270222 16.71507817-37.28270221 37.28270222s16.71507817 37.28270222 37.28270221 37.28270222zM672.5641709 693.81531117V504.41918388c0-20.56762405-16.71507817-37.28270222-37.28270222-37.28270223H472.29392213c-20.56762405 0-37.28270222 16.71507817-37.28270222 37.28270223v189.39612729c0 20.56762405 16.71507817 37.28270222 37.28270222 37.28270222h162.98754655c20.6297619 0 37.28270222-16.71507817 37.28270222-37.28270222z m-74.56540445-37.28270222H509.57662435v-114.83072285h88.4221421v114.83072285zM243.564544 304.27321078a37.15842655 37.15842655 0 0 0 27.09209695 11.68191337c9.19639988 0 18.39279977-3.3554432 25.60078886-10.19060527a37.23920574 37.23920574 0 0 0 1.49130809-52.69288581l-38.89828599-41.19738596a37.23920574 37.23920574 0 0 0-52.69288581-1.49130809 37.23920574 37.23920574 0 0 0-1.49130808 52.69288581l38.89828598 41.19738595zM345.84342377 692.94538145l-27.09209695 14.72666738 42.31586701-311.43483923c1.42917025-10.68770797-1.80199728-21.43755378-8.88571068-29.51547259s-17.33645653-12.73825659-28.08630235-12.73825659h-101.90605275c-20.56762405 0-37.28270222 16.71507817-37.28270222 37.28270221s16.71507817 37.28270222 37.28270222 37.28270222h59.27949654l-46.4791021 341.94451723c-1.86413511 13.9188755 4.16323508 27.71347532 15.72087276 35.72925629 6.33805938 4.41178643 13.73246198 6.58661072 21.18900244 6.58661073 6.08950803 0 12.17901605-1.49130809 17.77142139-4.47392427l91.71544747-49.71026963c18.08211058-9.81777825 24.85513482-32.43595093 15.03735656-50.51806151s-32.43595093-24.91727265-50.58019934-15.16163224z\"  fill=\"currentColor\" p-id=\"1439\"></path></svg>\n                      </div>\n                      <div\n                        ng-click=\"openLyricFloatingWindow(true)\"\n                        class=\"lyric-icon\"\n                        ng-show=\"enableLyricFloatingWindow\"\n                      >\n                      <svg style=\"width: 18px;height: 18px;\" t=\"1659234282623\"  fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1592\" width=\"512\" height=\"512\"><path d=\"M515.66613238 538.3464429h88.42214211v114.83072285h-88.42214211z\" fill=\"currentColor\" p-id=\"1593\"></path><path d=\"M839.59067685 18.81198743H179.00333132c-88.67069345 0-160.81272225 72.1420288-160.81272225 160.81272226v660.58734554c0 88.67069345 72.1420288 160.81272225 160.81272225 160.81272225h660.58734553c88.67069345 0 160.81272225-72.1420288 160.81272226-160.81272225V179.56257185c-0.06213783-88.67069345-72.1420288-160.75058442-160.81272226-160.75058442zM212.24707413 207.08963365a37.23299195 37.23299195 0 0 1 52.69288582 1.4913081l38.89828597 41.19738595a37.23299195 37.23299195 0 0 1-1.49130809 52.69288581 37.03415088 37.03415088 0 0 1-25.60078885 10.19060527 37.15842655 37.15842655 0 0 1-27.09209695-11.68191336l-38.89828598-41.19738595c-14.10528901-14.97521872-13.48391063-38.5875968 1.49130808-52.69288582z m175.22870045 548.11786052l-91.71544747 49.71026962a37.42561925 37.42561925 0 0 1-38.96042382-2.11268646 37.26406087 37.26406087 0 0 1-15.72087277-35.7292563l46.4791021-341.94451721h-59.27949653c-20.56762405 0-37.28270222-16.71507817-37.28270222-37.28270222s16.71507817-37.28270222 37.28270222-37.28270222h101.96819058c10.74984581 0 21.00258892 4.66033778 28.08630234 12.73825659s10.31488095 18.88990245 8.88571069 29.5154726l-42.31586702 311.43483922 27.09209695-14.72666737c18.14424842-9.81777825 40.7624211-3.10689185 50.51806152 15.03735656 9.75564042 18.20638625 3.04475402 40.82455893-15.03735657 50.64233719z m90.90765559-27.46492398c-20.56762405 0-37.28270222-16.71507817-37.28270222-37.28270222V501.06374068c0-20.56762405 16.71507817-37.28270222 37.28270222-37.28270223h162.98754654c20.56762405 0 37.28270222 16.71507817 37.28270222 37.28270223v189.39612729c0 20.56762405-16.71507817 37.28270222-37.28270222 37.28270222H478.38343017z m-44.49069132-342.62803342c0-20.56762405 16.71507817-37.28270222 37.28270222-37.28270222h170.56836266c20.56762405 0 37.28270222 16.71507817 37.28270222 37.28270222s-16.71507817 37.28270222-37.28270222 37.28270222H471.17544107c-20.56762405 0-37.28270222-16.65294032-37.28270222-37.28270222z m381.27776805 368.35309795c0 48.71606423-39.64394003 88.4221421-88.4221421 88.42214211h-29.14264557c-20.56762405 0-37.28270222-16.71507817-37.28270222-37.28270222s16.71507817-37.28270222 37.28270222-37.28270223h29.14264557c7.64295395 0 13.85673765-6.2137837 13.85673765-13.85673766v-462.30550755c0-14.22956468-9.32067555-26.28430507-20.25693487-26.28430507H447.62520083c-20.56762405 0-37.28270222-16.71507817-37.28270222-37.28270222s16.71507817-37.28270222 37.28270222-37.28270223h272.66082892c52.32005878 0 94.82233932 45.23634537 94.82233932 100.84970952v462.30550755z\"  fill=\"currentColor\"p-id=\"1594\"></path></svg>\n                      </div>\n                    </div>\n                  <div\n                    class=\"translate-switch\"\n                    ng-click=\"toggleLyricTranslation()\"\n                    ng-class=\"{selected: enableLyricTranslation,slidedown: window_type=='track'}\"\n                  >\n                  <svg style=\"width:20px ;height:20px;\" t=\"1659418908628\"  viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1413\" width=\"512\" height=\"512\"><path d=\"M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m0-960C265.6 64 64 265.6 64 512s201.6 448 448 448 448-201.6 448-448S758.4 64 512 64z m268.8 422.4c-60.8-6.4-118.4-22.4-169.6-51.2-54.4 25.6-115.2 44.8-172.8 57.6l-28.8-51.2c51.2-9.6 102.4-25.6 150.4-44.8-32-28.8-57.6-64-70.4-105.6H448V240h304v51.2c-19.2 44.8-48 83.2-86.4 112 44.8 16 89.6 28.8 134.4 32l-19.2 51.2z m-92.8-192H544c16 32 38.4 57.6 67.2 76.8 28.8-19.2 57.6-44.8 76.8-76.8zM265.6 265.6L307.2 224c38.4 28.8 70.4 57.6 102.4 92.8l-41.6 41.6c-28.8-35.2-64-67.2-102.4-92.8z m99.2 396.8c16-16 32-35.2 51.2-54.4l16 64c-35.2 38.4-73.6 76.8-115.2 108.8l-22.4-54.4c9.6-9.6 16-22.4 16-35.2v-227.2H224v-57.6h140.8v256z m214.4-83.2h-112v-54.4h112v-51.2h57.6v51.2h121.6v54.4h-121.6V640h150.4v57.6h-150.4V800h-57.6v-102.4h-137.6V640h137.6v-60.8z\" fill=\"currentColor\" p-id=\"1414\"></path></svg>\n                  </div>\n                    <div ng-click=\"toggleNowPlaying()\" class=\"mask\" ng-class=\"{slidedown: window_type=='track'}\">\n                      <?xml version=\"1.0\" ?><!DOCTYPE svg  PUBLIC '-//W3C//DTD SVG 1.1//EN'  'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg style=\"width: 18px;height: 18px;\" fill=\"currentColor\" enable-background=\"new 0 0 32 32\" height=\"32px\" id=\"Layer_1\" version=\"1.1\" viewBox=\"0 0 32 32\" width=\"32px\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><path d=\"M18.221,7.206l9.585,9.585c0.879,0.879,0.879,2.317,0,3.195l-0.8,0.801c-0.877,0.878-2.316,0.878-3.194,0  l-7.315-7.315l-7.315,7.315c-0.878,0.878-2.317,0.878-3.194,0l-0.8-0.801c-0.879-0.878-0.879-2.316,0-3.195l9.587-9.585  c0.471-0.472,1.103-0.682,1.723-0.647C17.115,6.524,17.748,6.734,18.221,7.206z\" fill=\"currentColor\"/></svg>\n                    </div>\n                  </div>\n                </div>\n                </div>\n                  <div\n                    class=\"menu-modal\"\n                    ng-class=\"{slideup: !menuHidden}\"\n                    ng-click=\"togglePlaylist()\"\n                  ></div>\n                  <div class=\"menu\" ng-class=\"{slideup: !menuHidden&&playlist!=0}\">\n                    <div class=\"menu-header\">\n                      <span class=\"menu-title\"\n                        >{{_TOTAL_SONG_PREFIX}}{{playlist.length}}{{_TOTAL_SONG_POSTFIX}}</span\n                      >\n                      <a class=\"add-all\" ng-click=\"showDialog(0, playlist)\">\n                        <svg class=\"icon\"\n                        ng-click=\"togglePlaylist()\" t=\"1659349903992\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"5198\" width=\"512\" height=\"512\"><path d=\"M768 768v192h-128v-192h-192v-128h192v-192h128v192h192v128h-192z m64-576H192v640h192v128H64V64h896v320h-128V192z\" fill=\"currentColor\" p-id=\"5199\"></path></svg>\n                        <span>{{_ADD_TO_PLAYLIST}}</span></a\n                      >\n                      <a class=\"remove-all\" ng-click=\"togglePlaylist()\" clear-playlist=\"\"\n                        >\n                        <svg clear-playlist=\"\"  fill=\"currentColor\"\n                        class=\"icon\" t=\"1659349733800\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1316\" width=\"512\" height=\"512\"><path d=\"M895.464448 119.006208 677.967872 119.006208c0 0-32.8448 1.020928-58.648576-26.943488-10.395648-12.050432-27.804672-23.795712-56.4224-24.799232l-41.183232 0-6.280192 0-41.182208 0c-28.618752 1.004544-46.031872 12.749824-56.4224 24.799232-25.807872 27.964416-58.6496 26.943488-58.6496 26.943488L141.682688 119.006208c-13.99296 0-25.33888 11.34592-25.33888 25.33888l0 93.090816c-0.053248 26.927104 26.083328 26.396672 26.083328 26.396672l49.83808 0L192.265216 913.65376c0 0-3.966976 44.084224 40.121344 46.45888l269.31712 0 33.738752 0 30.808064 0.238592 38.500352 0 174.934016 0 24.297472 0 0.782336-0.238592c44.080128-2.374656 40.117248-46.45888 40.117248-46.45888L844.88192 263.832576l49.842176 0c0 0 26.133504 0.530432 26.083328-26.396672l0-93.090816C920.8064 130.353152 909.46048 119.006208 895.464448 119.006208zM430.539776 803.171328c0 17.042432-13.828096 30.865408-30.865408 30.865408-17.042432 0-30.865408-13.824-30.865408-30.865408L368.80896 320.736256c0-17.042432 13.824-30.865408 30.865408-30.865408 17.038336 0 30.865408 13.824 30.865408 30.865408L430.539776 803.171328zM663.436288 803.171328c0 17.042432-13.824 30.865408-30.865408 30.865408-17.038336 0-30.865408-13.824-30.865408-30.865408L601.705472 320.736256c0-17.042432 13.828096-30.865408 30.865408-30.865408 17.041408 0 30.865408 13.824 30.865408 30.865408L663.436288 803.171328z\" fill=\"currentColor\" p-id=\"1317\"></path></svg>\n                        <span>{{_CLEAR_ALL}}</span></a\n                      >\n\n                      <a class=\"close\" ng-click=\"togglePlaylist()\"\n                        >\n                        <svg  fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n                      </a>\n                    </div>\n                    <ul class=\"menu-list\">\n                      <li\n                        id=\"song{{ song.id }}\"\n                        ng-repeat=\"song in playlist track by $index\"\n                        ng-class=\"{ playing: currentPlaying.id == song.id }\"\n                        ng-mouseenter=\"playlist_highlight=true\"\n                        ng-mouseleave=\"playlist_highlight=false\"\n                        ng-dblclick=\"playById(song.id)\"\n                        draggable=\"true\"\n                        drag-drop-zone\n                        drag-zone-object=\"song\"\n                        drag-zone-title=\"song.title\"\n                        sortable=\"true\"\n                        drag-zone-type=\"'application/listen1-song'\"\n                        drop-zone-ondrop=\"onCurrentPlayingSongDrop(song, arg1, arg2, arg3)\"\n                      >\n                        <div class=\"song-status-icon\">\n\n                          <svg class=\"feather play\"\n                          ng-show=\"currentPlaying.id == song.id\" aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"play\" class=\"svg-inline--fa fa-play fa-w-14\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z\"></path></svg>\n                        </div>\n                        <div\n                          class=\"song-title\"\n                          ng-class=\"song.disabled? 'disabled':'' \"\n                        >\n                          <a play-from-playlist=\"song\"\n                            ><span\n                              ng-if=\"song.source === 'xiami'\"\n                              style=\"\n                                color: orange;\n                                border-radius: 12px;\n                                border: solid 1px;\n                                padding: 0 4px;\n                              \"\n                              >⚠️ 🦐</span\n                            >{{ song.title }}</a\n                          >\n                        </div>\n                        <div class=\"song-singer\">\n                          <a\n                            ng-click=\"showPlaylist(song.artist_id); togglePlaylist();\"\n                            >{{ song.artist }}</a\n                          >\n                        </div>\n                        <div class=\"tools\">\n                          <span\n                            remove-from-playlist=\"song\"\n                            data-index=\"{{$index}}\"\n                            ng-show=\"playlist_highlight\"\n                            class=\"icon li-del\"\n                          ></span>\n                          <span\n                            open-url=\"song.source_url\"\n                            ng-show=\"playlist_highlight\"\n                            class=\"icon li-link\"\n                          ></span>\n                        </div>\n                        <!-- <div class=\"song-time\">00:00</div> -->\n                      </li>\n                    </ul>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      </div>\n  </div>\n  </body>\n  <!-- <script type=\"text/javascript\">\n    window.onload=function(){\n      let li = document.querySelectorAll(\".footer .cover li\")\n      for(let i=0;i<li.length-1;i++){\n          li[i].className=\"hid\"\n        }\n      li[li.length-3].className=\"def\"\n      li[li.length-2].className=\"a\"\n      li[0].className=\"b\"\n      li[1].className=\"c\"\n      li[2].className=\"def\"\n    }\n  </script> -->\n</html>"
  },
  {
    "path": "manifest.json",
    "content": "{\n  \"background\": {\n    \"service_worker\": \"js/background.js\"\n  },\n  \"action\": {\n    \"default_icon\": \"images/logo.png\",\n    \"default_title\": \"Listen 1\"\n  },\n  \"description\": \"One for all free music in China\",\n  \"icons\": {\n    \"128\": \"images/logo.png\",\n    \"16\": \"images/logo_16.png\",\n    \"48\": \"images/logo_48.png\"\n  },\n  \"manifest_version\": 3,\n  \"name\": \"Listen 1\",\n  \"permissions\": [\n    \"notifications\",\n    \"unlimitedStorage\",\n    \"cookies\",\n    \"declarativeNetRequest\"\n  ],\n  \"declarative_net_request\": {\n    \"rule_resources\": [\n      {\n        \"id\": \"ruleset_1\",\n        \"enabled\": true,\n        \"path\": \"rules_1.json\"\n      }\n    ]\n  },\n  \"host_permissions\": [\n    \"*://music.163.com/*\",\n    \"*://*.music.163.com/*\",\n    \"*://*.xiami.com/*\",\n    \"*://*.qq.com/*\",\n    \"*://*.kugou.com/\",\n    \"*://*.kuwo.cn/\",\n    \"*://*.bilibili.com/*\",\n    \"*://*.bilivideo.com/*\",\n    \"*://*.bilivideo.cn/*\",\n    \"*://*.migu.cn/*\",\n    \"*://*.taihe.com/*\",\n    \"*://music.91q.com/*\",\n    \"*://api.github.com/*\",\n    \"*://github.com/*\",\n    \"*://gist.githubusercontent.com/*\"\n  ],\n  \"version\": \"2.33.0\",\n  \"web_accessible_resources\": [\n    {\n      \"resources\": [\"images/*\"],\n      \"matches\": [\"*://*/*\"]\n    }\n  ],\n  \"content_scripts\": [\n    {\n      \"matches\": [\"https://listen1.github.io/listen1/*\"],\n      \"js\": [\"js/oauth_callback.js\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "manifest_firefox.json",
    "content": "{\n  \"applications\": {\n    \"gecko\": {\n      \"id\": \"githublisten1@gmail.com\",\n      \"strict_min_version\": \"45.0\"\n    }\n  },\n  \"background\": {\n    \"scripts\": [\n      \"js/vendor/axios.min.js\",\n      \"js/github.js\",\n      \"js/background.js\",\n      \"js/vendor/howler.core.min.js\",\n      \"js/bridge.js\",\n      \"js/player_thread.js\"\n    ],\n    \"persistent\": true\n  },\n  \"browser_action\": {\n    \"default_icon\": \"images/logo.png\",\n    \"default_title\": \"Listen 1\"\n  },\n  \"description\": \"One for all free music in China\",\n  \"icons\": {\n    \"128\": \"images/logo.png\",\n    \"16\": \"images/logo_16.png\",\n    \"48\": \"images/logo_48.png\"\n  },\n  \"manifest_version\": 2,\n  \"name\": \"Listen 1\",\n  \"permissions\": [\n    \"notifications\",\n    \"unlimitedStorage\",\n    \"downloads\",\n    \"storage\",\n    \"contextMenus\",\n    \"tabs\",\n    \"cookies\",\n    \"*://music.163.com/*\",\n    \"*://*.music.163.com/*\",\n    \"*://*.xiami.com/*\",\n    \"*://*.qq.com/*\",\n    \"*://*.kugou.com/\",\n    \"*://*.kuwo.cn/\",\n    \"*://*.bilibili.com/*\",\n    \"*://*.bilivideo.com/*\",\n    \"*://*.migu.cn/*\",\n    \"*://*.taihe.com/*\",\n    \"*://music.91q.com/*\",\n    \"*://api.github.com/*\",\n    \"*://github.com/*\",\n    \"*://gist.githubusercontent.com/*\",\n    \"webRequest\",\n    \"webRequestBlocking\"\n  ],\n  \"version\": \"2.33.0\",\n  \"web_accessible_resources\": [\"images/*\"],\n  \"content_scripts\": [\n    {\n      \"matches\": [\"https://listen1.github.io/listen1/*\"],\n      \"js\": [\"js/oauth_callback.js\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"listen1_chrome_extension\",\n  \"version\": \"2.33.0\",\n  \"description\": \"one for all free music in china\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/listen1/listen1_chrome_extension.git\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/listen1/listen1_chrome_extension/issues\"\n  },\n  \"homepage\": \"https://github.com/listen1/listen1_chrome_extension#readme\",\n  \"devDependencies\": {\n    \"eslint\": \"^7.30.0\",\n    \"eslint-config-airbnb-base\": \"^14.2.1\",\n    \"eslint-config-prettier\": \"^8.3.0\",\n    \"eslint-plugin-import\": \"^2.23.4\",\n    \"husky\": \"^4.3.8\",\n    \"lint-staged\": \"^10.5.4\",\n    \"prettier\": \"^2.3.2\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"!(**/vendor/*.js)*.js\": \"eslint --cache --fix\",\n    \"!(**/vendor/*.js)*.{js,css,md}\": \"prettier --write\"\n  }\n}\n"
  },
  {
    "path": "rules_1.json",
    "content": "[\n  {\n    \"id\": 1,\n    \"priority\": 1,\n    \"action\": {\n      \"type\": \"modifyHeaders\",\n      \"requestHeaders\": [\n        {\n          \"header\": \"referer\",\n          \"operation\": \"set\",\n          \"value\": \"https://y.qq.com/\"\n        },\n        {\n          \"header\": \"origin\",\n          \"operation\": \"set\",\n          \"value\": \"https://y.qq.com/\"\n        }\n      ]\n    },\n    \"condition\": {\n      \"urlFilter\": \"|*.y.qq.com\",\n      \"resourceTypes\": [\"main_frame\", \"xmlhttprequest\"]\n    }\n  },\n  {\n    \"id\": 2,\n    \"priority\": 1,\n    \"action\": {\n      \"type\": \"modifyHeaders\",\n      \"requestHeaders\": [\n        {\n          \"header\": \"user-agent\",\n          \"operation\": \"set\",\n          \"value\": \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\"\n        }\n      ]\n    },\n    \"condition\": {\n      \"urlFilter\": \"|*.kugou.com\",\n      \"resourceTypes\": [\"main_frame\", \"xmlhttprequest\"]\n    }\n  },\n  {\n    \"id\": 3,\n    \"priority\": 1,\n    \"action\": {\n      \"type\": \"modifyHeaders\",\n      \"requestHeaders\": [\n        {\n          \"header\": \"referer\",\n          \"operation\": \"set\",\n          \"value\": \"https://www.bilibili.com/\"\n        }\n      ]\n    },\n    \"condition\": {\n      \"urlFilter\": \"|*.bilivideo.com/\",\n      \"resourceTypes\": [\"media\"]\n    }\n  }\n]\n"
  }
]