@ozdao/martyrs 0.2.470 → 0.2.472
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Media-CR0V1zvB.js → Media-DW8RLbfM.js} +1 -1
- package/dist/{Media-C4Ges_Sd.mjs → Media-y_TX6us_.mjs} +1 -1
- package/dist/_virtual/index.cjs +1 -1
- package/dist/_virtual/index.js +1 -1
- package/dist/auth.server.js +1 -1
- package/dist/auth.server.mjs +1 -1
- package/dist/chats.server.js +1 -1
- package/dist/chats.server.mjs +1 -1
- package/dist/community.server.js +1 -1
- package/dist/community.server.mjs +1 -1
- package/dist/events.server.js +1 -1
- package/dist/events.server.mjs +1 -1
- package/dist/files.server.js +1 -1
- package/dist/files.server.mjs +1 -1
- package/dist/gallery.server.js +1 -1
- package/dist/gallery.server.mjs +1 -1
- package/dist/{index-DQqZReAr.js → index-CVXl1rB5.js} +0 -1
- package/dist/{index-DICZTQ-1.mjs → index-Df8vtZx7.mjs} +0 -1
- package/dist/{main-CsZAG5Wz.js → main-CCfQH-Dd.js} +2 -2
- package/dist/{main-CTcal9qN.mjs → main-CgmHzhq5.mjs} +74 -74
- package/dist/{node_modules/.pnpm/@vue_server-renderer@3.5.13_vue@3.5.13_typescript@5.8.3_ → martyrs}/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.cjs +1 -1
- package/dist/martyrs/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.cjs.map +1 -0
- package/dist/{node_modules/.pnpm/@vue_server-renderer@3.5.13_vue@3.5.13_typescript@5.8.3_ → martyrs}/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.js +1 -1
- package/dist/martyrs/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.js.map +1 -0
- package/dist/martyrs/node_modules/@vue/shared/dist/shared.esm-bundler.cjs.map +1 -0
- package/dist/martyrs/node_modules/@vue/shared/dist/shared.esm-bundler.js.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/regex.cjs +5 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/regex.cjs.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/regex.js +5 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/regex.js.map +1 -0
- package/dist/{node_modules/.pnpm/uuid@11.1.0 → martyrs}/node_modules/uuid/dist/esm-browser/rng.cjs +4 -4
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/rng.cjs.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/rng.js +15 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/rng.js.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/stringify.cjs +17 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/stringify.cjs.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/stringify.js +17 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/stringify.js.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/v4.cjs +13 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/v4.cjs.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/v4.js +13 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/v4.js.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/validate.cjs +8 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/validate.cjs.map +1 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/validate.js +8 -0
- package/dist/martyrs/node_modules/uuid/dist/esm-browser/validate.js.map +1 -0
- package/dist/martyrs/src/components/Feed/Feed.vue.cjs +2 -2
- package/dist/martyrs/src/components/Feed/Feed.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/Feed/Feed.vue.js +2 -2
- package/dist/martyrs/src/components/Feed/Feed.vue.js.map +1 -1
- package/dist/martyrs/src/components/Menu/{Menu.vue.cjs → Menu.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Menu/{Menu.vue.js → Menu.vue2.js} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +1 -0
- package/dist/martyrs/src/components/Skeleton/Skeleton.vue.cjs +5 -5
- package/dist/martyrs/src/components/Skeleton/Skeleton.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/Skeleton/Skeleton.vue.js +5 -5
- package/dist/martyrs/src/components/Skeleton/Skeleton.vue.js.map +1 -1
- package/dist/martyrs/src/components/Tab/{Tab.vue2.cjs → Tab.vue.cjs} +2 -2
- package/dist/martyrs/src/components/Tab/{Tab.vue2.js.map → Tab.vue.cjs.map} +1 -1
- package/dist/martyrs/src/components/Tab/{Tab.vue2.js → Tab.vue.js} +2 -2
- package/dist/martyrs/src/components/Tab/Tab.vue.js.map +1 -0
- package/dist/martyrs/src/components/Tree/Tree.vue.cjs +3 -1
- package/dist/martyrs/src/components/Tree/Tree.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/Tree/Tree.vue.js +3 -1
- package/dist/martyrs/src/components/Tree/Tree.vue.js.map +1 -1
- package/dist/martyrs/src/modules/auth/views/components/layouts/Auth.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/layouts/Auth.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/store/auth.cjs +1 -0
- package/dist/martyrs/src/modules/auth/views/store/auth.cjs.map +1 -1
- package/dist/martyrs/src/modules/auth/views/store/auth.js +1 -0
- package/dist/martyrs/src/modules/auth/views/store/auth.js.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.cjs +1 -1
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.store.cjs +107 -21
- package/dist/martyrs/src/modules/globals/views/classes/globals.store.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.store.js +107 -21
- package/dist/martyrs/src/modules/globals/views/classes/globals.store.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.cjs +10 -14
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.js +12 -16
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/SectionPageTitle.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/SectionPageTitle.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/utils/vue-app-renderer.cjs +7 -13
- package/dist/martyrs/src/modules/globals/views/utils/vue-app-renderer.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/utils/vue-app-renderer.js +7 -13
- package/dist/martyrs/src/modules/globals/views/utils/vue-app-renderer.js.map +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.cjs +6 -7
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.js +7 -8
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.js.map +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.cjs +11 -5
- package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.js +12 -6
- package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.js.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Categories.vue.cjs +34 -13
- package/dist/martyrs/src/modules/products/components/pages/Categories.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Categories.vue.js +35 -14
- package/dist/martyrs/src/modules/products/components/pages/Categories.vue.js.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +20 -9
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +21 -10
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Leftovers.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Leftovers.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.cjs +2 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +2 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Products.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Products.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/GanttChart.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/GanttChart.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +1 -1
- package/dist/martyrs.cjs.js +1 -1
- package/dist/martyrs.css +1 -1
- package/dist/martyrs.es.js +1 -1
- package/dist/notifications.server.js +35 -2
- package/dist/notifications.server.mjs +35 -2
- package/dist/orders.server.js +3 -3
- package/dist/orders.server.mjs +3 -3
- package/dist/organizations.server.js +1 -1
- package/dist/organizations.server.mjs +1 -1
- package/dist/products.server.js +78 -53
- package/dist/products.server.mjs +78 -53
- package/dist/rents.server.js +1 -1
- package/dist/rents.server.mjs +1 -1
- package/dist/style.css +37 -38
- package/dist/wallet.server.js +1 -1
- package/dist/wallet.server.mjs +1 -1
- package/package.json +2 -1
- package/src/components/Feed/Feed.vue +2 -7
- package/src/components/Skeleton/Skeleton.vue +4 -5
- package/src/components/Tree/Tree.vue +5 -2
- package/src/modules/auth/controllers/middlewares/authJwt.js +1 -1
- package/src/modules/auth/views/store/auth.js +2 -1
- package/src/modules/globals/controllers/classes/globals.crud.js +2 -2
- package/src/modules/globals/views/classes/globals.store.js +119 -31
- package/src/modules/globals/views/components/partials/Navigation.vue +11 -10
- package/src/modules/globals/views/utils/vue-app-renderer.js +7 -16
- package/src/modules/icons/entities/IconMusic.vue +14 -0
- package/src/modules/icons/navigation/IconCheck.vue +31 -0
- package/src/modules/icons/navigation/IconHeart.vue +26 -0
- package/src/modules/icons/navigation/IconPause.vue +31 -0
- package/src/modules/icons/navigation/IconPlay.vue +17 -0
- package/src/modules/icons/navigation/IconRefresh.vue +31 -0
- package/src/modules/icons/navigation/IconShuffle.vue +31 -0
- package/src/modules/icons/navigation/IconVolume.vue +31 -0
- package/src/modules/music/components/SidebarMusic.vue +156 -0
- package/src/modules/music/components/cards/AlbumCard.vue +107 -0
- package/src/modules/music/components/cards/ArtistCard.vue +37 -0
- package/src/modules/music/components/cards/PlaylistCard.vue +100 -0
- package/src/modules/music/components/cards/TrackCard.vue +86 -0
- package/src/modules/music/components/forms/PlaylistForm.vue +156 -0
- package/src/modules/music/components/forms/SearchForm.vue +82 -0
- package/src/modules/music/components/forms/UploadForm.vue +313 -0
- package/src/modules/music/components/layouts/MusicLayout.vue +137 -0
- package/src/modules/music/components/lists/AlbumList.vue +25 -0
- package/src/modules/music/components/lists/ArtistList.vue +25 -0
- package/src/modules/music/components/lists/PlaylistList.vue +25 -0
- package/src/modules/music/components/lists/TrackList.vue +175 -0
- package/src/modules/music/components/pages/AlbumDetail.vue +265 -0
- package/src/modules/music/components/pages/ArtistDetail.vue +247 -0
- package/src/modules/music/components/pages/MusicHome.vue +177 -0
- package/src/modules/music/components/pages/MusicLibrary.vue +192 -0
- package/src/modules/music/components/pages/MusicUpload.vue +44 -0
- package/src/modules/music/components/pages/PlaylistDetail.vue +504 -0
- package/src/modules/music/components/pages/SearchResults.vue +397 -0
- package/src/modules/music/components/pages/TrackDetail.vue +143 -0
- package/src/modules/music/components/player/MusicPlayer.vue +202 -0
- package/src/modules/music/components/player/TrackProgress.vue +110 -0
- package/src/modules/music/components/player/VolumeControl.vue +98 -0
- package/src/modules/music/controllers/album.controller.js +98 -0
- package/src/modules/music/controllers/artist.controller.js +111 -0
- package/src/modules/music/controllers/genre.controller.js +71 -0
- package/src/modules/music/controllers/music.controller.js +174 -0
- package/src/modules/music/controllers/playlist.controller.js +182 -0
- package/src/modules/music/controllers/search.controller.js +103 -0
- package/src/modules/music/controllers/stream.controller.js +106 -0
- package/src/modules/music/models/album.model.js +61 -0
- package/src/modules/music/models/artist.model.js +67 -0
- package/src/modules/music/models/genre.model.js +42 -0
- package/src/modules/music/models/play-history.model.js +51 -0
- package/src/modules/music/models/playlist.model.js +69 -0
- package/src/modules/music/models/track.model.js +94 -0
- package/src/modules/music/music.client.js +186 -0
- package/src/modules/music/music.server.js +114 -0
- package/src/modules/music/policies/music.policies.js +84 -0
- package/src/modules/music/router/music.js +77 -0
- package/src/modules/music/routes/album.routes.js +62 -0
- package/src/modules/music/routes/artist.routes.js +67 -0
- package/src/modules/music/routes/genre.routes.js +60 -0
- package/src/modules/music/routes/music.routes.js +145 -0
- package/src/modules/music/routes/playlist.routes.js +99 -0
- package/src/modules/music/routes/search.routes.js +10 -0
- package/src/modules/music/routes/stream.routes.js +38 -0
- package/src/modules/music/store/albums.js +200 -0
- package/src/modules/music/store/artists.js +180 -0
- package/src/modules/music/store/player.js +397 -0
- package/src/modules/music/store/playlists.js +211 -0
- package/src/modules/music/store/search.js +126 -0
- package/src/modules/music/store/tracks.js +230 -0
- package/src/modules/music/websocket/streaming.handler.js +151 -0
- package/src/modules/notifications/controllers/notifications.controller.js +44 -1
- package/src/modules/notifications/notifications.server.js +0 -1
- package/src/modules/notifications/routes/notifications.routes.js +3 -0
- package/src/modules/orders/components/pages/OrderCreate.vue +0 -2
- package/src/modules/products/components/blocks/CardCategory.vue +5 -4
- package/src/modules/products/components/pages/Categories.vue +38 -20
- package/src/modules/products/components/pages/CategoryEdit.vue +16 -7
- package/src/modules/products/components/pages/ProductEdit.vue +1 -0
- package/src/modules/products/controllers/categories.controller.js +155 -103
- package/dist/martyrs/src/components/Menu/Menu.vue.cjs.map +0 -1
- package/dist/martyrs/src/components/Menu/Menu.vue.js.map +0 -1
- package/dist/martyrs/src/components/Tab/Tab.vue2.cjs.map +0 -1
- package/dist/node_modules/.pnpm/@vue_server-renderer@3.5.13_vue@3.5.13_typescript@5.8.3_/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.cjs.map +0 -1
- package/dist/node_modules/.pnpm/@vue_server-renderer@3.5.13_vue@3.5.13_typescript@5.8.3_/node_modules/@vue/server-renderer/dist/server-renderer.esm-bundler.js.map +0 -1
- package/dist/node_modules/.pnpm/@vue_shared@3.5.13/node_modules/@vue/shared/dist/shared.esm-bundler.cjs.map +0 -1
- package/dist/node_modules/.pnpm/@vue_shared@3.5.13/node_modules/@vue/shared/dist/shared.esm-bundler.js.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/native.cjs +0 -6
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/native.cjs.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/native.js +0 -6
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/native.js.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/rng.cjs.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/rng.js +0 -15
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/rng.js.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.cjs +0 -11
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.cjs.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.js +0 -11
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.js.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/v4.cjs +0 -21
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/v4.cjs.map +0 -1
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/v4.js +0 -21
- package/dist/node_modules/.pnpm/uuid@11.1.0/node_modules/uuid/dist/esm-browser/v4.js.map +0 -1
- /package/dist/{node_modules/.pnpm/@vue_shared@3.5.13 → martyrs}/node_modules/@vue/shared/dist/shared.esm-bundler.cjs +0 -0
- /package/dist/{node_modules/.pnpm/@vue_shared@3.5.13 → martyrs}/node_modules/@vue/shared/dist/shared.esm-bundler.js +0 -0
- /package/dist/{node_modules → martyrs/node_modules}/fast-deep-equal/index.cjs +0 -0
- /package/dist/{node_modules → martyrs/node_modules}/fast-deep-equal/index.cjs.map +0 -0
- /package/dist/{node_modules → martyrs/node_modules}/fast-deep-equal/index.js +0 -0
- /package/dist/{node_modules → martyrs/node_modules}/fast-deep-equal/index.js.map +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<!-- components/player/MusicPlayer.vue -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="music-player pd-small flex-between flex-v-center flex bg-dark br-t br-solid br-dark-transp-20 t-white">
|
|
4
|
+
<!-- Current Track Info -->
|
|
5
|
+
<div class="player-track-info flex-v-center flex w-25r">
|
|
6
|
+
<div v-if="currentTrack" class="player-track-cover mn-r-small">
|
|
7
|
+
<Media
|
|
8
|
+
:url="currentTrack.coverUrl || (currentTrack.album && currentTrack.album.coverUrl) || '/assets/placeholder-track.jpg'"
|
|
9
|
+
class="w-3r h-3r object-fit-cover radius-small"
|
|
10
|
+
/>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="player-track-details t-truncate">
|
|
14
|
+
<div class="player-track-title t-white t-medium t-truncate">
|
|
15
|
+
{{ currentTrack?.title || 'No track playing' }}
|
|
16
|
+
</div>
|
|
17
|
+
<div class="player-track-artist t-grey t-small t-truncate">
|
|
18
|
+
{{ getArtistName(currentTrack) }}
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="player-favorite mn-l-small">
|
|
23
|
+
<Button
|
|
24
|
+
@click="toggleFavorite"
|
|
25
|
+
class="bg-transparent border-none pd-zero"
|
|
26
|
+
:showLoader="false"
|
|
27
|
+
:showSucces="false"
|
|
28
|
+
>
|
|
29
|
+
<IconHeart class="i-small" :fill="isFavorite ? 'rgb(var(--main))' : 'rgb(var(--grey))'"/>
|
|
30
|
+
</Button>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Player Controls -->
|
|
35
|
+
<div class="player-controls flex-1 flex flex-column items-center">
|
|
36
|
+
<div class="player-buttons flex-v-center flex gap-small">
|
|
37
|
+
<Button
|
|
38
|
+
@click="toggleShuffle"
|
|
39
|
+
class="bg-transparent border-none pd-zero"
|
|
40
|
+
:showLoader="false"
|
|
41
|
+
:showSucces="false"
|
|
42
|
+
>
|
|
43
|
+
<IconShuffle class="i-small" :fill="shuffle ? 'rgb(var(--main))' : 'rgb(var(--grey))'"/>
|
|
44
|
+
</Button>
|
|
45
|
+
|
|
46
|
+
<Button
|
|
47
|
+
@click="playPrevious"
|
|
48
|
+
class="bg-transparent border-none pd-zero"
|
|
49
|
+
:showLoader="false"
|
|
50
|
+
:showSucces="false"
|
|
51
|
+
>
|
|
52
|
+
<IconPrevious class="i-small" fill="rgb(var(--white))"/>
|
|
53
|
+
</Button>
|
|
54
|
+
|
|
55
|
+
<Button
|
|
56
|
+
@click="togglePlay"
|
|
57
|
+
class="play-pause-btn bg-white radius-round pd-micro flex-center flex"
|
|
58
|
+
:showLoader="false"
|
|
59
|
+
:showSucces="false"
|
|
60
|
+
>
|
|
61
|
+
<IconPause v-if="isPlaying" class="i-medium" fill="rgb(var(--black))"/>
|
|
62
|
+
<IconPlay v-else class="i-medium" fill="rgb(var(--black))"/>
|
|
63
|
+
</Button>
|
|
64
|
+
|
|
65
|
+
<Button
|
|
66
|
+
@click="playNext"
|
|
67
|
+
class="bg-transparent border-none pd-zero"
|
|
68
|
+
:showLoader="false"
|
|
69
|
+
:showSucces="false"
|
|
70
|
+
>
|
|
71
|
+
<IconNext class="i-small" fill="rgb(var(--white))"/>
|
|
72
|
+
</Button>
|
|
73
|
+
|
|
74
|
+
<Button
|
|
75
|
+
@click="toggleRepeat"
|
|
76
|
+
class="bg-transparent border-none pd-zero"
|
|
77
|
+
:showLoader="false"
|
|
78
|
+
:showSucces="false"
|
|
79
|
+
>
|
|
80
|
+
<IconRepeat class="i-small" :fill="repeatIcon"/>
|
|
81
|
+
</Button>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<TrackProgress class="w-100 mn-t-thin" />
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- Volume Control -->
|
|
88
|
+
<div class="player-volume flex-v-center flex w-15r">
|
|
89
|
+
<Button
|
|
90
|
+
@click="toggleMute"
|
|
91
|
+
class="bg-transparent border-none pd-zero mn-r-small"
|
|
92
|
+
:showLoader="false"
|
|
93
|
+
:showSucces="false"
|
|
94
|
+
>
|
|
95
|
+
<IconVolume v-if="!muted && volume > 0.5" class="i-small" fill="rgb(var(--white))"/>
|
|
96
|
+
<IconVolumeHalf v-else-if="!muted && volume > 0" class="i-small" fill="rgb(var(--white))"/>
|
|
97
|
+
<IconVolumeMute v-else class="i-small" fill="rgb(var(--white))"/>
|
|
98
|
+
</Button>
|
|
99
|
+
|
|
100
|
+
<VolumeControl />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</template>
|
|
104
|
+
|
|
105
|
+
<script setup>
|
|
106
|
+
import { computed, ref } from 'vue';
|
|
107
|
+
import Media from '@martyrs/src/components/Media/Media.vue';
|
|
108
|
+
import Button from '@martyrs/src/components/Button/Button.vue';
|
|
109
|
+
import TrackProgress from './TrackProgress.vue';
|
|
110
|
+
import VolumeControl from './VolumeControl.vue';
|
|
111
|
+
|
|
112
|
+
// Import icons
|
|
113
|
+
import IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';
|
|
114
|
+
import IconPause from '@martyrs/src/modules/icons/navigation/IconPause.vue';
|
|
115
|
+
import IconNext from '@martyrs/src/modules/icons/navigation/IconChevronRight.vue';
|
|
116
|
+
import IconPrevious from '@martyrs/src/modules/icons/navigation/IconChevronLeft.vue';
|
|
117
|
+
import IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';
|
|
118
|
+
import IconRepeat from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';
|
|
119
|
+
import IconHeart from '@martyrs/src/modules/icons/navigation/IconHeart.vue';
|
|
120
|
+
import IconVolume from '@martyrs/src/modules/icons/navigation/IconVolume.vue';
|
|
121
|
+
// import IconVolumeHalf from '@martyrs/src/modules/icons/navigation/IconVolume.vue'; // Using same icon but we'd style it differently
|
|
122
|
+
import IconVolumeMute from '@martyrs/src/modules/icons/navigation/IconMute.vue';
|
|
123
|
+
|
|
124
|
+
// Import player store
|
|
125
|
+
import { state as playerState, actions as playerActions } from '../../store/player.js';
|
|
126
|
+
|
|
127
|
+
// State
|
|
128
|
+
const isFavorite = ref(false);
|
|
129
|
+
|
|
130
|
+
// Computed properties
|
|
131
|
+
const currentTrack = computed(() => playerState.currentTrack);
|
|
132
|
+
const isPlaying = computed(() => playerState.isPlaying);
|
|
133
|
+
const volume = computed(() => playerState.volume);
|
|
134
|
+
const muted = computed(() => playerState.muted);
|
|
135
|
+
const shuffle = computed(() => playerState.shuffle);
|
|
136
|
+
const repeat = computed(() => playerState.repeat);
|
|
137
|
+
|
|
138
|
+
const repeatIcon = computed(() => {
|
|
139
|
+
switch(repeat.value) {
|
|
140
|
+
case 'one':
|
|
141
|
+
return 'rgb(var(--main))';
|
|
142
|
+
case 'all':
|
|
143
|
+
return 'rgb(var(--main))';
|
|
144
|
+
default:
|
|
145
|
+
return 'rgb(var(--grey))';
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Methods
|
|
150
|
+
const togglePlay = () => {
|
|
151
|
+
playerActions.togglePlay();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const playNext = () => {
|
|
155
|
+
playerActions.playNext();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const playPrevious = () => {
|
|
159
|
+
playerActions.playPrevious();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const toggleShuffle = () => {
|
|
163
|
+
playerActions.toggleShuffle();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const toggleRepeat = () => {
|
|
167
|
+
playerActions.toggleRepeat();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const toggleMute = () => {
|
|
171
|
+
playerActions.toggleMute();
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const toggleFavorite = () => {
|
|
175
|
+
isFavorite.value = !isFavorite.value;
|
|
176
|
+
// Implement favorite track logic here
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const getArtistName = (track) => {
|
|
180
|
+
if (!track) return 'Unknown Artist';
|
|
181
|
+
|
|
182
|
+
if (track.artist) {
|
|
183
|
+
if (typeof track.artist === 'object') {
|
|
184
|
+
return track.artist.name || 'Unknown Artist';
|
|
185
|
+
}
|
|
186
|
+
return track.artist;
|
|
187
|
+
}
|
|
188
|
+
return 'Unknown Artist';
|
|
189
|
+
};
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<style scoped>
|
|
193
|
+
.play-pause-btn {
|
|
194
|
+
width: 32px;
|
|
195
|
+
height: 32px;
|
|
196
|
+
transition: transform 0.2s ease;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.play-pause-btn:hover {
|
|
200
|
+
transform: scale(1.1);
|
|
201
|
+
}
|
|
202
|
+
</style>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!-- components/player/TrackProgress.vue -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="track-progress flex flex-v-center gap-small">
|
|
4
|
+
<div class="current-time t-grey t-small">{{ formatTime(currentTime) }}</div>
|
|
5
|
+
|
|
6
|
+
<div
|
|
7
|
+
ref="progressBarContainer"
|
|
8
|
+
class="progress-bar-container flex-1 h-thin bg-grey radius-extra pos-relative cursor-pointer"
|
|
9
|
+
@click="seek"
|
|
10
|
+
@mousedown="startSeek"
|
|
11
|
+
@mousemove="updateSeekPosition"
|
|
12
|
+
@mouseup="endSeek"
|
|
13
|
+
@mouseleave="endSeek"
|
|
14
|
+
>
|
|
15
|
+
<div
|
|
16
|
+
class="progress-bar h-100 bg-white radius-extra"
|
|
17
|
+
:style="{ width: progressPercentage + '%' }"
|
|
18
|
+
></div>
|
|
19
|
+
<div
|
|
20
|
+
class="progress-handle w-thin h-thin bg-white radius-round pos-absolute pos-t-50 pos-l-0"
|
|
21
|
+
:style="{ left: `calc(${progressPercentage}% - 4px)`, transform: 'translateY(-50%)' }"
|
|
22
|
+
></div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="total-time t-grey t-small">{{ formatTime(duration) }}</div>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup>
|
|
30
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
|
31
|
+
|
|
32
|
+
// Import player store
|
|
33
|
+
import { state as playerState, actions as playerActions } from '../../store/player.js';
|
|
34
|
+
|
|
35
|
+
// Refs
|
|
36
|
+
const progressBarContainer = ref(null);
|
|
37
|
+
const isSeeking = ref(false);
|
|
38
|
+
const seekPosition = ref(0);
|
|
39
|
+
|
|
40
|
+
// Computed properties
|
|
41
|
+
const currentTime = computed(() => playerState.currentTime);
|
|
42
|
+
const duration = computed(() => playerState.duration);
|
|
43
|
+
const progressPercentage = computed(() => {
|
|
44
|
+
if (!duration.value) return 0;
|
|
45
|
+
return Math.min(100, (currentTime.value / duration.value) * 100);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Methods
|
|
49
|
+
const formatTime = (seconds) => {
|
|
50
|
+
if (!seconds) return '0:00';
|
|
51
|
+
|
|
52
|
+
const minutes = Math.floor(seconds / 60);
|
|
53
|
+
const remainingSeconds = Math.floor(seconds % 60);
|
|
54
|
+
|
|
55
|
+
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const calculateSeekTime = (event) => {
|
|
59
|
+
if (!progressBarContainer.value || !duration.value) return 0;
|
|
60
|
+
|
|
61
|
+
const rect = progressBarContainer.value.getBoundingClientRect();
|
|
62
|
+
const offsetX = event.clientX - rect.left;
|
|
63
|
+
const percentage = Math.max(0, Math.min(1, offsetX / rect.width));
|
|
64
|
+
|
|
65
|
+
return percentage * duration.value;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const seek = (event) => {
|
|
69
|
+
const seekTime = calculateSeekTime(event);
|
|
70
|
+
playerActions.seek(seekTime);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const startSeek = (event) => {
|
|
74
|
+
isSeeking.value = true;
|
|
75
|
+
seekPosition.value = calculateSeekTime(event);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const updateSeekPosition = (event) => {
|
|
79
|
+
if (isSeeking.value) {
|
|
80
|
+
seekPosition.value = calculateSeekTime(event);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const endSeek = () => {
|
|
85
|
+
if (isSeeking.value) {
|
|
86
|
+
isSeeking.value = false;
|
|
87
|
+
playerActions.seek(seekPosition.value);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Clean up event listeners when component is unmounted
|
|
92
|
+
onUnmounted(() => {
|
|
93
|
+
isSeeking.value = false;
|
|
94
|
+
});
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<style scoped>
|
|
98
|
+
.progress-bar-container:hover .progress-bar {
|
|
99
|
+
background-color: rgb(var(--main));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.progress-handle {
|
|
103
|
+
display: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.progress-bar-container:hover .progress-handle {
|
|
107
|
+
display: block;
|
|
108
|
+
transform: translateY(-50%) scale(1.3);
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!-- components/player/VolumeControl.vue -->
|
|
2
|
+
<template>
|
|
3
|
+
<div
|
|
4
|
+
ref="volumeBarContainer"
|
|
5
|
+
class="volume-bar-container w-100 h-thin bg-grey-transp-50 radius-extra pos-relative cursor-pointer"
|
|
6
|
+
@click="setVolume"
|
|
7
|
+
@mousedown="startVolumeChange"
|
|
8
|
+
@mousemove="updateVolumePosition"
|
|
9
|
+
@mouseup="endVolumeChange"
|
|
10
|
+
@mouseleave="endVolumeChange"
|
|
11
|
+
>
|
|
12
|
+
<div
|
|
13
|
+
class="volume-bar h-100 bg-white radius-extra"
|
|
14
|
+
:style="{ width: volumePercentage + '%' }"
|
|
15
|
+
:class="{ 'muted': muted }"
|
|
16
|
+
></div>
|
|
17
|
+
<div
|
|
18
|
+
class="volume-handle w-thin h-thin bg-white radius-round pos-absolute pos-t-50 pos-l-0"
|
|
19
|
+
:style="{
|
|
20
|
+
left: `calc(${volumePercentage}% - 4px)`,
|
|
21
|
+
transform: 'translateY(-50%)',
|
|
22
|
+
opacity: muted ? 0 : 1
|
|
23
|
+
}"
|
|
24
|
+
></div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup>
|
|
29
|
+
import { ref, computed } from 'vue';
|
|
30
|
+
|
|
31
|
+
// Import player store
|
|
32
|
+
import { state as playerState, actions as playerActions } from '../../store/player.js';
|
|
33
|
+
|
|
34
|
+
// Refs
|
|
35
|
+
const volumeBarContainer = ref(null);
|
|
36
|
+
const isChangingVolume = ref(false);
|
|
37
|
+
|
|
38
|
+
// Computed properties
|
|
39
|
+
const volume = computed(() => playerState.volume);
|
|
40
|
+
const muted = computed(() => playerState.muted);
|
|
41
|
+
const volumePercentage = computed(() => {
|
|
42
|
+
if (muted.value) return 0;
|
|
43
|
+
return volume.value * 100;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Methods
|
|
47
|
+
const calculateVolume = (event) => {
|
|
48
|
+
if (!volumeBarContainer.value) return 0;
|
|
49
|
+
|
|
50
|
+
const rect = volumeBarContainer.value.getBoundingClientRect();
|
|
51
|
+
const offsetX = event.clientX - rect.left;
|
|
52
|
+
|
|
53
|
+
return Math.max(0, Math.min(1, offsetX / rect.width));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const setVolume = (event) => {
|
|
57
|
+
const newVolume = calculateVolume(event);
|
|
58
|
+
playerActions.setVolume(newVolume);
|
|
59
|
+
|
|
60
|
+
// Unmute if was muted
|
|
61
|
+
if (muted.value && newVolume > 0) {
|
|
62
|
+
playerActions.toggleMute();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const startVolumeChange = (event) => {
|
|
67
|
+
isChangingVolume.value = true;
|
|
68
|
+
setVolume(event);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const updateVolumePosition = (event) => {
|
|
72
|
+
if (isChangingVolume.value) {
|
|
73
|
+
setVolume(event);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const endVolumeChange = () => {
|
|
78
|
+
isChangingVolume.value = false;
|
|
79
|
+
};
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<style scoped>
|
|
83
|
+
.volume-bar-container:hover .volume-bar:not(.muted) {
|
|
84
|
+
background-color: rgb(var(--main));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.volume-handle {
|
|
88
|
+
display: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.volume-bar-container:hover .volume-handle {
|
|
92
|
+
display: block;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.muted {
|
|
96
|
+
opacity: 0.5;
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// controllers/album.controller.js
|
|
2
|
+
const globalsQuery = require('@martyrs/src/modules/globals/controllers/utils/queryProcessor');
|
|
3
|
+
|
|
4
|
+
module.exports = function(app,db) {
|
|
5
|
+
const { MusicController } = require('./music.controller')(app,db);
|
|
6
|
+
|
|
7
|
+
class AlbumController extends MusicController {
|
|
8
|
+
constructor(app) {
|
|
9
|
+
super('/api/albums', app, db, db.album);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Get tracks for a specific album
|
|
13
|
+
async getAlbumTracks(req, res) {
|
|
14
|
+
try {
|
|
15
|
+
const albumId = req.params.albumId;
|
|
16
|
+
|
|
17
|
+
// Check if album exists and user has access
|
|
18
|
+
const album = await db.album.findById(albumId);
|
|
19
|
+
if (!album) {
|
|
20
|
+
return res.status(404).json({ error: 'Album not found' });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check access using ABAC
|
|
24
|
+
const accessResult = await this.abac.checkAccess({
|
|
25
|
+
user: req.userId,
|
|
26
|
+
resource: 'albums',
|
|
27
|
+
action: 'read',
|
|
28
|
+
currentResource: album
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!accessResult.allowed && !album.isPublic) {
|
|
32
|
+
return res.status(403).json({
|
|
33
|
+
errorCode: accessResult.reason,
|
|
34
|
+
message: 'Access Denied'
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Find tracks for this album
|
|
39
|
+
const tracks = await db.track.find({ album: albumId, status: 'published' })
|
|
40
|
+
.sort({ releaseDate: -1 });
|
|
41
|
+
|
|
42
|
+
res.json(tracks);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.logger.error(`Error getting album tracks: ${error.message}`);
|
|
45
|
+
res.status(500).json({ error: error.message });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get featured albums
|
|
50
|
+
async getFeaturedAlbums(req, res) {
|
|
51
|
+
try {
|
|
52
|
+
const cacheKey = 'featured-albums';
|
|
53
|
+
let featuredAlbums = await this.cache.get(cacheKey);
|
|
54
|
+
|
|
55
|
+
if (!featuredAlbums) {
|
|
56
|
+
// Get featured albums with track counts
|
|
57
|
+
featuredAlbums = await db.album.aggregate([
|
|
58
|
+
{ $match: { status: 'featured', isPublic: true } },
|
|
59
|
+
{
|
|
60
|
+
$lookup: {
|
|
61
|
+
from: 'tracks',
|
|
62
|
+
localField: '_id',
|
|
63
|
+
foreignField: 'album',
|
|
64
|
+
as: 'tracks'
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
$addFields: {
|
|
69
|
+
trackCount: { $size: '$tracks' }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{ $project: { tracks: 0 } },
|
|
73
|
+
{ $sort: { releaseDate: -1 } },
|
|
74
|
+
{ $limit: 10 }
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
await this.cache.set(cacheKey, featuredAlbums, 3600); // Cache for 1 hour
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
res.json(featuredAlbums);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
this.logger.error(`Error getting featured albums: ${error.message}`);
|
|
83
|
+
res.status(500).json({ error: error.message });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const albumController = new AlbumController(app,db); // Pass app in actual implementation
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
create: albumController.create.bind(albumController),
|
|
92
|
+
read: albumController.read.bind(albumController),
|
|
93
|
+
update: albumController.update.bind(albumController),
|
|
94
|
+
delete: albumController.delete.bind(albumController),
|
|
95
|
+
getAlbumTracks: albumController.getAlbumTracks.bind(albumController),
|
|
96
|
+
getFeaturedAlbums: albumController.getFeaturedAlbums.bind(albumController)
|
|
97
|
+
};
|
|
98
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// controllers/artist.controller.js (continued)
|
|
2
|
+
module.exports = function(app,db) {
|
|
3
|
+
const { MusicController } = require('./music.controller')(app,db);
|
|
4
|
+
|
|
5
|
+
class ArtistController extends MusicController {
|
|
6
|
+
constructor(app) {
|
|
7
|
+
super('/api/artists', app, db, db.artist);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Get artist discography
|
|
11
|
+
async getDiscography(req, res) {
|
|
12
|
+
try {
|
|
13
|
+
const artistId = req.params.artistId;
|
|
14
|
+
|
|
15
|
+
// Verify artist exists
|
|
16
|
+
const artist = await db.artist.findById(artistId);
|
|
17
|
+
if (!artist) {
|
|
18
|
+
return res.status(404).json({ error: 'Artist not found' });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Get all albums by this artist
|
|
22
|
+
const albums = await db.album.find({
|
|
23
|
+
artist: artistId,
|
|
24
|
+
status: 'published',
|
|
25
|
+
isPublic: true
|
|
26
|
+
}).sort({ releaseDate: -1 });
|
|
27
|
+
|
|
28
|
+
// Get all tracks not in albums
|
|
29
|
+
const singleTracks = await db.track.find({
|
|
30
|
+
artist: artistId,
|
|
31
|
+
album: { $exists: false },
|
|
32
|
+
status: 'published',
|
|
33
|
+
isPublic: true
|
|
34
|
+
}).sort({ releaseDate: -1 });
|
|
35
|
+
|
|
36
|
+
res.json({
|
|
37
|
+
artist,
|
|
38
|
+
albums,
|
|
39
|
+
singles: singleTracks
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
this.logger.error(`Error getting artist discography: ${error.message}`);
|
|
43
|
+
res.status(500).json({ error: error.message });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Verify an artist (admin only)
|
|
48
|
+
async verifyArtist(req, res) {
|
|
49
|
+
try {
|
|
50
|
+
const artistId = req.params.artistId;
|
|
51
|
+
|
|
52
|
+
// Check if user is admin
|
|
53
|
+
if (!req.userRoles.includes('ROLE_ADMIN')) {
|
|
54
|
+
return res.status(403).json({ error: 'Only administrators can verify artists' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const artist = await db.artist.findById(artistId);
|
|
58
|
+
if (!artist) {
|
|
59
|
+
return res.status(404).json({ error: 'Artist not found' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
artist.isVerified = true;
|
|
63
|
+
await artist.save();
|
|
64
|
+
|
|
65
|
+
await this.cache.flush();
|
|
66
|
+
|
|
67
|
+
res.json({ success: true, artist });
|
|
68
|
+
} catch (error) {
|
|
69
|
+
this.logger.error(`Error verifying artist: ${error.message}`);
|
|
70
|
+
res.status(500).json({ error: error.message });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get related artists
|
|
75
|
+
async getRelatedArtists(req, res) {
|
|
76
|
+
try {
|
|
77
|
+
const artistId = req.params.artistId;
|
|
78
|
+
|
|
79
|
+
// Find genres of this artist
|
|
80
|
+
const artist = await db.artist.findById(artistId);
|
|
81
|
+
if (!artist) {
|
|
82
|
+
return res.status(404).json({ error: 'Artist not found' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Get artists with similar genres
|
|
86
|
+
const relatedArtists = await db.artist.find({
|
|
87
|
+
_id: { $ne: artistId },
|
|
88
|
+
genre: { $in: artist.genre },
|
|
89
|
+
status: 'published'
|
|
90
|
+
}).limit(5);
|
|
91
|
+
|
|
92
|
+
res.json(relatedArtists);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
this.logger.error(`Error getting related artists: ${error.message}`);
|
|
95
|
+
res.status(500).json({ error: error.message });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const artistController = new ArtistController(app,db);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
create: artistController.create.bind(artistController),
|
|
104
|
+
read: artistController.read.bind(artistController),
|
|
105
|
+
update: artistController.update.bind(artistController),
|
|
106
|
+
delete: artistController.delete.bind(artistController),
|
|
107
|
+
getDiscography: artistController.getDiscography.bind(artistController),
|
|
108
|
+
verifyArtist: artistController.verifyArtist.bind(artistController),
|
|
109
|
+
getRelatedArtists: artistController.getRelatedArtists.bind(artistController)
|
|
110
|
+
};
|
|
111
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// controllers/genre.controller.js
|
|
2
|
+
module.exports = function(app,db) {
|
|
3
|
+
const { MusicController } = require('./music.controller')(app,db);
|
|
4
|
+
|
|
5
|
+
class GenreController extends MusicController {
|
|
6
|
+
constructor(app) {
|
|
7
|
+
super('/api/genres', app, db, db.genre);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Get tracks by genre
|
|
11
|
+
async getGenreTracks(req, res) {
|
|
12
|
+
try {
|
|
13
|
+
const genreId = req.params.genreId;
|
|
14
|
+
|
|
15
|
+
// Verify genre exists
|
|
16
|
+
const genre = await db.genre.findById(genreId);
|
|
17
|
+
if (!genre) {
|
|
18
|
+
return res.status(404).json({ error: 'Genre not found' });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Get tracks from this genre
|
|
22
|
+
const tracks = await db.track.find({
|
|
23
|
+
genre: genreId,
|
|
24
|
+
status: 'published',
|
|
25
|
+
isPublic: true
|
|
26
|
+
})
|
|
27
|
+
.sort({ releaseDate: -1 })
|
|
28
|
+
.limit(50)
|
|
29
|
+
.populate('artist', 'name')
|
|
30
|
+
.populate('album', 'title coverUrl');
|
|
31
|
+
|
|
32
|
+
res.json({
|
|
33
|
+
genre,
|
|
34
|
+
tracks
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
this.logger.error(`Error getting genre tracks: ${error.message}`);
|
|
38
|
+
res.status(500).json({ error: error.message });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Get popular genres
|
|
43
|
+
async getPopularGenres(req, res) {
|
|
44
|
+
try {
|
|
45
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
46
|
+
|
|
47
|
+
const genres = await db.genre.find({
|
|
48
|
+
status: 'published'
|
|
49
|
+
})
|
|
50
|
+
.sort({ popularity: -1 })
|
|
51
|
+
.limit(limit);
|
|
52
|
+
|
|
53
|
+
res.json(genres);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
this.logger.error(`Error getting popular genres: ${error.message}`);
|
|
56
|
+
res.status(500).json({ error: error.message });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const genreController = new GenreController(app,db);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
create: genreController.create.bind(genreController),
|
|
65
|
+
read: genreController.read.bind(genreController),
|
|
66
|
+
update: genreController.update.bind(genreController),
|
|
67
|
+
delete: genreController.delete.bind(genreController),
|
|
68
|
+
getGenreTracks: genreController.getGenreTracks.bind(genreController),
|
|
69
|
+
getPopularGenres: genreController.getPopularGenres.bind(genreController)
|
|
70
|
+
};
|
|
71
|
+
};
|