|
@@ -36,10 +36,13 @@ import { getNavidromeVideoItems, setNavidromePlaylist } from '../common/util/Nav
|
|
|
import {
|
|
import {
|
|
|
buildPreferredRemotePlaybackPool,
|
|
buildPreferredRemotePlaybackPool,
|
|
|
buildSortedDiscoverySongs,
|
|
buildSortedDiscoverySongs,
|
|
|
|
|
+ findPreferredRemotePlaybackAccountId,
|
|
|
FindCollectionSortType,
|
|
FindCollectionSortType,
|
|
|
- resolveQueueStartIndex
|
|
|
|
|
|
|
+ resolveQueueStartIndex,
|
|
|
|
|
+ shouldWaitForIndexedRemotePlayback
|
|
|
} from '../common/util/FindDiscoveryHelper'
|
|
} from '../common/util/FindDiscoveryHelper'
|
|
|
import { insertSongToNextPlayQueue, QueueInsertStatus } from '../common/util/PlayQueueHelper'
|
|
import { insertSongToNextPlayQueue, QueueInsertStatus } from '../common/util/PlayQueueHelper'
|
|
|
|
|
+import { resolveRemoteCoverForSong } from '../common/util/RemoteCoverResolver'
|
|
|
|
|
|
|
|
@Builder
|
|
@Builder
|
|
|
export function FindViewBuilder() {
|
|
export function FindViewBuilder() {
|
|
@@ -50,6 +53,7 @@ const TAG = 'FindView'
|
|
|
const SWIPER_MAX_COUNT = 5
|
|
const SWIPER_MAX_COUNT = 5
|
|
|
const HOT_SECTION_COUNT = 10
|
|
const HOT_SECTION_COUNT = 10
|
|
|
const REMOTE_POOL_COUNT = 32
|
|
const REMOTE_POOL_COUNT = 32
|
|
|
|
|
+const REMOTE_VISIBLE_COVER_RESOLVE_LIMIT = 24
|
|
|
const CLOUD_SECTION_COUNT = 18
|
|
const CLOUD_SECTION_COUNT = 18
|
|
|
const RECENT_POOL_COUNT = 24
|
|
const RECENT_POOL_COUNT = 24
|
|
|
const RECENT_SECTION_COUNT = 18
|
|
const RECENT_SECTION_COUNT = 18
|
|
@@ -213,6 +217,9 @@ export struct FindView {
|
|
|
private playlistSongsCache: Map<string, VideoItem[]> = new Map<string, VideoItem[]>()
|
|
private playlistSongsCache: Map<string, VideoItem[]> = new Map<string, VideoItem[]>()
|
|
|
private heartPlaylistCoverTicket: number = 0
|
|
private heartPlaylistCoverTicket: number = 0
|
|
|
private deleteComponentId: number = 0
|
|
private deleteComponentId: number = 0
|
|
|
|
|
+ private remoteAccountMap: Map<string, WebDavAccount> = new Map<string, WebDavAccount>()
|
|
|
|
|
+ private remoteCoverRepairTicket: number = 0
|
|
|
|
|
+ private remoteCoverRepairTimer: number = -1
|
|
|
|
|
|
|
|
aboutToAppear(): void {
|
|
aboutToAppear(): void {
|
|
|
this.initSetting()
|
|
this.initSetting()
|
|
@@ -231,6 +238,7 @@ export struct FindView {
|
|
|
aboutToDisappear(): void {
|
|
aboutToDisappear(): void {
|
|
|
AppStorage.setOrCreate('findCanBack', false)
|
|
AppStorage.setOrCreate('findCanBack', false)
|
|
|
emitter.off(EventConstants.EVENT_FIND_VIEW_BACK)
|
|
emitter.off(EventConstants.EVENT_FIND_VIEW_BACK)
|
|
|
|
|
+ this.clearPendingRemoteCoverRepair()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
initSetting(){
|
|
initSetting(){
|
|
@@ -372,7 +380,10 @@ export struct FindView {
|
|
|
this.refreshText = ''
|
|
this.refreshText = ''
|
|
|
this.heartPlaylistCoverTicket += 1
|
|
this.heartPlaylistCoverTicket += 1
|
|
|
void this.resolveHeartPlaylistCovers(this.heartPlaylistCoverTicket, playlists)
|
|
void this.resolveHeartPlaylistCovers(this.heartPlaylistCoverTicket, playlists)
|
|
|
- if (uniqueRemoteSongs.length === 0) {
|
|
|
|
|
|
|
+ if (uniqueRemoteSongs.length > 0) {
|
|
|
|
|
+ void this.prepareRemoteAccounts()
|
|
|
|
|
+ this.scheduleVisibleRemoteCoverRepair(false, 1200)
|
|
|
|
|
+ } else {
|
|
|
void this.bootstrapRemoteDiscoverySongsIfNeeded()
|
|
void this.bootstrapRemoteDiscoverySongsIfNeeded()
|
|
|
}
|
|
}
|
|
|
Logger.info(
|
|
Logger.info(
|
|
@@ -438,6 +449,9 @@ export struct FindView {
|
|
|
this.cloudMoodSongs = this.pickPreferredSongs(this.remoteSongsPool, HOT_SECTION_COUNT)
|
|
this.cloudMoodSongs = this.pickPreferredSongs(this.remoteSongsPool, HOT_SECTION_COUNT)
|
|
|
this.remoteSongs = this.pickPreferredSongs(this.remoteSongsPool, CLOUD_SECTION_COUNT)
|
|
this.remoteSongs = this.pickPreferredSongs(this.remoteSongsPool, CLOUD_SECTION_COUNT)
|
|
|
this.cloudAlbums = this.pickAlbumGroups(this.cloudAlbumsPool, CLOUD_ALBUM_COUNT)
|
|
this.cloudAlbums = this.pickAlbumGroups(this.cloudAlbumsPool, CLOUD_ALBUM_COUNT)
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('remoteSongsPool', this.remoteSongsPool)
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('漫步云端', this.remoteSongs)
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('云卷云舒', this.cloudMoodSongs)
|
|
|
if (refreshSections) {
|
|
if (refreshSections) {
|
|
|
this.cloudSectionPageIndex = 0
|
|
this.cloudSectionPageIndex = 0
|
|
|
this.cloudAlbumPageIndex = 0
|
|
this.cloudAlbumPageIndex = 0
|
|
@@ -446,6 +460,134 @@ export struct FindView {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private cacheRemoteAccounts(accounts: WebDavAccount[]): void {
|
|
|
|
|
+ this.remoteAccountMap.clear()
|
|
|
|
|
+ for (let i = 0; i < accounts.length; i++) {
|
|
|
|
|
+ const account = accounts[i]
|
|
|
|
|
+ if (account.id !== undefined && account.id !== null) {
|
|
|
|
|
+ this.remoteAccountMap.set(account.id.toString(), account)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async prepareRemoteAccounts(): Promise<WebDavAccount[]> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const context = getContext(this) as common.Context
|
|
|
|
|
+ this.remoteDriveManager.setContext(context)
|
|
|
|
|
+ await this.remoteDriveManager.createWebDavTableInDB()
|
|
|
|
|
+ await this.remoteDriveManager.queryWebDavAccountsFromDB()
|
|
|
|
|
+ const accounts: WebDavAccount[] = this.remoteDriveManager.getAllWebDavAccounts()
|
|
|
|
|
+ .filter((account: WebDavAccount) => account.id !== undefined && account.id !== null && account.id > 0)
|
|
|
|
|
+ this.cacheRemoteAccounts(accounts)
|
|
|
|
|
+ return accounts
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.warn(TAG, `发现页加载远程账号失败: ${this.toErrorMessage(error)}`)
|
|
|
|
|
+ this.remoteAccountMap.clear()
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async ensureRemoteAccountMapReady(): Promise<Map<string, WebDavAccount>> {
|
|
|
|
|
+ if (this.remoteAccountMap.size > 0) {
|
|
|
|
|
+ return this.remoteAccountMap
|
|
|
|
|
+ }
|
|
|
|
|
+ await this.prepareRemoteAccounts()
|
|
|
|
|
+ return this.remoteAccountMap
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private collectVisibleRemoteCoverTargets(): VideoItem[] {
|
|
|
|
|
+ const targets: VideoItem[] = []
|
|
|
|
|
+ targets.push(...this.remoteSongs)
|
|
|
|
|
+ targets.push(...this.cloudMoodSongs)
|
|
|
|
|
+ for (let index = 0; index < this.cloudAlbums.length; index += 1) {
|
|
|
|
|
+ const group = this.cloudAlbums[index]
|
|
|
|
|
+ if (group.songs.length > 0) {
|
|
|
|
|
+ targets.push(group.songs[0])
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return this.filterUniqueSongs(targets).slice(0, REMOTE_VISIBLE_COVER_RESOLVE_LIMIT)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private refreshVisibleRemoteAlbumCovers(): void {
|
|
|
|
|
+ for (let index = 0; index < this.cloudAlbumsPool.length; index += 1) {
|
|
|
|
|
+ const coverSong = this.pickAlbumCoverSong(this.cloudAlbumsPool[index].songs)
|
|
|
|
|
+ this.cloudAlbumsPool[index].coverPath = coverSong?.pixelMapPath ?? ''
|
|
|
|
|
+ }
|
|
|
|
|
+ for (let index = 0; index < this.cloudAlbums.length; index += 1) {
|
|
|
|
|
+ const coverSong = this.pickAlbumCoverSong(this.cloudAlbums[index].songs)
|
|
|
|
|
+ this.cloudAlbums[index].coverPath = coverSong?.pixelMapPath ?? ''
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private clearPendingRemoteCoverRepair(): void {
|
|
|
|
|
+ if (this.remoteCoverRepairTimer >= 0) {
|
|
|
|
|
+ clearTimeout(this.remoteCoverRepairTimer)
|
|
|
|
|
+ this.remoteCoverRepairTimer = -1
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private scheduleVisibleRemoteCoverRepair(persistResolvedCover: boolean = false, delayMs: number = 800): void {
|
|
|
|
|
+ this.clearPendingRemoteCoverRepair()
|
|
|
|
|
+ const ticket = ++this.remoteCoverRepairTicket
|
|
|
|
|
+ this.remoteCoverRepairTimer = setTimeout(() => {
|
|
|
|
|
+ this.remoteCoverRepairTimer = -1
|
|
|
|
|
+ const coverTargets = this.collectVisibleRemoteCoverTargets()
|
|
|
|
|
+ if (coverTargets.length === 0) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ Logger.info(TAG,
|
|
|
|
|
+ `[cover-debug] scheduleVisibleRemoteCoverRepair ticket=${ticket}, count=${coverTargets.length}, ` +
|
|
|
|
|
+ `persist=${persistResolvedCover}, delayMs=${delayMs}`)
|
|
|
|
|
+ void this.resolveRemoteSongCovers(coverTargets, persistResolvedCover)
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ if (ticket !== this.remoteCoverRepairTicket) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.refreshVisibleRemoteAlbumCovers()
|
|
|
|
|
+ this.remoteSongs = this.remoteSongs.slice()
|
|
|
|
|
+ this.cloudMoodSongs = this.cloudMoodSongs.slice()
|
|
|
|
|
+ this.cloudAlbums = this.cloudAlbums.slice()
|
|
|
|
|
+ this.cloudSectionPages = this.cloudSectionPages.slice()
|
|
|
|
|
+ this.cloudAlbumPages = this.cloudAlbumPages.slice()
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('remoteSongsPool', this.remoteSongsPool)
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('漫步云端', this.remoteSongs)
|
|
|
|
|
+ this.logRemoteDiscoveryCoverState('云卷云舒', this.cloudMoodSongs)
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((error: Object) => {
|
|
|
|
|
+ Logger.warn(TAG, `发现页后台修正远程封面失败: ${this.toErrorMessage(error)}`)
|
|
|
|
|
+ })
|
|
|
|
|
+ }, delayMs)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async resolveRemoteSongCovers(items: VideoItem[], persistResolvedCover: boolean): Promise<VideoItem[]> {
|
|
|
|
|
+ if (!items || items.length === 0) {
|
|
|
|
|
+ return items
|
|
|
|
|
+ }
|
|
|
|
|
+ const accountMap = await this.ensureRemoteAccountMapReady()
|
|
|
|
|
+ const tasks: Promise<VideoItem>[] = items.map(async (item: VideoItem): Promise<VideoItem> => {
|
|
|
|
|
+ const accountId = item.webdav_account_id ?? ''
|
|
|
|
|
+ const account = accountMap.get(accountId)
|
|
|
|
|
+ if (!account) {
|
|
|
|
|
+ return item
|
|
|
|
|
+ }
|
|
|
|
|
+ const resolvedCover = await resolveRemoteCoverForSong(item, account)
|
|
|
|
|
+ if (StrUtil.isEmpty(resolvedCover) || item.pixelMapPath === resolvedCover) {
|
|
|
|
|
+ return item
|
|
|
|
|
+ }
|
|
|
|
|
+ Logger.info(
|
|
|
|
|
+ TAG,
|
|
|
|
|
+ `[cover-debug] repairRemoteSongCover title=${this.getSongTitle(item)}, type=${item.type}, acc=${accountId}, ` +
|
|
|
|
|
+ `raw=${this.sanitizeCoverValue(item.pixelMapPath)}, resolved=${this.sanitizeCoverValue(resolvedCover)}`
|
|
|
|
|
+ )
|
|
|
|
|
+ item.pixelMapPath = resolvedCover
|
|
|
|
|
+ if (persistResolvedCover && this.mediaTable) {
|
|
|
|
|
+ await this.mediaTable.saveOrUpdateWebDavItem(cloneVideoItem(item))
|
|
|
|
|
+ }
|
|
|
|
|
+ return item
|
|
|
|
|
+ })
|
|
|
|
|
+ return await Promise.all(tasks)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private async ensureRemoteDiscoverySongsAvailable(): Promise<void> {
|
|
private async ensureRemoteDiscoverySongsAvailable(): Promise<void> {
|
|
|
if (!this.mediaTable) {
|
|
if (!this.mediaTable) {
|
|
|
return
|
|
return
|
|
@@ -461,12 +603,13 @@ export struct FindView {
|
|
|
)
|
|
)
|
|
|
if (remoteSongs.length > 0) {
|
|
if (remoteSongs.length > 0) {
|
|
|
this.applyRemoteDiscoverySongs(remoteSongs, true)
|
|
this.applyRemoteDiscoverySongs(remoteSongs, true)
|
|
|
|
|
+ this.scheduleVisibleRemoteCoverRepair()
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
await this.bootstrapRemoteDiscoverySongsIfNeeded()
|
|
await this.bootstrapRemoteDiscoverySongsIfNeeded()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async prepareActiveRemoteAccount(): Promise<WebDavAccount | undefined> {
|
|
|
|
|
|
|
+ private async prepareActiveRemoteAccount(fallbackSongs?: VideoItem[]): Promise<WebDavAccount | undefined> {
|
|
|
try {
|
|
try {
|
|
|
const context = getContext(this) as common.Context
|
|
const context = getContext(this) as common.Context
|
|
|
this.remoteDriveManager.setContext(context)
|
|
this.remoteDriveManager.setContext(context)
|
|
@@ -476,15 +619,33 @@ export struct FindView {
|
|
|
if (accounts.length === 0) {
|
|
if (accounts.length === 0) {
|
|
|
return undefined
|
|
return undefined
|
|
|
}
|
|
}
|
|
|
- return this.remoteDriveManager.getActivatedWebDavAccount() || accounts[0]
|
|
|
|
|
|
|
+ const activeAccount = this.remoteDriveManager.getActivatedWebDavAccount() || accounts[0]
|
|
|
|
|
+ const preferredAccountId = findPreferredRemotePlaybackAccountId(
|
|
|
|
|
+ fallbackSongs ?? [],
|
|
|
|
|
+ activeAccount?.id?.toString() ?? ''
|
|
|
|
|
+ )
|
|
|
|
|
+ if (StrUtil.isNotEmpty(preferredAccountId)) {
|
|
|
|
|
+ for (let index = 0; index < accounts.length; index += 1) {
|
|
|
|
|
+ if (accounts[index].id?.toString() === preferredAccountId) {
|
|
|
|
|
+ Logger.info(
|
|
|
|
|
+ TAG,
|
|
|
|
|
+ `[remote-debug] prepareActiveRemoteAccount selected account=${accounts[index].name}, ` +
|
|
|
|
|
+ `type=${accounts[index].webType}, preferredAccountId=${preferredAccountId}, ` +
|
|
|
|
|
+ `fallbackSongs=${fallbackSongs?.length ?? 0}`
|
|
|
|
|
+ )
|
|
|
|
|
+ return accounts[index]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return activeAccount
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
Logger.warn(TAG, `发现页加载远程账号失败: ${this.toErrorMessage(error)}`)
|
|
Logger.warn(TAG, `发现页加载远程账号失败: ${this.toErrorMessage(error)}`)
|
|
|
return undefined
|
|
return undefined
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async queryIndexedRemotePlaybackSongs(): Promise<VideoItem[]> {
|
|
|
|
|
- const account = await this.prepareActiveRemoteAccount()
|
|
|
|
|
|
|
+ private async queryIndexedRemotePlaybackSongs(fallbackSongs: VideoItem[] = []): Promise<VideoItem[]> {
|
|
|
|
|
+ const account = await this.prepareActiveRemoteAccount(fallbackSongs)
|
|
|
if (!account) {
|
|
if (!account) {
|
|
|
Logger.warn(TAG, '[remote-debug] queryIndexedRemotePlaybackSongs skip: no active account')
|
|
Logger.warn(TAG, '[remote-debug] queryIndexedRemotePlaybackSongs skip: no active account')
|
|
|
return []
|
|
return []
|
|
@@ -498,6 +659,15 @@ export struct FindView {
|
|
|
)
|
|
)
|
|
|
if (supportsGlobalIndex && !isIndexReady) {
|
|
if (supportsGlobalIndex && !isIndexReady) {
|
|
|
ToastUtil.showToast('云端加载,请稍候')
|
|
ToastUtil.showToast('云端加载,请稍候')
|
|
|
|
|
+ if (!shouldWaitForIndexedRemotePlayback(isIndexReady, fallbackSongs.length)) {
|
|
|
|
|
+ Logger.info(
|
|
|
|
|
+ TAG,
|
|
|
|
|
+ `[remote-debug] queryIndexedRemotePlaybackSongs fallbackToDb account=${account.name}, ` +
|
|
|
|
|
+ `fallbackSongCount=${fallbackSongs.length}`
|
|
|
|
|
+ )
|
|
|
|
|
+ void this.remoteDriveManager.ensureGlobalSearchIndex(account)
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
const indexedSongs = await this.remoteDriveManager.getGlobalSearchIndexSongs(account)
|
|
const indexedSongs = await this.remoteDriveManager.getGlobalSearchIndexSongs(account)
|
|
|
const clonedSongs = indexedSongs.map((item: VideoItem) => cloneVideoItem(item))
|
|
const clonedSongs = indexedSongs.map((item: VideoItem) => cloneVideoItem(item))
|
|
@@ -555,6 +725,7 @@ export struct FindView {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
this.applyRemoteDiscoverySongs(refreshedRemoteSongs, true)
|
|
this.applyRemoteDiscoverySongs(refreshedRemoteSongs, true)
|
|
|
|
|
+ this.scheduleVisibleRemoteCoverRepair()
|
|
|
Logger.info(
|
|
Logger.info(
|
|
|
TAG,
|
|
TAG,
|
|
|
`发现页远程预热完成: saved=${savedCount}, remote=${refreshedRemoteSongs.length}, ` +
|
|
`发现页远程预热完成: saved=${savedCount}, remote=${refreshedRemoteSongs.length}, ` +
|
|
@@ -1137,8 +1308,9 @@ export struct FindView {
|
|
|
if (!this.mediaTable) {
|
|
if (!this.mediaTable) {
|
|
|
return []
|
|
return []
|
|
|
}
|
|
}
|
|
|
- const indexedSongs = await this.queryIndexedRemotePlaybackSongs()
|
|
|
|
|
let remoteSongs = this.filterUniqueSongs(await this.mediaTable.queryRemoteSongsAsync())
|
|
let remoteSongs = this.filterUniqueSongs(await this.mediaTable.queryRemoteSongsAsync())
|
|
|
|
|
+ const fallbackSongs = remoteSongs.length > 0 ? remoteSongs : this.remoteSongsPool
|
|
|
|
|
+ const indexedSongs = await this.queryIndexedRemotePlaybackSongs(fallbackSongs)
|
|
|
if (indexedSongs.length === 0 && remoteSongs.length === 0) {
|
|
if (indexedSongs.length === 0 && remoteSongs.length === 0) {
|
|
|
await this.ensureRemoteDiscoverySongsAvailable()
|
|
await this.ensureRemoteDiscoverySongsAvailable()
|
|
|
remoteSongs = this.filterUniqueSongs(await this.mediaTable.queryRemoteSongsAsync())
|
|
remoteSongs = this.filterUniqueSongs(await this.mediaTable.queryRemoteSongsAsync())
|
|
@@ -1769,7 +1941,7 @@ export struct FindView {
|
|
|
}
|
|
}
|
|
|
if (this.searchRemoteSongsPool.length === 0) {
|
|
if (this.searchRemoteSongsPool.length === 0) {
|
|
|
const remoteSongs = await this.mediaTable.queryRemoteSongsAsync()
|
|
const remoteSongs = await this.mediaTable.queryRemoteSongsAsync()
|
|
|
- this.searchRemoteSongsPool = this.filterUniqueSongs(remoteSongs)
|
|
|
|
|
|
|
+ this.searchRemoteSongsPool = await this.resolveRemoteSongCovers(this.filterUniqueSongs(remoteSongs), true)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1903,7 +2075,8 @@ export struct FindView {
|
|
|
if (!item) {
|
|
if (!item) {
|
|
|
return 'unknown'
|
|
return 'unknown'
|
|
|
}
|
|
}
|
|
|
- return `${this.getSongTitle(item)}|type=${item.type}|acc=${item.webdav_account_id ?? ''}|path=${this.sanitizeSongPath(item.filePath)}`
|
|
|
|
|
|
|
+ return `${this.getSongTitle(item)}|type=${item.type}|acc=${item.webdav_account_id ?? ''}|path=${this.sanitizeSongPath(item.filePath)}|` +
|
|
|
|
|
+ `cover=${this.sanitizeCoverValue(item.pixelMapPath)}`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private sanitizeSongPath(path?: string): string {
|
|
private sanitizeSongPath(path?: string): string {
|
|
@@ -1917,6 +2090,44 @@ export struct FindView {
|
|
|
return `...${value.substring(value.length - 48)}`
|
|
return `...${value.substring(value.length - 48)}`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private sanitizeCoverValue(value?: string): string {
|
|
|
|
|
+ if (StrUtil.isEmpty(value)) {
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+ const coverValue = value as string
|
|
|
|
|
+ if (coverValue.length <= 72) {
|
|
|
|
|
+ return coverValue
|
|
|
|
|
+ }
|
|
|
|
|
+ return `...${coverValue.substring(coverValue.length - 72)}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private logRemoteDiscoveryCoverState(label: string, items: VideoItem[]): void {
|
|
|
|
|
+ if (!items || items.length === 0) {
|
|
|
|
|
+ Logger.info(TAG, `[cover-debug] ${label} items=0`)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ let coverCount = 0
|
|
|
|
|
+ let absoluteCoverCount = 0
|
|
|
|
|
+ let relativeCoverCount = 0
|
|
|
|
|
+ for (let i = 0; i < items.length; i++) {
|
|
|
|
|
+ const coverPath = items[i].pixelMapPath ?? ''
|
|
|
|
|
+ if (coverPath.length <= 0) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ coverCount += 1
|
|
|
|
|
+ if (coverPath.startsWith('http://') || coverPath.startsWith('https://') || coverPath.startsWith('file://')) {
|
|
|
|
|
+ absoluteCoverCount += 1
|
|
|
|
|
+ } else {
|
|
|
|
|
+ relativeCoverCount += 1
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Logger.info(
|
|
|
|
|
+ TAG,
|
|
|
|
|
+ `[cover-debug] ${label} items=${items.length}, coverCount=${coverCount}, absoluteCover=${absoluteCoverCount}, ` +
|
|
|
|
|
+ `relativeCover=${relativeCoverCount}, sample=${this.buildSongDebugLog(items)}`
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private buildAlbumSelectionLog(items: FindAlbumGroup[], limit: number = 4): string {
|
|
private buildAlbumSelectionLog(items: FindAlbumGroup[], limit: number = 4): string {
|
|
|
if (items.length === 0) {
|
|
if (items.length === 0) {
|
|
|
return '[]'
|
|
return '[]'
|
|
@@ -2706,6 +2917,7 @@ export struct FindView {
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.aspectRatio(3 / 4)
|
|
.aspectRatio(3 / 4)
|
|
|
.borderRadius(16)
|
|
.borderRadius(16)
|
|
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
.objectFit(ImageFit.Cover)
|
|
.objectFit(ImageFit.Cover)
|
|
|
|
|
|
|
|
Row() {
|
|
Row() {
|
|
@@ -2797,6 +3009,7 @@ export struct FindView {
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.aspectRatio(3 / 4)
|
|
.aspectRatio(3 / 4)
|
|
|
.borderRadius(18)
|
|
.borderRadius(18)
|
|
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
.objectFit(ImageFit.Cover)
|
|
.objectFit(ImageFit.Cover)
|
|
|
|
|
|
|
|
Row() {
|
|
Row() {
|
|
@@ -2941,6 +3154,7 @@ export struct FindView {
|
|
|
.aspectRatio(3 / 4)
|
|
.aspectRatio(3 / 4)
|
|
|
.borderRadius(16)
|
|
.borderRadius(16)
|
|
|
.width(160)
|
|
.width(160)
|
|
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
.objectFit(ImageFit.Cover)
|
|
.objectFit(ImageFit.Cover)
|
|
|
|
|
|
|
|
Row() {
|
|
Row() {
|