feat: open and import QQ Music playlists
CI / go (push) Canceled after 0s
CI / web (push) Canceled after 0s
CI / images (Dockerfile, server, alisten) (push) Canceled after 0s
CI / images (web/Dockerfile, web, ) (push) Canceled after 0s
CI / images (worker.Dockerfile, worker, alisten-worker) (push) Canceled after 0s
CI / helm-release (push) Canceled after 0s
CI / deploy (push) Canceled after 0s
CI / go (push) Canceled after 0s
CI / web (push) Canceled after 0s
CI / images (Dockerfile, server, alisten) (push) Canceled after 0s
CI / images (web/Dockerfile, web, ) (push) Canceled after 0s
CI / images (worker.Dockerfile, worker, alisten-worker) (push) Canceled after 0s
CI / helm-release (push) Canceled after 0s
CI / deploy (push) Canceled after 0s
This commit is contained in:
1 parent
19a135c983
commit
6f286c2eb2
23 files changed
+150
-81
No files matched your search
@@ -32,7 +32,9 @@ describe('parseRoute', () => {
|
||||
['/r/my-room/search?q=%E6%99%B4%E5%A4%A9&source=netease&view=playlists&page=2&panel=chat', { name: 'search', ref: 'my-room', panel: 'chat', query: { view: 'playlists', keyword: '晴天', source: 'netease', page: 2 } }],
|
||||
['/r/my-room/search?source=netease&view=tracks', { name: 'search', ref: 'my-room', panel: null, query: { view: 'tracks', keyword: '', source: 'netease', page: 1 } }],
|
||||
['/r/my-room/search?view=recommend', { name: 'search', ref: 'my-room', panel: null, query: { view: 'recommend' } }],
|
||||
['/r/my-room/search?q=a&source=qq&view=playlists', { name: 'search', ref: 'my-room', panel: null, query: { view: 'playlists', keyword: 'a', source: 'qq', page: 1 } }],
|
||||
['/r/my-room/playlists/netease/123?page=3&panel=queue', { name: 'playlist', ref: 'my-room', panel: 'queue', source: 'netease', id: '123', page: 3 }],
|
||||
['/r/my-room/playlists/qq/123', { name: 'playlist', ref: 'my-room', panel: null, source: 'qq', id: '123', page: 1 }],
|
||||
['/s/AbC_-1x', { name: 'shared-playlist', shareId: 'AbC_-1x', panel: null }],
|
||||
['/s/AbC_-1x?panel=queue', { name: 'shared-playlist', shareId: 'AbC_-1x', panel: 'queue' }],
|
||||
])('%s', (url, route) => {
|
||||
@@ -50,7 +52,6 @@ describe('parseRoute', () => {
|
||||
['/r/my-room?panel=lyrics', '/r/my-room'],
|
||||
['/r/my-room/search?q=%20a%20', '/r/my-room/search?q=a&source=qq'],
|
||||
['/r/my-room/search?q=a&source=kuwo&view=lyrics&page=0', '/r/my-room/search?q=a&source=qq'],
|
||||
['/r/my-room/search?q=a&source=qq&view=playlists', '/r/my-room/search?q=a&source=qq'],
|
||||
['/r/my-room/search?q=a&page=2', '/r/my-room/search?q=a&source=qq'],
|
||||
['/r/my-room/search?view=tracks&page=2', '/r/my-room/search?view=tracks'],
|
||||
['/r/my-room/search?view=recommend&q=a&source=netease&page=2', '/r/my-room/search?view=recommend'],
|
||||
@@ -64,7 +65,7 @@ describe('parseRoute', () => {
|
||||
expect(route && formatRoute(route)).toBe(canonical)
|
||||
})
|
||||
|
||||
it.each(['/r/', '/r/my-room/listen', '/r/my-room/queue/extra', '/settings/profile', '/settings/', '/settings/account/extra', '/admin/', '/admin/overview', '/r/my-room/playlists/qq/123', '/r/my-room/playlists/netease', '/s/', '/s/abc/def'])('%s 不对应任何页面', (url) => {
|
||||
it.each(['/r/', '/r/my-room/listen', '/r/my-room/queue/extra', '/settings/profile', '/settings/', '/settings/account/extra', '/admin/', '/admin/overview', '/r/my-room/playlists/bilibili/123', '/r/my-room/playlists/netease', '/s/', '/s/abc/def'])('%s 不对应任何页面', (url) => {
|
||||
expect(parse(url)).toBeNull()
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { AdminSection } from '@/features/admin'
|
||||
import type { SearchSource } from '@/features/history'
|
||||
import type { SearchQuery } from '@/features/search'
|
||||
import type { SettingsSection } from '@/features/settings'
|
||||
import { canOpenPlaylists, defaultSearchSource, keywordSearch } from '@/features/search'
|
||||
import { defaultSearchSource, keywordSearch } from '@/features/search'
|
||||
|
||||
export type RoomPage = 'listen' | 'queue' | 'chat'
|
||||
export type Panel = 'queue' | 'history' | 'chat'
|
||||
@@ -144,7 +144,7 @@ export function parseRoute({ pathname, search, hash }: Pick<Location, 'pathname'
|
||||
}
|
||||
const playlist = /^\/playlists\/([^/]+)\/([^/]+)$/.exec(subpath)
|
||||
const source = parseSource(playlist?.[1] ?? null)
|
||||
if (!playlist || !source || !canOpenPlaylists(source))
|
||||
if (!playlist || !source)
|
||||
return null
|
||||
return { name: 'playlist', ref, panel, source, id: playlist[2], page: parsePage(params) }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protocolgen from protocol/idl.json; DO NOT EDIT.
|
||||
|
||||
export const PROTOCOL_VERSION = 'd68525cd68ef691f'
|
||||
export const PROTOCOL_VERSION = '42749e16714e0d7c'
|
||||
|
||||
export interface APIKey {
|
||||
key: string
|
||||
|
||||
@@ -95,7 +95,7 @@ function ImportLinkDialog({ onClose, onImported }: { onClose: () => void, onImpo
|
||||
>
|
||||
<label>
|
||||
歌单链接
|
||||
<input data-autofocus required placeholder="粘贴网易云音乐的歌单链接或分享文字" value={link} onChange={event => setLink(event.target.value)} />
|
||||
<input data-autofocus required placeholder="粘贴网易云音乐或 QQ 音乐的歌单链接、分享文字" value={link} onChange={event => setLink(event.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
歌单名
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { lazyPage } from '@/shared/ui'
|
||||
|
||||
export { HeaderSearch } from './header-search'
|
||||
export { canOpenPlaylists, defaultSearchSource, keywordSearch, searchStore } from './search-store'
|
||||
export { defaultSearchSource, keywordSearch, searchStore } from './search-store'
|
||||
export type { SearchQuery } from './search-store'
|
||||
export const SearchPage = lazyPage(() => import('./search-page').then(module => module.SearchPage))
|
||||
export const PlaylistPage = lazyPage(() => import('./search-page').then(module => module.PlaylistPage))
|
||||
@@ -11,7 +11,7 @@ import { bookTitle } from '@/shared/lib/book-title'
|
||||
import { useStore } from '@/shared/lib/use-store'
|
||||
import { Artwork, AsyncIconButton, EmptyState, IconButton, PlayingBars, Select, Skeleton, SongArtwork, SongList } from '@/shared/ui'
|
||||
import { SearchBox } from './search-box'
|
||||
import { canOpenPlaylists, keywordSearch, pageSize, recordSearch, searchStore } from './search-store'
|
||||
import { keywordSearch, pageSize, recordSearch, searchStore } from './search-store'
|
||||
import './search.css'
|
||||
|
||||
const categories: { view: SearchCategory, label: string }[] = [
|
||||
@@ -278,7 +278,7 @@ export function SearchPage({ inlineSearchClassName, onSearch, onOpenPlaylist }:
|
||||
<div className="search-filter-bar">
|
||||
<SearchBox className={`search-inline ${inlineSearchClassName}`} recent={false} onSearch={onSearch} />
|
||||
<div className="search-filter-chips" role="group" aria-label="搜索类别">
|
||||
{categories.filter(category => category.view !== 'playlists' || canOpenPlaylists(source)).map(category => (
|
||||
{categories.map(category => (
|
||||
<button
|
||||
type="button"
|
||||
key={category.view}
|
||||
|
||||
@@ -255,10 +255,10 @@ describe('搜索请求', () => {
|
||||
expect(notificationStore.state.notifications).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('只有网易云搜歌单:其他来源的全部类别只搜歌曲', async () => {
|
||||
it('搜 QQ 音乐的全部类别时,也同时搜歌曲和歌单', async () => {
|
||||
const { searchStore, keywordSearch } = await load()
|
||||
searchStore.show(keywordSearch('a', 'qq', 'all'))
|
||||
expect(calls.map(({ name }) => name)).toEqual(['searchTracks'])
|
||||
expect(calls.map(({ name }) => name)).toEqual(['searchTracks', 'searchPlaylists'])
|
||||
})
|
||||
|
||||
it('关键词太长时提示长度上限', async () => {
|
||||
|
||||
@@ -58,13 +58,8 @@ interface SearchState extends Results {
|
||||
|
||||
const noResults: Results = { tracks: [], tracksTotal: 0, playlists: [], playlistsTotal: 0, recommendations: [] }
|
||||
|
||||
// 只有网易云能取歌单内的歌曲,其他平台的歌单搜到了也打不开,所以不搜。
|
||||
export function canOpenPlaylists(source: Source): boolean {
|
||||
return source === 'netease'
|
||||
}
|
||||
|
||||
export function keywordSearch(keyword: string, source: SearchSource, category: SearchCategory, page = 1): KeywordQuery {
|
||||
return { view: category === 'playlists' && !canOpenPlaylists(source) ? 'all' : category, keyword, source, page }
|
||||
return { view: category, keyword, source, page }
|
||||
}
|
||||
|
||||
// 用户发起的搜索记入搜索历史,包括提交、点最近搜索、换来源和类别。翻页、推荐、打开歌单,以及前进、后退还原的搜索都不记。
|
||||
@@ -223,7 +218,7 @@ class SearchStore extends Store<SearchState> {
|
||||
const tracks = (at: number) => requests.searchTracks({ source, keyword, page: at, pageSize }).then(({ items, total }) => ({ tracks: items, tracksTotal: total }))
|
||||
const playlists = (at: number) => requests.searchPlaylists({ source, keyword, page: at, pageSize }).then(({ items, total }) => ({ playlists: items, playlistsTotal: total }))
|
||||
if (view === 'all')
|
||||
return canOpenPlaylists(source) ? [tracks(1), playlists(1)] : [tracks(1)]
|
||||
return [tracks(1), playlists(1)]
|
||||
return [view === 'tracks' ? tracks(page) : playlists(page)]
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user