dsh-music-panel 0.3.3 → 0.3.4

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/client.js +76 -41
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.4
4
+
5
+ ### 变更
6
+
7
+ - 随机播放时,播放队列(播放列表)按实际播放顺序(乱序)展示,从上往下播放;切回顺序播放恢复原顺序
8
+ - 切换到随机播放时重新洗牌
9
+ - 双击歌曲 / 播放全部时,入队范围是当前歌单的完整曲目(忽略搜索过滤),从双击的歌曲开始
10
+
3
11
  ## 0.3.3
4
12
 
5
13
  ### 变更
package/client.js CHANGED
@@ -1609,6 +1609,10 @@ window.__ModuleLoader__.load({
1609
1609
  getTime: function () {
1610
1610
  return { currentTime: state.currentTime, duration: state.duration, buffered: state.buffered }
1611
1611
  },
1612
+ /** Queue indices in the current playback order (random mode). */
1613
+ getPlayOrder: function () {
1614
+ return randomOrder.slice()
1615
+ },
1612
1616
  /** Restore the last queue/position once (called by the resident host). */
1613
1617
  tryRestore: tryRestore,
1614
1618
  /** Flush statistics + resume state when the page is hidden/closed. */
@@ -1823,8 +1827,14 @@ window.__ModuleLoader__.load({
1823
1827
  emit()
1824
1828
  },
1825
1829
  setOrder: function (value) {
1826
- state = Object.assign({}, state, { order: value })
1827
- savePrefs({ order: value })
1830
+ var next = value === 'random' ? 'random' : 'list'
1831
+ if (next === 'random' && state.queue.length > 1) {
1832
+ // Fresh shuffle whenever random is selected, so the queue panel's
1833
+ // displayed order is the actual playback order.
1834
+ randomOrder = randomOrderOf(state.queue.length)
1835
+ }
1836
+ state = Object.assign({}, state, { order: next })
1837
+ savePrefs({ order: next })
1828
1838
  emit()
1829
1839
  },
1830
1840
  clear: function () {
@@ -3068,6 +3078,18 @@ window.__ModuleLoader__.load({
3068
3078
  var prefs = useRef(readPrefs()).current
3069
3079
  var [snap, setSnap] = useState(player.getSnapshot())
3070
3080
  var leaderNow = player.isLeader()
3081
+ var playOrder = player.getPlayOrder()
3082
+ // In random mode the queue panel lists songs in the order they will
3083
+ // actually play ("from top to bottom"), while edits still target the
3084
+ // original queue indices.
3085
+ var queueView =
3086
+ snap.order === 'random' && Array.isArray(playOrder) && playOrder.length === snap.queue.length
3087
+ ? playOrder.map(function (originalIndex) {
3088
+ return { track: snap.queue[originalIndex], originalIndex: originalIndex }
3089
+ })
3090
+ : snap.queue.map(function (track, originalIndex) {
3091
+ return { track: track, originalIndex: originalIndex }
3092
+ })
3071
3093
  var [config, setConfig] = useState(null)
3072
3094
  var [tracks, setTracks] = useState([])
3073
3095
  var [loading, setLoading] = useState(true)
@@ -3393,11 +3415,12 @@ window.__ModuleLoader__.load({
3393
3415
  }
3394
3416
 
3395
3417
  function activate(track) {
3396
- var at = visible.findIndex(function (item) {
3418
+ var at = sortedCollection.findIndex(function (item) {
3397
3419
  return item.path === track.path
3398
3420
  })
3399
3421
  if (at < 0) return
3400
- player.select(visible, at)
3422
+ // Queue the whole playlist (ignores the search filter), starting here.
3423
+ player.playList(sortedCollection, at)
3401
3424
  }
3402
3425
 
3403
3426
  function clearList() {
@@ -3521,6 +3544,39 @@ window.__ModuleLoader__.load({
3521
3544
  [deleteConfirm],
3522
3545
  )
3523
3546
 
3547
+ // Full collection in display order (search is a filter on top of it).
3548
+ // Double-clicking a song queues this whole playlist, not just the
3549
+ // filtered results, which is what desktop players do.
3550
+ var sortedCollection = useMemo(
3551
+ function () {
3552
+ // "Liked" is ordered by when it was liked (newest first) unless the
3553
+ // user picks another sort, matching NetEase/Spotify liked songs.
3554
+ if (view === 'favorites' && sortBy === 'title' && sortDir === 'asc') {
3555
+ return collectionTracks
3556
+ }
3557
+ var direction = sortDir === 'desc' ? -1 : 1
3558
+ var keyed = collectionTracks.map(function (track, position) {
3559
+ return { track: track, position: position }
3560
+ })
3561
+ keyed.sort(function (left, right) {
3562
+ var a = left.track
3563
+ var b = right.track
3564
+ var aValue = sortValueOf(a, sortBy)
3565
+ var bValue = sortValueOf(b, sortBy)
3566
+ var compared =
3567
+ typeof aValue === 'number' && typeof bValue === 'number'
3568
+ ? aValue - bValue
3569
+ : String(aValue).localeCompare(String(bValue), LANG)
3570
+ if (compared === 0) compared = left.position - right.position
3571
+ return compared * direction
3572
+ })
3573
+ return keyed.map(function (entry) {
3574
+ return entry.track
3575
+ })
3576
+ },
3577
+ [collectionTracks, sortBy, sortDir, view],
3578
+ )
3579
+
3524
3580
  var visible = useMemo(
3525
3581
  function () {
3526
3582
  var needles = query
@@ -3528,8 +3584,8 @@ window.__ModuleLoader__.load({
3528
3584
  .toLowerCase()
3529
3585
  .split(/\s+/)
3530
3586
  .filter(Boolean)
3531
- var list = collectionTracks.filter(function (track) {
3532
- if (needles.length === 0) return true
3587
+ if (needles.length === 0) return sortedCollection
3588
+ return sortedCollection.filter(function (track) {
3533
3589
  // Search across metadata, folder/path and pinyin (full + initials),
3534
3590
  // the way desktop players let you type "zhoujielun" / "zjl".
3535
3591
  var haystack = [
@@ -3550,32 +3606,8 @@ window.__ModuleLoader__.load({
3550
3606
  return haystack.indexOf(needle) >= 0
3551
3607
  })
3552
3608
  })
3553
- // "Liked" is ordered by when it was liked (newest first) unless the
3554
- // user picks another sort, matching NetEase/Spotify liked songs.
3555
- if (view === 'favorites' && sortBy === 'title' && sortDir === 'asc') {
3556
- return list
3557
- }
3558
- var direction = sortDir === 'desc' ? -1 : 1
3559
- var keyed = list.map(function (track, position) {
3560
- return { track: track, position: position }
3561
- })
3562
- keyed.sort(function (left, right) {
3563
- var a = left.track
3564
- var b = right.track
3565
- var aValue = sortValueOf(a, sortBy)
3566
- var bValue = sortValueOf(b, sortBy)
3567
- var compared =
3568
- typeof aValue === 'number' && typeof bValue === 'number'
3569
- ? aValue - bValue
3570
- : String(aValue).localeCompare(String(bValue), LANG)
3571
- if (compared === 0) compared = left.position - right.position
3572
- return compared * direction
3573
- })
3574
- return keyed.map(function (entry) {
3575
- return entry.track
3576
- })
3577
3609
  },
3578
- [collectionTracks, query, sortBy, sortDir, view],
3610
+ [sortedCollection, query],
3579
3611
  )
3580
3612
 
3581
3613
  var selectedSet = useMemo(function () {
@@ -4512,7 +4544,7 @@ window.__ModuleLoader__.load({
4512
4544
  type: 'button',
4513
4545
  className: 'mu-chip',
4514
4546
  onClick: function () {
4515
- if (visible.length > 0) player.playList(visible, 0)
4547
+ if (sortedCollection.length > 0) player.playList(sortedCollection, 0)
4516
4548
  },
4517
4549
  },
4518
4550
  icon('play', 13),
@@ -4826,20 +4858,23 @@ window.__ModuleLoader__.load({
4826
4858
  { className: 'mu-pop-body' },
4827
4859
  snap.queue.length === 0
4828
4860
  ? h('div', { className: 'mu-state-sub' }, t('page.queueEmpty'))
4829
- : snap.queue.map(function (track, position) {
4861
+ : queueView.map(function (entry, position) {
4862
+ var track = entry.track
4863
+ var originalIndex = entry.originalIndex
4864
+ var visibleCurrent = originalIndex === snap.index
4830
4865
  return h(
4831
4866
  'div',
4832
4867
  {
4833
4868
  key: position + ':' + track.path,
4834
- className: 'mu-queue-row' + (position === snap.index ? ' is-current' : ''),
4869
+ className: 'mu-queue-row' + (visibleCurrent ? ' is-current' : ''),
4835
4870
  onClick: function () {
4836
- player.select(snap.queue, position)
4871
+ player.select(snap.queue, originalIndex)
4837
4872
  },
4838
4873
  },
4839
4874
  h(
4840
4875
  'span',
4841
4876
  { className: 'mu-queue-index' },
4842
- position === snap.index && snap.playing ? '▶' : String(position + 1),
4877
+ visibleCurrent && snap.playing ? '▶' : String(position + 1),
4843
4878
  ),
4844
4879
  h('span', { className: 'mu-ellipsis' }, track.title),
4845
4880
  h('span', { className: 'mu-queue-artist mu-ellipsis' }, track.artist || ''),
@@ -4852,10 +4887,10 @@ window.__ModuleLoader__.load({
4852
4887
  type: 'button',
4853
4888
  className: 'mu-queue-btn',
4854
4889
  title: t('page.moveUp'),
4855
- disabled: position === 0,
4890
+ disabled: originalIndex === 0,
4856
4891
  onClick: function (event) {
4857
4892
  event.stopPropagation()
4858
- player.moveQueue(position, position - 1)
4893
+ player.moveQueue(originalIndex, originalIndex - 1)
4859
4894
  },
4860
4895
  },
4861
4896
  '↑',
@@ -4866,10 +4901,10 @@ window.__ModuleLoader__.load({
4866
4901
  type: 'button',
4867
4902
  className: 'mu-queue-btn',
4868
4903
  title: t('page.moveDown'),
4869
- disabled: position === snap.queue.length - 1,
4904
+ disabled: originalIndex === snap.queue.length - 1,
4870
4905
  onClick: function (event) {
4871
4906
  event.stopPropagation()
4872
- player.moveQueue(position, position + 1)
4907
+ player.moveQueue(originalIndex, originalIndex + 1)
4873
4908
  },
4874
4909
  },
4875
4910
  '↓',
@@ -4882,7 +4917,7 @@ window.__ModuleLoader__.load({
4882
4917
  title: t('page.removeTrack'),
4883
4918
  onClick: function (event) {
4884
4919
  event.stopPropagation()
4885
- player.removeQueueAt(position)
4920
+ player.removeQueueAt(originalIndex)
4886
4921
  },
4887
4922
  },
4888
4923
  '×',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-music-panel",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "description": "Local music library for DeepSeek Harness: scan a folder, browse tracks with cover art and tags, and play through a sidebar-panel player with loop, shuffle and volume. · DSH 本地音乐插件:扫描音乐目录、读取封面与标签、侧栏入口 + 全屏播放器。",
6
6
  "license": "MIT",