Преглед изворни кода

修复共享名称必须是share的bug

chendeben пре 9 месеци
родитељ
комит
c7cc46b842
2 измењених фајлова са 145 додато и 34 уклоњено
  1. 116 15
      entry/src/main/ets/common/network/SmbFileCache.ets
  2. 29 19
      entry/src/main/ets/view/LocalMusic.ets

+ 116 - 15
entry/src/main/ets/common/network/SmbFileCache.ets

@@ -2,6 +2,7 @@ import { MD5 } from '@pura/harmony-utils';
 import { WebdavManager } from '../util/WebdavManager';
 import { WebdavManager } from '../util/WebdavManager';
 import FileManager, { merge2paths } from '../util/FileManager';
 import FileManager, { merge2paths } from '../util/FileManager';
 import { WebDavAccount } from '../../viewmodel/WebDavAccount';
 import { WebDavAccount } from '../../viewmodel/WebDavAccount';
+import Logger from '../util/Logger';
 import nativeBridge from 'libentry.so';
 import nativeBridge from 'libentry.so';
 
 
 interface SmbDownloadBinding {
 interface SmbDownloadBinding {
@@ -10,6 +11,88 @@ interface SmbDownloadBinding {
 
 
 const smbBinding: SmbDownloadBinding = nativeBridge as SmbDownloadBinding;
 const smbBinding: SmbDownloadBinding = nativeBridge as SmbDownloadBinding;
 
 
+function normalizeRelativePath(relativePath: string): string {
+  if (!relativePath || relativePath.length === 0) {
+    return '/';
+  }
+  let normalized = relativePath.replace(/\\/g, '/').replace(/\/+/g, '/');
+  if (!normalized.startsWith('/')) {
+    normalized = `/${normalized}`;
+  }
+  if (normalized.length > 1 && normalized.endsWith('/')) {
+    normalized = normalized.slice(0, -1);
+  }
+  return normalized || '/';
+}
+
+function cleanShareName(name?: string): string {
+  return name ? name.replace(/^\/+|\/+$/g, '') : '';
+}
+
+function buildRemotePathCandidates(relativePath: string, account: WebDavAccount): string[] {
+  const ordered: string[] = [];
+  const seen = new Set<string>();
+  const pushCandidate = (value: string) => {
+    if (!value || value.length === 0) {
+      return;
+    }
+    const normalized = normalizeRelativePath(value);
+    if (!seen.has(normalized)) {
+      seen.add(normalized);
+      ordered.push(normalized);
+    }
+  };
+
+  const base = normalizeRelativePath(relativePath);
+  pushCandidate(base);
+
+  const trimmed = base.replace(/^\/+/, '');
+  if (trimmed.length === 0) {
+    return ordered;
+  }
+
+  const shareNames: string[] = [];
+  const accountShare = cleanShareName(account.smbShare);
+  if (accountShare.length > 0) {
+    shareNames.push(accountShare);
+  }
+
+  for (let i = 0; i < shareNames.length; i++) {
+    const share = shareNames[i];
+    const lowerShare = share.toLowerCase();
+    if (trimmed.toLowerCase().startsWith(`${lowerShare}/`)) {
+      const remainder = trimmed.substring(share.length + 1);
+      if (remainder.length > 0) {
+        pushCandidate(remainder);
+      }
+    }
+  }
+
+  const slashIndex = trimmed.indexOf('/');
+  if (slashIndex > 0) {
+    const firstSegment = trimmed.substring(0, slashIndex);
+    const remainder = trimmed.substring(slashIndex + 1);
+    if (remainder.length > 0) {
+      const matchesShare = shareNames.some(name => name.toLowerCase() === firstSegment.toLowerCase());
+      if (!matchesShare) {
+        pushCandidate(remainder);
+      }
+    }
+  }
+
+  return ordered;
+}
+
+function shouldRetryRemoteDownload(error: Error): boolean {
+  if (!error || !error.message) {
+    return false;
+  }
+  const message = error.message.toLowerCase();
+  return message.indexOf('failed to open remote file') >= 0 ||
+    message.indexOf('object_name_not_found') >= 0 ||
+    message.indexOf('no such file') >= 0;
+}
+
 async function buildCacheFileName(relativePath: string): Promise<string> {
 async function buildCacheFileName(relativePath: string): Promise<string> {
   const hash = await MD5.digestSync(relativePath ?? '');
   const hash = await MD5.digestSync(relativePath ?? '');
   const lastSlash = relativePath.lastIndexOf('/');
   const lastSlash = relativePath.lastIndexOf('/');
@@ -34,7 +117,7 @@ export async function ensureSmbFileCached(account: WebDavAccount, relativePath:
   await FileManager.createDir(cacheRoot);
   await FileManager.createDir(cacheRoot);
   const accountDir = merge2paths(cacheRoot, account.id?.toString() ?? 'default');
   const accountDir = merge2paths(cacheRoot, account.id?.toString() ?? 'default');
   await FileManager.createDir(accountDir);
   await FileManager.createDir(accountDir);
-  const normalizedRelative = relativePath?.startsWith('/') ? relativePath : `/${relativePath}`;
+  const normalizedRelative = normalizeRelativePath(relativePath);
   const cacheFileName = await buildCacheFileName(normalizedRelative);
   const cacheFileName = await buildCacheFileName(normalizedRelative);
   const localPath = merge2paths(accountDir, cacheFileName);
   const localPath = merge2paths(accountDir, cacheFileName);
   let exists = await FileManager.isExist(localPath);
   let exists = await FileManager.isExist(localPath);
@@ -47,20 +130,38 @@ export async function ensureSmbFileCached(account: WebDavAccount, relativePath:
   }
   }
   if (!exists) {
   if (!exists) {
     const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
     const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
-    try {
-      smbBinding.downloadSmbFile(
-        host,
-        account.smbShare,
-        account.account,
-        account.password,
-        account.smbDomain,
-        normalizedRelative,
-        localPath
-      );
-    } catch (error) {
-      await FileManager.deleteFile(localPath);
-      const err = error as Error;
-      throw err;
+    const candidates = buildRemotePathCandidates(normalizedRelative, account);
+    let lastError: Error | undefined;
+    for (let i = 0; i < candidates.length; i++) {
+      const candidate = candidates[i];
+      try {
+        smbBinding.downloadSmbFile(
+          host,
+          account.smbShare,
+          account.account,
+          account.password,
+          account.smbDomain,
+          candidate,
+          localPath
+        );
+        if (i > 0) {
+          Logger.warn('SMB download fallback succeeded with path:', candidate);
+        }
+        lastError = undefined;
+        break;
+      } catch (error) {
+        await FileManager.deleteFile(localPath);
+        const err = error as Error;
+        lastError = err;
+        const shouldRetry = i < candidates.length - 1 && shouldRetryRemoteDownload(err);
+        if (!shouldRetry) {
+          throw err;
+        }
+        Logger.warn('SMB download failed for path, trying fallback:', candidate);
+      }
+    }
+    if (lastError) {
+      throw lastError;
     }
     }
   }
   }
   return localPath;
   return localPath;

+ 29 - 19
entry/src/main/ets/view/LocalMusic.ets

@@ -176,31 +176,41 @@ function getShareNameFromFilePath(filePath?: string): string | undefined {
   return match && match[1] ? match[1] : undefined;
   return match && match[1] ? match[1] : undefined;
 }
 }
 
 
-function removeSharePrefixFromPath(path: string, shareName?: string): string {
-  if (!shareName) {
+function removeSharePrefixFromPath(path: string, primaryShare?: string, secondaryShare?: string): string {
+  const uniqueShares: string[] = [];
+  [primaryShare, secondaryShare].forEach((name?: string) => {
+    const cleaned = name ? name.replace(/^\/+|\/+$/g, '') : '';
+    if (cleaned.length > 0 && !uniqueShares.some(item => item.toLowerCase() === cleaned.toLowerCase())) {
+      uniqueShares.push(cleaned);
+    }
+  });
+  if (uniqueShares.length === 0) {
     return path;
     return path;
   }
   }
-  const cleanedShare = shareName.replace(/^\/+|\/+$/g, '');
-  if (cleanedShare.length === 0) {
-    return path;
-  }
-  const lowerPath = path.toLowerCase();
-  const lowerShare = cleanedShare.toLowerCase();
-  if (lowerPath === lowerShare) {
-    return '';
-  }
-  const prefix = `${lowerShare}/`;
-  if (lowerPath.startsWith(prefix)) {
-    return path.substring(cleanedShare.length + 1);
+  let result = path;
+  for (let i = 0; i < uniqueShares.length; i++) {
+    const share = uniqueShares[i];
+    const lowerPath = result.toLowerCase();
+    const lowerShare = share.toLowerCase();
+    if (lowerPath === lowerShare) {
+      result = '';
+      break;
+    }
+    const prefix = `${lowerShare}/`;
+    if (lowerPath.startsWith(prefix)) {
+      result = result.substring(share.length + 1);
+      break;
+    }
   }
   }
-  return path;
+  return result;
 }
 }
 
 
-function extractSmbRelativePath(song: VideoItem): string {
-  const shareName = getShareNameFromFilePath(song.filePath);
+function extractSmbRelativePath(song: VideoItem, shareNameOverride?: string): string {
+  const shareFromFilePath = getShareNameFromFilePath(song.filePath);
+  const shareName = shareNameOverride ?? shareFromFilePath;
   if (song.remote_rel_path && song.remote_rel_path.length > 0) {
   if (song.remote_rel_path && song.remote_rel_path.length > 0) {
     const cleaned = song.remote_rel_path.replace(/^\/+/, '');
     const cleaned = song.remote_rel_path.replace(/^\/+/, '');
-    return removeSharePrefixFromPath(cleaned, shareName);
+    return removeSharePrefixFromPath(cleaned, shareName, shareFromFilePath);
   }
   }
   if (!song.filePath) {
   if (!song.filePath) {
     return '';
     return '';
@@ -254,7 +264,7 @@ async function setVideoUrlForSong(song: VideoItem): Promise<string> {
       if (!account) {
       if (!account) {
         throw new Error('SMB账号不可用');
         throw new Error('SMB账号不可用');
       }
       }
-      const relativePath = extractSmbRelativePath(song);
+      const relativePath = extractSmbRelativePath(song, account.smbShare);
       const cachedPath = await ensureSmbFileCached(account, relativePath);
       const cachedPath = await ensureSmbFileCached(account, relativePath);
       Logger.info(TAG, `SMB 缓存路径: ${cachedPath}`);
       Logger.info(TAG, `SMB 缓存路径: ${cachedPath}`);
       return cachedPath;
       return cachedPath;