From 5d5ba4007c49147ec2f2071e1cca02932824b5d7 Mon Sep 17 00:00:00 2001 From: Aresn Date: Fri, 10 May 2024 18:09:00 +0800 Subject: [PATCH] Fri May 10 18:09:00 CST 2024 inscode --- .dockerignore | 16 + .editorconfig | 8 + .env.example | 7 + .gitattributes | 7 + .github/ISSUE_TEMPLATE/----------.md | 21 + .github/workflows/build.yaml | 116 + .github/workflows/sync.yml | 48 + .gitignore | 17 +- .inscode | 8 +- .nvmrc | 1 + .prettierignore | 3 + .prettierrc | 12 + Dockerfile | 25 + LICENSE | 21 + README.md | 260 + babel.config.js | 11 + build/icons/1024x1024.png | Bin 0 -> 535997 bytes build/icons/128x128.png | Bin 0 -> 6564 bytes build/icons/16x16.png | Bin 0 -> 474 bytes build/icons/24x24.png | Bin 0 -> 750 bytes build/icons/256x256.png | Bin 0 -> 22892 bytes build/icons/32x32.png | Bin 0 -> 965 bytes build/icons/48x48.png | Bin 0 -> 1631 bytes build/icons/512x512.png | Bin 0 -> 107173 bytes build/icons/64x64.png | Bin 0 -> 2282 bytes build/icons/icon.icns | Bin 0 -> 809011 bytes build/icons/icon.ico | Bin 0 -> 361102 bytes build/icons/icon.png | Bin 0 -> 282449 bytes build/icons/menu@88.png | Bin 0 -> 1084 bytes docker-compose.yml | 39 + docker/nginx.conf.example | 28 + images/album.png | Bin 0 -> 258656 bytes images/artist.png | Bin 0 -> 233300 bytes images/explore.png | Bin 0 -> 362723 bytes images/home-2.png | Bin 0 -> 319717 bytes images/home.png | Bin 0 -> 398056 bytes images/library-dark.png | Bin 0 -> 343483 bytes images/library.png | Bin 0 -> 332219 bytes images/logo.png | Bin 0 -> 178986 bytes images/lyrics.png | Bin 0 -> 347434 bytes images/search.png | Bin 0 -> 282425 bytes index.js | 1 - install-replit.sh | 28 + jsconfig.json | 15 + package-lock.json | 24316 ++++++++++++++++ package.json | 145 +- public/favicon.ico | Bin 0 -> 15086 bytes public/img/icons/1024x1024.png | Bin 0 -> 390383 bytes public/img/icons/128x128.png | Bin 0 -> 9650 bytes public/img/icons/16x16.png | Bin 0 -> 523 bytes public/img/icons/24x24.png | Bin 0 -> 913 bytes public/img/icons/256x256.png | Bin 0 -> 35687 bytes public/img/icons/32x32.png | Bin 0 -> 1180 bytes public/img/icons/48x48.png | Bin 0 -> 1981 bytes public/img/icons/512x512.png | Bin 0 -> 152336 bytes public/img/icons/64x64.png | Bin 0 -> 2925 bytes public/img/icons/android-chrome-192x192.png | Bin 0 -> 16395 bytes public/img/icons/android-chrome-512x512.png | Bin 0 -> 129147 bytes public/img/icons/apple-touch-icon-120x120.png | Bin 0 -> 3369 bytes public/img/icons/apple-touch-icon-152x152.png | Bin 0 -> 4046 bytes public/img/icons/apple-touch-icon-180x180.png | Bin 0 -> 4678 bytes public/img/icons/apple-touch-icon-60x60.png | Bin 0 -> 1491 bytes public/img/icons/apple-touch-icon-76x76.png | Bin 0 -> 1823 bytes public/img/icons/apple-touch-icon.png | Bin 0 -> 14744 bytes public/img/icons/exit.png | Bin 0 -> 223 bytes public/img/icons/favicon-16x16.png | Bin 0 -> 1166 bytes public/img/icons/favicon-32x32.png | Bin 0 -> 1562 bytes public/img/icons/favicon.ico | Bin 0 -> 15086 bytes public/img/icons/icon.icns | Bin 0 -> 783497 bytes public/img/icons/icon.ico | Bin 0 -> 361102 bytes public/img/icons/left.png | Bin 0 -> 191 bytes public/img/icons/like.png | Bin 0 -> 308 bytes public/img/icons/menu-dark@88.png | Bin 0 -> 1096 bytes public/img/icons/menu-light@88.png | Bin 0 -> 1084 bytes public/img/icons/menu.png | Bin 0 -> 311 bytes .../img/icons/msapplication-icon-144x144.png | Bin 0 -> 1169 bytes public/img/icons/mstile-150x150.png | Bin 0 -> 10295 bytes public/img/icons/pause.png | Bin 0 -> 953 bytes public/img/icons/play.png | Bin 0 -> 396 bytes public/img/icons/repeat.png | Bin 0 -> 344 bytes public/img/icons/right.png | Bin 0 -> 218 bytes public/img/icons/safari-pinned-tab.svg | 3 + public/img/icons/unlike.png | Bin 0 -> 932 bytes public/img/logos/lastfm.png | Bin 0 -> 9135 bytes public/img/logos/netease-music.png | Bin 0 -> 11223 bytes public/img/logos/nyancat-stop.png | Bin 0 -> 816 bytes public/img/logos/nyancat.gif | Bin 0 -> 29361 bytes public/img/logos/yesplaymusic-white24x24.png | Bin 0 -> 750 bytes public/img/logos/yesplaymusic.png | Bin 0 -> 49099 bytes public/img/touchbar/backward.png | Bin 0 -> 801 bytes public/img/touchbar/forward.png | Bin 0 -> 779 bytes public/img/touchbar/like.png | Bin 0 -> 1150 bytes public/img/touchbar/like_fill.png | Bin 0 -> 871 bytes public/img/touchbar/next_up.png | Bin 0 -> 855 bytes public/img/touchbar/page_next.png | Bin 0 -> 656 bytes public/img/touchbar/page_prev.png | Bin 0 -> 610 bytes public/img/touchbar/pause.png | Bin 0 -> 499 bytes public/img/touchbar/play.png | Bin 0 -> 746 bytes public/img/touchbar/search.png | Bin 0 -> 1084 bytes public/index.html | 25 + public/robots.txt | 2 + restyled.yml | 7 + src/App.vue | 148 + src/api/album.js | 79 + src/api/artist.js | 107 + src/api/auth.js | 110 + src/api/lastfm.js | 80 + src/api/mv.js | 69 + src/api/others.js | 50 + src/api/playlist.js | 229 + src/api/track.js | 148 + src/api/user.js | 221 + src/assets/css/global.scss | 135 + src/assets/css/nprogress.css | 40 + src/assets/css/plyr.css | 1811 ++ src/assets/css/slider.css | 144 + src/assets/fonts/Barlow-Black.ttf | Bin 0 -> 105403 bytes src/assets/fonts/Barlow-Black.woff2 | Bin 0 -> 38196 bytes src/assets/fonts/Barlow-Bold.ttf | Bin 0 -> 102460 bytes src/assets/fonts/Barlow-Bold.woff2 | Bin 0 -> 38284 bytes src/assets/fonts/Barlow-ExtraBold.ttf | Bin 0 -> 104370 bytes src/assets/fonts/Barlow-ExtraBold.woff2 | Bin 0 -> 38680 bytes src/assets/fonts/Barlow-Medium.ttf | Bin 0 -> 97763 bytes src/assets/fonts/Barlow-Medium.woff2 | Bin 0 -> 37076 bytes src/assets/fonts/Barlow-Regular.ttf | Bin 0 -> 98327 bytes src/assets/fonts/Barlow-Regular.woff2 | Bin 0 -> 37168 bytes src/assets/fonts/Barlow-SemiBold.ttf | Bin 0 -> 102537 bytes src/assets/fonts/Barlow-SemiBold.woff2 | Bin 0 -> 38324 bytes src/assets/icons/arrow-down.svg | 1 + src/assets/icons/arrow-left.svg | 1 + src/assets/icons/arrow-right.svg | 1 + src/assets/icons/arrow-up-alt.svg | 1 + src/assets/icons/arrow-up.svg | 1 + src/assets/icons/dropdown.svg | 1 + src/assets/icons/explicit.svg | 1 + src/assets/icons/fm.svg | 1 + src/assets/icons/github.svg | 1 + src/assets/icons/heart-solid.svg | 2 + src/assets/icons/heart.svg | 2 + src/assets/icons/index.js | 7 + src/assets/icons/list.svg | 1 + src/assets/icons/lock.svg | 1 + src/assets/icons/login.svg | 1 + src/assets/icons/logout.svg | 1 + src/assets/icons/mail.svg | 1 + src/assets/icons/mobile.svg | 1 + src/assets/icons/more.svg | 1 + src/assets/icons/next.svg | 1 + src/assets/icons/pause.svg | 1 + src/assets/icons/play.svg | 1 + src/assets/icons/plus.svg | 1 + src/assets/icons/previous.svg | 1 + src/assets/icons/repeat-1.svg | 1 + src/assets/icons/repeat.svg | 1 + src/assets/icons/search.svg | 1 + src/assets/icons/settings.svg | 1 + src/assets/icons/shuffle.svg | 1 + src/assets/icons/sort-up.svg | 2 + src/assets/icons/thumbs-down.svg | 1 + src/assets/icons/volume-half.svg | 1 + src/assets/icons/volume-mute.svg | 1 + src/assets/icons/volume.svg | 1 + src/assets/icons/x.svg | 1 + src/background.js | 489 + src/components/ArtistsInLine.vue | 53 + src/components/ButtonIcon.vue | 36 + src/components/ButtonTwoTone.vue | 96 + src/components/ContextMenu.vue | 181 + src/components/Cover.vue | 171 + src/components/CoverRow.vue | 201 + src/components/DailyTracksCard.vue | 172 + src/components/ExplicitSymbol.vue | 35 + src/components/FMCard.vue | 193 + src/components/LinuxTitlebar.vue | 130 + src/components/Modal.vue | 193 + src/components/ModalAddTrackToPlaylist.vue | 174 + src/components/ModalNewPlaylist.vue | 154 + src/components/MvRow.vue | 177 + src/components/Navbar.vue | 349 + src/components/Player.vue | 447 + src/components/Scrollbar.vue | 198 + src/components/SvgIcon.vue | 39 + src/components/Toast.vue | 51 + src/components/TrackList.vue | 312 + src/components/TrackListItem.vue | 446 + src/components/Win32Titlebar.vue | 121 + src/electron/dockMenu.js | 25 + src/electron/globalShortcut.js | 58 + src/electron/ipcMain.js | 323 + src/electron/ipcRenderer.js | 94 + src/electron/menu.js | 220 + src/electron/mpris.js | 94 + src/electron/services.js | 14 + src/electron/touchBar.js | 97 + src/electron/tray.js | 216 + src/locale/index.js | 25 + src/locale/lang/en.js | 257 + src/locale/lang/tr.js | 238 + src/locale/lang/zh-CN.js | 256 + src/locale/lang/zh-TW.js | 253 + src/main.js | 48 + src/ncmModDef.js | 617 + src/registerServiceWorker.js | 34 + src/router/index.js | 171 + src/store/actions.js | 201 + src/store/index.js | 67 + src/store/initLocalStorage.js | 53 + src/store/mutations.js | 78 + src/store/plugins/localStorage.js | 7 + src/store/plugins/sendSettings.js | 11 + src/store/state.js | 54 + src/utils/Player.js | 998 + src/utils/auth.js | 56 + src/utils/base64.js | 67 + src/utils/checkAuthToken.js | 8 + src/utils/common.js | 227 + src/utils/db.js | 183 + src/utils/filters.js | 113 + src/utils/lyrics.js | 86 + src/utils/nativeAlert.js | 30 + src/utils/platform.js | 7 + src/utils/playList.js | 61 + src/utils/request.js | 92 + src/utils/shortcuts.js | 47 + src/utils/staticData.js | 420 + src/utils/updateApp.js | 62 + src/views/album.vue | 438 + src/views/artist.vue | 547 + src/views/artistMV.vue | 100 + src/views/dailyTracks.vue | 132 + src/views/explore.vue | 300 + src/views/home.vue | 205 + src/views/lastfmCallback.vue | 96 + src/views/library.vue | 624 + src/views/login.vue | 151 + src/views/loginAccount.vue | 491 + src/views/loginUsername.vue | 205 + src/views/lyrics.vue | 1028 + src/views/mv.vue | 225 + src/views/newAlbum.vue | 49 + src/views/next.vue | 142 + src/views/playlist.vue | 948 + src/views/search.vue | 288 + src/views/searchType.vue | 161 + src/views/settings.vue | 1714 ++ vercel.example.json | 8 + vue.config.js | 208 + yarn.lock | 13767 +++++++++ 248 files changed, 60797 insertions(+), 21 deletions(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/----------.md create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/sync.yml create mode 100644 .nvmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 babel.config.js create mode 100644 build/icons/1024x1024.png create mode 100644 build/icons/128x128.png create mode 100644 build/icons/16x16.png create mode 100644 build/icons/24x24.png create mode 100644 build/icons/256x256.png create mode 100644 build/icons/32x32.png create mode 100644 build/icons/48x48.png create mode 100644 build/icons/512x512.png create mode 100644 build/icons/64x64.png create mode 100644 build/icons/icon.icns create mode 100644 build/icons/icon.ico create mode 100644 build/icons/icon.png create mode 100644 build/icons/menu@88.png create mode 100644 docker-compose.yml create mode 100644 docker/nginx.conf.example create mode 100644 images/album.png create mode 100644 images/artist.png create mode 100644 images/explore.png create mode 100644 images/home-2.png create mode 100644 images/home.png create mode 100644 images/library-dark.png create mode 100644 images/library.png create mode 100644 images/logo.png create mode 100644 images/lyrics.png create mode 100644 images/search.png delete mode 100644 index.js create mode 100644 install-replit.sh create mode 100644 jsconfig.json create mode 100644 package-lock.json create mode 100644 public/favicon.ico create mode 100644 public/img/icons/1024x1024.png create mode 100644 public/img/icons/128x128.png create mode 100644 public/img/icons/16x16.png create mode 100644 public/img/icons/24x24.png create mode 100644 public/img/icons/256x256.png create mode 100644 public/img/icons/32x32.png create mode 100644 public/img/icons/48x48.png create mode 100644 public/img/icons/512x512.png create mode 100644 public/img/icons/64x64.png create mode 100644 public/img/icons/android-chrome-192x192.png create mode 100644 public/img/icons/android-chrome-512x512.png create mode 100644 public/img/icons/apple-touch-icon-120x120.png create mode 100644 public/img/icons/apple-touch-icon-152x152.png create mode 100644 public/img/icons/apple-touch-icon-180x180.png create mode 100644 public/img/icons/apple-touch-icon-60x60.png create mode 100644 public/img/icons/apple-touch-icon-76x76.png create mode 100644 public/img/icons/apple-touch-icon.png create mode 100644 public/img/icons/exit.png create mode 100644 public/img/icons/favicon-16x16.png create mode 100644 public/img/icons/favicon-32x32.png create mode 100644 public/img/icons/favicon.ico create mode 100644 public/img/icons/icon.icns create mode 100644 public/img/icons/icon.ico create mode 100644 public/img/icons/left.png create mode 100644 public/img/icons/like.png create mode 100644 public/img/icons/menu-dark@88.png create mode 100644 public/img/icons/menu-light@88.png create mode 100644 public/img/icons/menu.png create mode 100644 public/img/icons/msapplication-icon-144x144.png create mode 100644 public/img/icons/mstile-150x150.png create mode 100644 public/img/icons/pause.png create mode 100644 public/img/icons/play.png create mode 100644 public/img/icons/repeat.png create mode 100644 public/img/icons/right.png create mode 100644 public/img/icons/safari-pinned-tab.svg create mode 100644 public/img/icons/unlike.png create mode 100644 public/img/logos/lastfm.png create mode 100644 public/img/logos/netease-music.png create mode 100644 public/img/logos/nyancat-stop.png create mode 100644 public/img/logos/nyancat.gif create mode 100644 public/img/logos/yesplaymusic-white24x24.png create mode 100644 public/img/logos/yesplaymusic.png create mode 100644 public/img/touchbar/backward.png create mode 100644 public/img/touchbar/forward.png create mode 100644 public/img/touchbar/like.png create mode 100644 public/img/touchbar/like_fill.png create mode 100644 public/img/touchbar/next_up.png create mode 100644 public/img/touchbar/page_next.png create mode 100644 public/img/touchbar/page_prev.png create mode 100644 public/img/touchbar/pause.png create mode 100644 public/img/touchbar/play.png create mode 100644 public/img/touchbar/search.png create mode 100644 public/index.html create mode 100644 public/robots.txt create mode 100644 restyled.yml create mode 100644 src/App.vue create mode 100644 src/api/album.js create mode 100644 src/api/artist.js create mode 100644 src/api/auth.js create mode 100644 src/api/lastfm.js create mode 100644 src/api/mv.js create mode 100644 src/api/others.js create mode 100644 src/api/playlist.js create mode 100644 src/api/track.js create mode 100644 src/api/user.js create mode 100644 src/assets/css/global.scss create mode 100644 src/assets/css/nprogress.css create mode 100644 src/assets/css/plyr.css create mode 100644 src/assets/css/slider.css create mode 100644 src/assets/fonts/Barlow-Black.ttf create mode 100644 src/assets/fonts/Barlow-Black.woff2 create mode 100644 src/assets/fonts/Barlow-Bold.ttf create mode 100644 src/assets/fonts/Barlow-Bold.woff2 create mode 100644 src/assets/fonts/Barlow-ExtraBold.ttf create mode 100644 src/assets/fonts/Barlow-ExtraBold.woff2 create mode 100644 src/assets/fonts/Barlow-Medium.ttf create mode 100644 src/assets/fonts/Barlow-Medium.woff2 create mode 100644 src/assets/fonts/Barlow-Regular.ttf create mode 100644 src/assets/fonts/Barlow-Regular.woff2 create mode 100644 src/assets/fonts/Barlow-SemiBold.ttf create mode 100644 src/assets/fonts/Barlow-SemiBold.woff2 create mode 100644 src/assets/icons/arrow-down.svg create mode 100644 src/assets/icons/arrow-left.svg create mode 100644 src/assets/icons/arrow-right.svg create mode 100644 src/assets/icons/arrow-up-alt.svg create mode 100644 src/assets/icons/arrow-up.svg create mode 100644 src/assets/icons/dropdown.svg create mode 100644 src/assets/icons/explicit.svg create mode 100644 src/assets/icons/fm.svg create mode 100644 src/assets/icons/github.svg create mode 100644 src/assets/icons/heart-solid.svg create mode 100644 src/assets/icons/heart.svg create mode 100644 src/assets/icons/index.js create mode 100644 src/assets/icons/list.svg create mode 100644 src/assets/icons/lock.svg create mode 100644 src/assets/icons/login.svg create mode 100644 src/assets/icons/logout.svg create mode 100644 src/assets/icons/mail.svg create mode 100644 src/assets/icons/mobile.svg create mode 100644 src/assets/icons/more.svg create mode 100644 src/assets/icons/next.svg create mode 100644 src/assets/icons/pause.svg create mode 100644 src/assets/icons/play.svg create mode 100644 src/assets/icons/plus.svg create mode 100644 src/assets/icons/previous.svg create mode 100644 src/assets/icons/repeat-1.svg create mode 100644 src/assets/icons/repeat.svg create mode 100644 src/assets/icons/search.svg create mode 100644 src/assets/icons/settings.svg create mode 100644 src/assets/icons/shuffle.svg create mode 100644 src/assets/icons/sort-up.svg create mode 100644 src/assets/icons/thumbs-down.svg create mode 100644 src/assets/icons/volume-half.svg create mode 100644 src/assets/icons/volume-mute.svg create mode 100644 src/assets/icons/volume.svg create mode 100644 src/assets/icons/x.svg create mode 100644 src/background.js create mode 100644 src/components/ArtistsInLine.vue create mode 100644 src/components/ButtonIcon.vue create mode 100644 src/components/ButtonTwoTone.vue create mode 100644 src/components/ContextMenu.vue create mode 100644 src/components/Cover.vue create mode 100644 src/components/CoverRow.vue create mode 100644 src/components/DailyTracksCard.vue create mode 100644 src/components/ExplicitSymbol.vue create mode 100644 src/components/FMCard.vue create mode 100644 src/components/LinuxTitlebar.vue create mode 100644 src/components/Modal.vue create mode 100644 src/components/ModalAddTrackToPlaylist.vue create mode 100644 src/components/ModalNewPlaylist.vue create mode 100644 src/components/MvRow.vue create mode 100644 src/components/Navbar.vue create mode 100644 src/components/Player.vue create mode 100644 src/components/Scrollbar.vue create mode 100644 src/components/SvgIcon.vue create mode 100644 src/components/Toast.vue create mode 100644 src/components/TrackList.vue create mode 100644 src/components/TrackListItem.vue create mode 100644 src/components/Win32Titlebar.vue create mode 100644 src/electron/dockMenu.js create mode 100644 src/electron/globalShortcut.js create mode 100644 src/electron/ipcMain.js create mode 100644 src/electron/ipcRenderer.js create mode 100644 src/electron/menu.js create mode 100644 src/electron/mpris.js create mode 100644 src/electron/services.js create mode 100644 src/electron/touchBar.js create mode 100644 src/electron/tray.js create mode 100644 src/locale/index.js create mode 100644 src/locale/lang/en.js create mode 100644 src/locale/lang/tr.js create mode 100644 src/locale/lang/zh-CN.js create mode 100644 src/locale/lang/zh-TW.js create mode 100644 src/main.js create mode 100644 src/ncmModDef.js create mode 100644 src/registerServiceWorker.js create mode 100644 src/router/index.js create mode 100644 src/store/actions.js create mode 100644 src/store/index.js create mode 100644 src/store/initLocalStorage.js create mode 100644 src/store/mutations.js create mode 100644 src/store/plugins/localStorage.js create mode 100644 src/store/plugins/sendSettings.js create mode 100644 src/store/state.js create mode 100644 src/utils/Player.js create mode 100644 src/utils/auth.js create mode 100644 src/utils/base64.js create mode 100644 src/utils/checkAuthToken.js create mode 100644 src/utils/common.js create mode 100644 src/utils/db.js create mode 100644 src/utils/filters.js create mode 100644 src/utils/lyrics.js create mode 100644 src/utils/nativeAlert.js create mode 100644 src/utils/platform.js create mode 100644 src/utils/playList.js create mode 100644 src/utils/request.js create mode 100644 src/utils/shortcuts.js create mode 100644 src/utils/staticData.js create mode 100644 src/utils/updateApp.js create mode 100644 src/views/album.vue create mode 100644 src/views/artist.vue create mode 100644 src/views/artistMV.vue create mode 100644 src/views/dailyTracks.vue create mode 100644 src/views/explore.vue create mode 100644 src/views/home.vue create mode 100644 src/views/lastfmCallback.vue create mode 100644 src/views/library.vue create mode 100644 src/views/login.vue create mode 100644 src/views/loginAccount.vue create mode 100644 src/views/loginUsername.vue create mode 100644 src/views/lyrics.vue create mode 100644 src/views/mv.vue create mode 100644 src/views/newAlbum.vue create mode 100644 src/views/next.vue create mode 100644 src/views/playlist.vue create mode 100644 src/views/search.vue create mode 100644 src/views/searchType.vue create mode 100644 src/views/settings.vue create mode 100644 vercel.example.json create mode 100644 vue.config.js create mode 100644 yarn.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..03f2450 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +node_modules +npm-debug.log +Dockerfile* +docker-compose* +.dockerignore +.git +.github +.gitignore +README.md +LICENSE +.vscode +dist +dist_electron +build +images +script \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..db87e1f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7c0058a --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +VUE_APP_NETEASE_API_URL=/api +VUE_APP_ELECTRON_API_URL=/api +VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:10754 +VUE_APP_LASTFM_API_KEY=09c55292403d961aa517ff7f5e8a3d9c +VUE_APP_LASTFM_API_SHARED_SECRET=307c9fda32b3904e53654baff215cb67 +DEV_SERVER_PORT=20201 + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ac10488 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text eol=lf +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.mp3 binary +*.icns binary +*.gif binary diff --git a/.github/ISSUE_TEMPLATE/----------.md b/.github/ISSUE_TEMPLATE/----------.md new file mode 100644 index 0000000..ba42d00 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/----------.md @@ -0,0 +1,21 @@ +--- +name: 反馈问题或请求新功能 +about: bug & feature +title: '' +labels: '' +assignees: '' + +--- + +# 尽量每个 issue 只提一个 bug 或新功能 + +### 提新 issue 前请确认 👉 + +- 没人提过这个 issue([这里看所有 issue](https://github.com/qier222/YesPlayMusic/issues)) +- 项目的 Todo 里没有与你 issue 相关的内容([这里看 Todo](https://github.com/qier222/YesPlayMusic/projects/1)) + +### 反馈 bug 需要的信息 + +- 用的是网页版还是客户端 +- 浏览器名称或电脑操作系统 +- 控制台 Console 页面的截图(按 F12 可打开控制台) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..4caf481 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,116 @@ +name: Release + +env: + YARN_INSTALL_NOPT: yarn add --ignore-platform --ignore-optional + +on: + push: + branches: + - master + tags: + - v* + workflow_dispatch: + +jobs: + release: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-22.04] + + steps: + - name: Check out Git repository + uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Install Node.js, NPM and Yarn + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'yarn' + + - name: Install RPM & Pacman (on Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update && + sudo apt-get install --no-install-recommends -y rpm && + sudo apt-get install --no-install-recommends -y libarchive-tools && + sudo apt-get install --no-install-recommends -y libopenjp2-tools + + - name: Install Snapcraft (on Ubuntu) + uses: samuelmeuli/action-snapcraft@v1 + if: startsWith(matrix.os, 'ubuntu') + with: + snapcraft_token: ${{ secrets.snapcraft_token }} + + - id: get_unm_version + name: Get the installed UNM version + run: | + yarn --ignore-optional + unm_version=$(node -e "console.log(require('./node_modules/@unblockneteasemusic/rust-napi/package.json').version)") + echo "::set-output name=unmver::${unm_version}" + shell: bash + + - name: Install UNM dependencies for Windows + if: runner.os == 'Windows' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-win32-x64-msvc@${{steps.get_unm_version.outputs.unmver}} + shell: bash + + - name: Install UNM dependencies for macOS + if: runner.os == 'macOS' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-darwin-x64@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-darwin-arm64@${{steps.get_unm_version.outputs.unmver}} \ + dmg-license + shell: bash + + - name: Install UNM dependencies for Linux + if: runner.os == 'Linux' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-linux-x64-gnu@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-linux-arm64-gnu@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-linux-arm-gnueabihf@${{steps.get_unm_version.outputs.unmver}} + shell: bash + + - name: Build/release Electron app + uses: samuelmeuli/action-electron-builder@v1.6.0 + env: + VUE_APP_NETEASE_API_URL: /api + VUE_APP_ELECTRON_API_URL: /api + VUE_APP_ELECTRON_API_URL_DEV: http://127.0.0.1:10754 + VUE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c + VUE_APP_LASTFM_API_SHARED_SECRET: 307c9fda32b3904e53654baff215cb67 + with: + # GitHub token, automatically provided to the action + # (No need to define this secret in the repo settings) + github_token: ${{ secrets.github_token }} + + # If the commit is tagged with a version (e.g. "v1.0.0"), + # release the app after building + release: ${{ startsWith(github.ref, 'refs/tags/v') }} + + use_vue_cli: true + + - uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-mac + path: dist_electron/*-universal.dmg + if-no-files-found: ignore + + - uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-win + path: dist_electron/*Setup*.exe + if-no-files-found: ignore + + - uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-linux + path: dist_electron/*.AppImage + if-no-files-found: ignore diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000..f8e0be8 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,48 @@ +name: Upstream Sync + +permissions: + contents: write + issues: write + actions: write + +on: + schedule: + - cron: '0 * * * *' # every hour + workflow_dispatch: + +jobs: + sync_latest_from_upstream: + name: Sync latest commits from upstream repo + runs-on: ubuntu-latest + if: ${{ github.event.repository.fork }} + + steps: + - uses: actions/checkout@v4 + + - name: Clean issue notice + uses: actions-cool/issues-helper@v3 + with: + actions: 'close-issues' + labels: '🚨 Sync Fail' + + - name: Sync upstream changes + id: sync + uses: aormsby/Fork-Sync-With-Upstream-action@v3.4 + with: + upstream_sync_repo: qier222/YesPlayMusic + upstream_sync_branch: master + target_sync_branch: master + target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set + test_mode: false + + - name: Sync check + if: failure() + uses: actions-cool/issues-helper@v3 + with: + actions: 'create-issue' + title: '🚨 同步失败 | Sync Fail' + labels: '🚨 Sync Fail' + body: | + Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. + + 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次。 diff --git a/.gitignore b/.gitignore index dc99ae8..b96fe0f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ node_modules /dist - # local env files +.env .env.local .env.*.local @@ -12,14 +12,23 @@ npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* -package-lock.json # Editor directories and files .idea -.vscode/* -!.vscode/preview.yml +.vscode *.suo *.ntvs* *.njsproj *.sln *.sw? + +.vercel + +#Electron-builder output +/dist_electron +NeteaseCloudMusicApi-master +NeteaseCloudMusicApi-master.zip + +# Local Netlify folder +.netlify +vercel.json diff --git a/.inscode b/.inscode index ecc8bdd..ca9dbb6 100644 --- a/.inscode +++ b/.inscode @@ -1,6 +1,10 @@ -run = "npm i && npm run dev" +run = "yarn install && yarn serve" +language = "node" [env] PATH = "/root/${PROJECT_DIR}/.config/npm/node_global/bin:/root/${PROJECT_DIR}/node_modules/.bin:${PATH}" XDG_CONFIG_HOME = "/root/.config" -npm_config_prefix = "/root/${PROJECT_DIR}/.config/npm/node_global" \ No newline at end of file +npm_config_prefix = "/root/${PROJECT_DIR}/.config/npm/node_global" + +[debugger] +program = "main.js" diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..13859d2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +build +coverage +dist diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..38800a3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "arrowParens": "avoid", + "endOfLine": "lf", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "strict" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2fb8f95 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM node:16.13.1-alpine as build +ENV VUE_APP_NETEASE_API_URL=/api +WORKDIR /app +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\ + apk add --no-cache python3 make g++ git +COPY package.json yarn.lock ./ +RUN yarn install +COPY . . +RUN yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ && \ + yarn build + +FROM nginx:1.20.2-alpine as app + +COPY --from=build /app/package.json /usr/local/lib/ + +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\ + apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \ + && apk add --no-cache --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main nodejs npm \ + && npm i -g $(awk -F \" '{if($2=="NeteaseCloudMusicApi") print $2"@"$4}' /usr/local/lib/package.json) \ + && rm -f /usr/local/lib/package.json + +COPY --from=build /app/docker/nginx.conf.example /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +CMD nginx ; exec npx NeteaseCloudMusicApi \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7385fd2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 qier222 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..35018c4 --- /dev/null +++ b/README.md @@ -0,0 +1,260 @@ +
+

+ + Logo + +

YesPlayMusic

+ +

+ 高颜值的第三方网易云播放器 +
+ 🌎 访问DEMO  |   + 📦️ 下载安装包  |   + 💬 加入交流群 +
+
+

+

+ +[![Library][library-screenshot]](https://music.qier222.com) + + +## 全新版本 +全新2.0 Alpha测试版已发布,欢迎前往 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载。 +当前版本将会进入维护模式,除重大bug修复外,不会再更新新功能。 + +## ✨ 特性 + +- ✅ 使用 Vue.js 全家桶开发 +- 🔴 网易云账号登录(扫码/手机/邮箱登录) +- 📺 支持 MV 播放 +- 📃 支持歌词显示 +- 📻 支持私人 FM / 每日推荐歌曲 +- 🚫🤝 无任何社交功能 +- 🌎️ 海外用户可直接播放(需要登录网易云账号) +- 🔐 支持 [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server#音源清单),自动使用[各类音源](https://github.com/UnblockNeteaseMusic/server#音源清单)替换变灰歌曲链接 (网页版不支持) + - 「各类音源」指默认启用的音源。 + - YouTube 音源需自行安装 `yt-dlp`。 +- ✔️ 每日自动签到(手机端和电脑端同时签到) +- 🌚 Light/Dark Mode 自动切换 +- 👆 支持 Touch Bar +- 🖥️ 支持 PWA,可在 Chrome/Edge 里点击地址栏右边的 ➕ 安装到电脑 +- 🟥 支持 Last.fm Scrobble +- ☁️ 支持音乐云盘 +- ⌨️ 自定义快捷键和全局快捷键 +- 🎧 支持 Mpris +- 🛠 更多特性开发中 + +## 📦️ 安装 + +Electron 版本由 [@hawtim](https://github.com/hawtim) 和 [@qier222](https://github.com/qier222) 适配并维护,支持 macOS、Windows、Linux。 + +访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases) +页面下载安装包。 + +- macOS 用户可以通过 Homebrew 来安装:`brew install --cask yesplaymusic` + +- Windows 用户可以通过 Scoop 来安装:`scoop install extras/yesplaymusic` + +## ⚙️ 部署至 Vercel + +除了下载安装包使用,你还可以将本项目部署到 Vercel 或你的服务器上。下面是部署到 Vercel 的方法。 + +本项目的 Demo (https://music.qier222.com) 就是部署在 Vercel 上的网站。 + +[![Powered by Vercel](https://www.datocms-assets.com/31049/1618983297-powered-by-vercel.svg)](https://vercel.com/?utm_source=ohmusic&utm_campaign=oss) + +1. 部署网易云 API,详情参见 [Binaryify/NeteaseCloudMusicApi](https://neteasecloudmusicapi.vercel.app/#/?id=%e5%ae%89%e8%a3%85) + 。你也可以将 API 部署到 Vercel。 + +2. 点击本仓库右上角的 Fork,复制本仓库到你的 GitHub 账号。 + +3. 点击仓库的 Add File,选择 Create new file,输入 `vercel.json`,将下面的内容复制粘贴到文件中,并将 `https://your-netease-api.example.com` 替换为你刚刚部署的网易云 API 地址: + +```json +{ + "rewrites": [ + { + "source": "/api/:match*", + "destination": "https://your-netease-api.example.com/:match*" + } + ] +} +``` + +4. 打开 [Vercel.com](https://vercel.com),使用 GitHub 登录。 + +5. 点击 Import Git Repository 并选择你刚刚复制的仓库并点击 Import。 + +6. 点击 PERSONAL ACCOUNT 旁边的 Select。 + +7. 点击 Environment Variables,填写 Name 为 `VUE_APP_NETEASE_API_URL`,Value 为 `/api`,点击 Add。最后点击底部的 Deploy 就可以部署到 + Vercel 了。 + +## ⚙️ 部署到自己的服务器 + +除了部署到 Vercel,你还可以部署到自己的服务器上 + +1. 部署网易云 API,详情参见 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) +2. 克隆本仓库 + +```sh +git clone --recursive https://github.com/qier222/YesPlayMusic.git +``` + +3. 安装依赖 + +```sh +yarn install + +``` + +4. (可选)使用 Nginx 反向代理 API,将 API 路径映射为 `/api`,如果 API 和网页不在同一个域名下的话(跨域),会有一些 bug。 + +5. 复制 `/.env.example` 文件为 `/.env`,修改里面 `VUE_APP_NETEASE_API_URL` 的值为网易云 API 地址。本地开发的话可以填写 API 地址为 `http://localhost:3000`,YesPlayMusic 地址为 `http://localhost:8080`。如果你使用了反向代理 API,可以填写 API 地址为 `/api`。 + +``` +VUE_APP_NETEASE_API_URL=http://localhost:3000 +``` + +6. 编译打包 + +```sh +yarn run build +``` + +7. 将 `/dist` 目录下的文件上传到你的 Web 服务器 + +## ⚙️ Docker 部署 + +1. 构建 Docker Image + +```sh +docker build -t yesplaymusic . +``` + +2. 启动 Docker Container + +```sh +docker run -d --name YesPlayMusic -p 80:80 yesplaymusic +``` + +3. Docker Compose 启动 + +```sh +docker-compose up -d +``` + +YesPlayMusic 地址为 `http://localhost` + +## ⚙️ 部署至 Replit + +1. 新建 Repl,选择 Bash 模板 + +2. 在 Replit shell 中运行以下命令 + +```sh +bash <(curl -s -L https://raw.githubusercontent.com/qier222/YesPlayMusic/main/install-replit.sh) +``` + +3. 首次运行成功后,只需点击绿色按钮 `Run` 即可再次运行 + +4. 由于 replit 个人版限制内存为 1G(教育版为 3G),构建过程中可能会失败,请再次运行上述命令或运行以下命令: + +```sh +cd /home/runner/${REPL_SLUG}/music && yarn install && yarn run build +``` + +## 👷‍♂️ 打包客户端 + +如果在 Release 页面没有找到适合你的设备的安装包的话,你可以根据下面的步骤来打包自己的客户端。 + +1. 打包 Electron 需要用到 Node.js 和 Yarn。可前往 [Node.js 官网](https://nodejs.org/zh-cn/) 下载安装包。安装 Node.js + 后可在终端里执行 `npm install -g yarn` 来安装 Yarn。 + +2. 使用 `git clone --recursive https://github.com/qier222/YesPlayMusic.git` 克隆本仓库到本地。 + +3. 使用 `yarn install` 安装项目依赖。 + +4. 复制 `/.env.example` 文件为 `/.env` 。 + +5. 选择下列表格的命令来打包适合的你的安装包,打包出来的文件在 `/dist_electron` 目录下。了解更多信息可访问 [electron-builder 文档](https://www.electron.build/cli) + +| 命令 | 说明 | +| ------------------------------------------ | ------------------------- | +| `yarn electron:build --windows nsis:ia32` | Windows 32 位 | +| `yarn electron:build --windows nsis:arm64` | Windows ARM | +| `yarn electron:build --linux deb:armv7l` | Debian armv7l(树莓派等) | +| `yarn electron:build --macos dir:arm64` | macOS ARM | + +## :computer: 配置开发环境 + +本项目由 [NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) 提供 API。 + +运行本项目 + +```shell +# 安装依赖 +yarn install + +# 创建本地环境变量 +cp .env.example .env + +# 运行(网页端) +yarn serve + +# 运行(electron) +yarn electron:serve +``` + +本地运行 NeteaseCloudMusicApi,或者将 API [部署至 Vercel](#%EF%B8%8F-部署至-vercel) + +```shell +# 运行 API (默认 3000 端口) +yarn netease_api:run +``` + +## ☑️ Todo + +查看 Todo 请访问本项目的 [Projects](https://github.com/qier222/YesPlayMusic/projects/1) + +欢迎提 Issue 和 Pull request。 + +## 📜 开源许可 + +本项目仅供个人学习研究使用,禁止用于商业及非法用途。 + +基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。 + +## 灵感来源 + +API 源代码来自 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) + +- [Apple Music](https://music.apple.com) +- [YouTube Music](https://music.youtube.com) +- [Spotify](https://www.spotify.com) +- [网易云音乐](https://music.163.com) + +## 🖼️ 截图 + +![lyrics][lyrics-screenshot] +![library-dark][library-dark-screenshot] +![album][album-screenshot] +![home-2][home-2-screenshot] +![artist][artist-screenshot] +![search][search-screenshot] +![home][home-screenshot] +![explore][explore-screenshot] + + + + +[album-screenshot]: images/album.png +[artist-screenshot]: images/artist.png +[explore-screenshot]: images/explore.png +[home-screenshot]: images/home.png +[home-2-screenshot]: images/home-2.png +[lyrics-screenshot]: images/lyrics.png +[library-screenshot]: images/library.png +[library-dark-screenshot]: images/library-dark.png +[search-screenshot]: images/search.png diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..707ce4a --- /dev/null +++ b/babel.config.js @@ -0,0 +1,11 @@ +module.exports = { + presets: [ + [ + '@vue/cli-plugin-babel/preset', + { + useBuiltIns: 'usage', + shippedProposals: true, + }, + ], + ], +}; diff --git a/build/icons/1024x1024.png b/build/icons/1024x1024.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc1c628fc2ad728018f8ddd6242b6952aaab0f1 GIT binary patch literal 535997 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

STOsdgdSo`x;0s}%HEjy{lwFlQyjHS(RT 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

Y|V(;75VU4*p)iz_8H~`` zSKiE>)`Eg+j2k{24`G9SB>z-blA+xXciDOT^l7RU4YO5yQ^;u=c&eUwkW-%^hAp@E zp)oKhx(qB>Kyj3USlfw<)oGyr3iaOtF4=@HzzKW(y?35NCZ#es^6@rakxNC@nXt%N?N9cR!cM+9`R$(5c*|zs zmvU|#KJYQ8A&b^1Efv~&KkjBi$DJ3CU>p?7caj@uWwIjv$yX0JUU*xZyuZR`9G`i{ zA9;tDiTc(05B@Jj+AOM0z6PrT7=HK=o_SYi?`;e{Z&^Rq=wLm?mn=~=M)ri))>0$J za*cP%*v)O49zP8Ao0sbXleqviM*1ckFaQK)H@J?`PoP%LG>APBL>i@&R06W4cm<>I z=OWFW^+>MZZi|n1GmoPWo~#r9%YZtZER4zuW$^m2^<$YywZe4`97>A&c*gIefE@s$ z=4HY+_Ds{~#RA=5)SJO_;D_g76$Pz_>+es%ndq6GHZn_iz>9ni5lSeT!fWGkgYqAw z!rwxiox~F|&W$IG5aaBWG(`EC=)}G=&#<8jjt>^~xnA~ZyI+a> zb(HE)xz&D=Vdp77AgvzIrsr541M}F!zEt18FJ8_&YVWX6s=Ka62B3%d>f8y&>ZnZ5 zJ#h@YIK1vC_p6lTi><4mSi&%lx{z?gC33CJ3+Otgg2NyVYN>sV>`+IAs-9W1s_x^0 zwSiM5FGm{6KjiaV&uP;PP)b=zm`IRsqOYL@&EM+%Xomr4Q>Es2-*lI(i>s~Jm9|GL z344`VemVrurv1u=dh20%+{c=10T>)>Y zhO~yhn81&u8d;P+HZE=*2fgAyycfd3;LGHCD*S~k(lX%#_2R@j!gE6JKgmk77N