|
@@ -20,6 +20,7 @@ import { listSmbDirectory, SmbDirectoryEntry } from '../network/SmbBridge';
|
|
|
import { NavidromeApi, NavidromeAlbumDetail, NavidromeArtist, NavidromeArtistDetail, NavidromeSong } from '../network/NavidromeApi';
|
|
import { NavidromeApi, NavidromeAlbumDetail, NavidromeArtist, NavidromeArtistDetail, NavidromeSong } from '../network/NavidromeApi';
|
|
|
import { WebDavUrlUtil } from './WebDavUrlUtil';
|
|
import { WebDavUrlUtil } from './WebDavUrlUtil';
|
|
|
import MediaTable from './MediaTable';
|
|
import MediaTable from './MediaTable';
|
|
|
|
|
+import { FtpClient, FileInfo as FtpEntryInfo, StringEncoding, AccessOptions } from '@liuzhosoft/ftp4h';
|
|
|
|
|
|
|
|
const TAG = 'heanup RemoteDriveManager';
|
|
const TAG = 'heanup RemoteDriveManager';
|
|
|
|
|
|
|
@@ -177,6 +178,7 @@ export class RemoteDriveManager {
|
|
|
const smbShareIndex = resultSet.getColumnIndex('smbShare');
|
|
const smbShareIndex = resultSet.getColumnIndex('smbShare');
|
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
|
const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
|
|
const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
|
|
|
|
|
+ const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
|
if (smbShareIndex >= 0) {
|
|
if (smbShareIndex >= 0) {
|
|
|
account.smbShare = resultSet.getString(smbShareIndex) ?? '';
|
|
account.smbShare = resultSet.getString(smbShareIndex) ?? '';
|
|
|
}
|
|
}
|
|
@@ -319,7 +321,7 @@ export class RemoteDriveManager {
|
|
|
// 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
|
|
// 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
|
|
|
const querySql = `
|
|
const querySql = `
|
|
|
SELECT id, filePath FROM mediaTable
|
|
SELECT id, filePath FROM mediaTable
|
|
|
- WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB})
|
|
|
|
|
|
|
+ WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB}, ${CommonConstants.TYPE_FTP})
|
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
@@ -333,7 +335,7 @@ export class RemoteDriveManager {
|
|
|
const updateSql = `
|
|
const updateSql = `
|
|
|
UPDATE mediaTable
|
|
UPDATE mediaTable
|
|
|
SET webdav_account_id = ?
|
|
SET webdav_account_id = ?
|
|
|
- WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB})
|
|
|
|
|
|
|
+ WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB}, ${CommonConstants.TYPE_FTP})
|
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
@@ -408,12 +410,26 @@ export class RemoteDriveManager {
|
|
|
account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
|
|
account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
|
|
|
const smbShareIndex = resultSet.getColumnIndex('smbShare');
|
|
const smbShareIndex = resultSet.getColumnIndex('smbShare');
|
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
|
|
|
+ const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
|
|
|
if (smbShareIndex >= 0) {
|
|
if (smbShareIndex >= 0) {
|
|
|
account.smbShare = resultSet.getString(smbShareIndex) ?? '';
|
|
account.smbShare = resultSet.getString(smbShareIndex) ?? '';
|
|
|
}
|
|
}
|
|
|
if (smbDomainIndex >= 0) {
|
|
if (smbDomainIndex >= 0) {
|
|
|
account.smbDomain = resultSet.getString(smbDomainIndex) ?? '';
|
|
account.smbDomain = resultSet.getString(smbDomainIndex) ?? '';
|
|
|
}
|
|
}
|
|
|
|
|
+ const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
|
|
|
+ if (navBaseIndex >= 0) {
|
|
|
|
|
+ const stored = resultSet.getString(navBaseIndex);
|
|
|
|
|
+ account.navidromeBasePath = stored && stored.length > 0 ? stored : '/rest';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ account.navidromeBasePath = '/rest';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (ftpEncodingIndex >= 0) {
|
|
|
|
|
+ const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
|
|
|
+ account.ftpEncoding = encodingValue && encodingValue.length > 0 ? encodingValue : 'utf-8';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ account.ftpEncoding = 'utf-8';
|
|
|
|
|
+ }
|
|
|
account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
|
|
account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
|
|
|
|
|
|
|
|
resultSet.close();
|
|
resultSet.close();
|
|
@@ -450,7 +466,8 @@ export class RemoteDriveManager {
|
|
|
webType INTEGER DEFAULT 0,
|
|
webType INTEGER DEFAULT 0,
|
|
|
smbShare TEXT,
|
|
smbShare TEXT,
|
|
|
smbDomain TEXT,
|
|
smbDomain TEXT,
|
|
|
- navidromeBasePath TEXT
|
|
|
|
|
|
|
+ navidromeBasePath TEXT,
|
|
|
|
|
+ ftpEncoding TEXT
|
|
|
)`;
|
|
)`;
|
|
|
|
|
|
|
|
return this.dataBaseUtil.executeSql(createTableSql)
|
|
return this.dataBaseUtil.executeSql(createTableSql)
|
|
@@ -477,7 +494,6 @@ export class RemoteDriveManager {
|
|
|
Logger.info(TAG, 'coverPath字段添加成功,数据库升级完成');
|
|
Logger.info(TAG, 'coverPath字段添加成功,数据库升级完成');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
// 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
|
|
// 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
|
|
|
- Logger.info(TAG, 'coverPath字段可能已存在或添加失败,继续正常运行');
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -489,7 +505,6 @@ export class RemoteDriveManager {
|
|
|
Logger.info(TAG, 'webType字段添加成功,数据库升级完成');
|
|
Logger.info(TAG, 'webType字段添加成功,数据库升级完成');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
// 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
|
|
// 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
|
|
|
- Logger.info(TAG, 'webType字段可能已存在或添加失败,继续正常运行');
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -498,7 +513,6 @@ export class RemoteDriveManager {
|
|
|
await this.dataBaseUtil.executeSql(addSmbShareSql);
|
|
await this.dataBaseUtil.executeSql(addSmbShareSql);
|
|
|
Logger.info(TAG, 'smbShare字段添加成功');
|
|
Logger.info(TAG, 'smbShare字段添加成功');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- Logger.info(TAG, 'smbShare字段可能已存在或添加失败,继续运行');
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -507,7 +521,6 @@ export class RemoteDriveManager {
|
|
|
await this.dataBaseUtil.executeSql(addSmbDomainSql);
|
|
await this.dataBaseUtil.executeSql(addSmbDomainSql);
|
|
|
Logger.info(TAG, 'smbDomain字段添加成功');
|
|
Logger.info(TAG, 'smbDomain字段添加成功');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- Logger.info(TAG, 'smbDomain字段可能已存在或添加失败,继续运行');
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -516,7 +529,14 @@ export class RemoteDriveManager {
|
|
|
await this.dataBaseUtil.executeSql(addNavBaseSql);
|
|
await this.dataBaseUtil.executeSql(addNavBaseSql);
|
|
|
Logger.info(TAG, 'navidromeBasePath字段添加成功');
|
|
Logger.info(TAG, 'navidromeBasePath字段添加成功');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- Logger.info(TAG, 'navidromeBasePath字段可能已存在或添加失败,继续运行');
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Logger.info(TAG, '尝试添加ftpEncoding字段...');
|
|
|
|
|
+ const addFtpEncodingSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN ftpEncoding TEXT`;
|
|
|
|
|
+ await this.dataBaseUtil.executeSql(addFtpEncodingSql);
|
|
|
|
|
+ Logger.info(TAG, 'ftpEncoding字段添加成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -530,7 +550,7 @@ export class RemoteDriveManager {
|
|
|
// 先查询基础字段(确保这些字段在旧版本中存在)
|
|
// 先查询基础字段(确保这些字段在旧版本中存在)
|
|
|
const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
|
|
const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
|
|
|
'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
|
|
'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
|
|
|
- 'account', 'password', 'enableHttps', 'coverPath', 'webType', 'smbShare', 'smbDomain', 'navidromeBasePath'];
|
|
|
|
|
|
|
+ 'account', 'password', 'enableHttps', 'coverPath', 'webType', 'smbShare', 'smbDomain', 'navidromeBasePath', 'ftpEncoding'];
|
|
|
|
|
|
|
|
const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
|
|
const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
|
|
|
|
|
|
|
@@ -573,6 +593,13 @@ export class RemoteDriveManager {
|
|
|
} else {
|
|
} else {
|
|
|
account.navidromeBasePath = '/rest';
|
|
account.navidromeBasePath = '/rest';
|
|
|
}
|
|
}
|
|
|
|
|
+ const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
|
|
|
+ if (ftpEncodingIndex >= 0) {
|
|
|
|
|
+ const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
|
|
|
+ account.ftpEncoding = encodingValue && encodingValue.length > 0 ? encodingValue : 'utf-8';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ account.ftpEncoding = 'utf-8';
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
this.webDavAccounts.push(account);
|
|
this.webDavAccounts.push(account);
|
|
|
}
|
|
}
|
|
@@ -606,7 +633,8 @@ export class RemoteDriveManager {
|
|
|
webType: number = 0,
|
|
webType: number = 0,
|
|
|
smbShare: string = '',
|
|
smbShare: string = '',
|
|
|
smbDomain: string = '',
|
|
smbDomain: string = '',
|
|
|
- navidromeBasePath: string = '/rest'
|
|
|
|
|
|
|
+ navidromeBasePath: string = '/rest',
|
|
|
|
|
+ ftpEncoding: string = 'utf-8'
|
|
|
): Promise<void> {
|
|
): Promise<void> {
|
|
|
try {
|
|
try {
|
|
|
const values: relationalStore.ValuesBucket = {
|
|
const values: relationalStore.ValuesBucket = {
|
|
@@ -627,7 +655,8 @@ export class RemoteDriveManager {
|
|
|
'webType': webType,
|
|
'webType': webType,
|
|
|
'smbShare': smbShare,
|
|
'smbShare': smbShare,
|
|
|
'smbDomain': smbDomain,
|
|
'smbDomain': smbDomain,
|
|
|
- 'navidromeBasePath': navidromeBasePath
|
|
|
|
|
|
|
+ 'navidromeBasePath': navidromeBasePath,
|
|
|
|
|
+ 'ftpEncoding': ftpEncoding
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
await this.dataBaseUtil.insertData(this.webDavTable, values);
|
|
await this.dataBaseUtil.insertData(this.webDavTable, values);
|
|
@@ -664,7 +693,8 @@ export class RemoteDriveManager {
|
|
|
'webType': account.webType,
|
|
'webType': account.webType,
|
|
|
'smbShare': account.smbShare,
|
|
'smbShare': account.smbShare,
|
|
|
'smbDomain': account.smbDomain,
|
|
'smbDomain': account.smbDomain,
|
|
|
- 'navidromeBasePath': account.navidromeBasePath
|
|
|
|
|
|
|
+ 'navidromeBasePath': account.navidromeBasePath,
|
|
|
|
|
+ 'ftpEncoding': account.ftpEncoding
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const predicates = new relationalStore.RdbPredicates(this.webDavTable);
|
|
const predicates = new relationalStore.RdbPredicates(this.webDavTable);
|
|
@@ -759,6 +789,8 @@ export class RemoteDriveManager {
|
|
|
await this.loadSmbFiles(account, normalizedFullPath);
|
|
await this.loadSmbFiles(account, normalizedFullPath);
|
|
|
} else if (account.webType === RemoteDriveType.Navidrome) {
|
|
} else if (account.webType === RemoteDriveType.Navidrome) {
|
|
|
await this.loadNavidromeFiles(account, normalizedFullPath);
|
|
await this.loadNavidromeFiles(account, normalizedFullPath);
|
|
|
|
|
+ } else if (account.webType === RemoteDriveType.Ftp) {
|
|
|
|
|
+ await this.loadFtpFiles(account, normalizedFullPath);
|
|
|
} else {
|
|
} else {
|
|
|
await this.loadWebDavFiles(account, normalizedFullPath);
|
|
await this.loadWebDavFiles(account, normalizedFullPath);
|
|
|
}
|
|
}
|
|
@@ -867,6 +899,58 @@ export class RemoteDriveManager {
|
|
|
Logger.info(TAG, `从SMB获取到 ${entries.length} 个文件/文件夹`);
|
|
Logger.info(TAG, `从SMB获取到 ${entries.length} 个文件/文件夹`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private resolveFtpHost(account: WebDavAccount): string {
|
|
|
|
|
+ const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
|
|
|
|
|
+ return host?.trim() ?? '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getFtpEncoding(account: WebDavAccount): StringEncoding {
|
|
|
|
|
+ if (account.ftpEncoding && account.ftpEncoding.length > 0) {
|
|
|
|
|
+ return account.ftpEncoding as StringEncoding;
|
|
|
|
|
+ }
|
|
|
|
|
+ return 'utf-8';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private buildFtpAccessOptions(account: WebDavAccount): AccessOptions {
|
|
|
|
|
+ const host = this.resolveFtpHost(account);
|
|
|
|
|
+ if (!host) {
|
|
|
|
|
+ throw new Error('FTP账户缺少服务器地址');
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ host,
|
|
|
|
|
+ port: account.port && account.port > 0 ? account.port : 21,
|
|
|
|
|
+ user: account.account && account.account.length > 0 ? account.account : undefined,
|
|
|
|
|
+ password: account.password && account.password.length > 0 ? account.password : undefined,
|
|
|
|
|
+ encoding: this.getFtpEncoding(account)
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async loadFtpFiles(account: WebDavAccount, fullPath: string): Promise<void> {
|
|
|
|
|
+ const client = new FtpClient();
|
|
|
|
|
+ const options = this.buildFtpAccessOptions(account);
|
|
|
|
|
+ await client.access(options);
|
|
|
|
|
+ try {
|
|
|
|
|
+ const listPath = this.normalizeFullPath(fullPath);
|
|
|
|
|
+ const entries = await client.list(listPath === '/' ? '/' : listPath);
|
|
|
|
|
+ this.webDavFiles = [];
|
|
|
|
|
+ this.webDavSongs = [];
|
|
|
|
|
+ for (let i = 0; i < entries.length; i++) {
|
|
|
|
|
+ const entry = entries[i];
|
|
|
|
|
+ const info = this.ftpEntryToFileInfo(entry, listPath);
|
|
|
|
|
+ this.webDavFiles.push(info);
|
|
|
|
|
+ if (info.isDirectory) {
|
|
|
|
|
+ this.registerPathLabel(info.href, info.fileName);
|
|
|
|
|
+ } else if (this.isAudioFile(info.fileName)) {
|
|
|
|
|
+ this.webDavSongs.push(this.buildFtpVideoItem(info, account));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ await this.enrichSongsWithDatabase(this.webDavSongs);
|
|
|
|
|
+ Logger.info(TAG, `从FTP获取到 ${entries.length} 个文件/文件夹`);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ await client.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private async loadNavidromeFiles(account: WebDavAccount, fullPath: string): Promise<void> {
|
|
private async loadNavidromeFiles(account: WebDavAccount, fullPath: string): Promise<void> {
|
|
|
const normalized = this.normalizeFullPath(fullPath);
|
|
const normalized = this.normalizeFullPath(fullPath);
|
|
|
this.registerPathLabel('/', '根目录');
|
|
this.registerPathLabel('/', '根目录');
|
|
@@ -968,7 +1052,7 @@ export class RemoteDriveManager {
|
|
|
}
|
|
}
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
- if (item.type === CommonConstants.TYPE_SMB || item.type === CommonConstants.TYPE_NAVIDROME) {
|
|
|
|
|
|
|
+ if (item.type === CommonConstants.TYPE_SMB || item.type === CommonConstants.TYPE_NAVIDROME || item.type === CommonConstants.TYPE_FTP) {
|
|
|
if (item.remote_rel_path) {
|
|
if (item.remote_rel_path) {
|
|
|
return item.remote_rel_path;
|
|
return item.remote_rel_path;
|
|
|
}
|
|
}
|
|
@@ -1004,6 +1088,9 @@ export class RemoteDriveManager {
|
|
|
if (account.webType === RemoteDriveType.Smb) {
|
|
if (account.webType === RemoteDriveType.Smb) {
|
|
|
return this.buildSmbVideoItem(fileInfo, account);
|
|
return this.buildSmbVideoItem(fileInfo, account);
|
|
|
}
|
|
}
|
|
|
|
|
+ if (account.webType === RemoteDriveType.Ftp) {
|
|
|
|
|
+ return this.buildFtpVideoItem(fileInfo, account);
|
|
|
|
|
+ }
|
|
|
// 构建安全的WebDAV URL(不包含认证信息)
|
|
// 构建安全的WebDAV URL(不包含认证信息)
|
|
|
const protocol = account.enableHttps ? 'https' : 'http';
|
|
const protocol = account.enableHttps ? 'https' : 'http';
|
|
|
const host = account.isUseLocalHost ? account.localHost : account.host;
|
|
const host = account.isUseLocalHost ? account.localHost : account.host;
|
|
@@ -1101,6 +1188,52 @@ export class RemoteDriveManager {
|
|
|
return videoItem;
|
|
return videoItem;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private ftpEntryToFileInfo(entry: FtpEntryInfo, basePath: string): FileInfo {
|
|
|
|
|
+ const normalizedBase = this.normalizeFullPath(basePath);
|
|
|
|
|
+ const targetPath = this.normalizeFullPath(`${normalizedBase === '/' ? '' : normalizedBase}/${entry.name}`);
|
|
|
|
|
+ const info = new FileInfo(normalizedBase, entry.name, entry.size ?? 0, entry.modifiedAt ? entry.modifiedAt.getTime() : Date.now());
|
|
|
|
|
+ info.fileName = entry.name;
|
|
|
|
|
+ info.href = targetPath;
|
|
|
|
|
+ info.isDirectory = Boolean(entry.isDirectory);
|
|
|
|
|
+ info.contentLength = entry.size ?? 0;
|
|
|
|
|
+ info.time = entry.modifiedAt ? entry.modifiedAt.getTime() : Date.now();
|
|
|
|
|
+ return info;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private buildFtpVideoItem(fileInfo: FileInfo, account: WebDavAccount): VideoItem {
|
|
|
|
|
+ const host = this.resolveFtpHost(account);
|
|
|
|
|
+ if (!host) {
|
|
|
|
|
+ throw new Error('FTP账号缺少服务器地址');
|
|
|
|
|
+ }
|
|
|
|
|
+ const normalizedPath = this.normalizeFullPath(fileInfo.href || `/${fileInfo.fileName}`);
|
|
|
|
|
+ const encodedPath = encodeURI(normalizedPath);
|
|
|
|
|
+ const port = account.port && account.port > 0 ? `:${account.port}` : '';
|
|
|
|
|
+ const ftpUrl = `ftp://${host}${port}${encodedPath}`;
|
|
|
|
|
+
|
|
|
|
|
+ const videoItem = new VideoItem(
|
|
|
|
|
+ this.getFileNameWithoutExtension(fileInfo.fileName),
|
|
|
|
|
+ '',
|
|
|
|
|
+ ftpUrl,
|
|
|
|
|
+ CommonConstants.TYPE_FTP,
|
|
|
|
|
+ fileInfo.contentLength,
|
|
|
|
|
+ Utility.getFormatDateStr(fileInfo.time, 'yyyy-MM-dd HH:mm')
|
|
|
|
|
+ );
|
|
|
|
|
+ videoItem.album = '';
|
|
|
|
|
+ videoItem.size = Utility.formatFSize(fileInfo.contentLength);
|
|
|
|
|
+ const musicData: MusicInfo = parseMusicFileName(fileInfo.fileName);
|
|
|
|
|
+ if (musicData.isValid) {
|
|
|
|
|
+ videoItem.artist = musicData.artist;
|
|
|
|
|
+ videoItem.name = musicData.title;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ videoItem.artist = Constants.UNKNOWN_ARTIST;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (account && account.id) {
|
|
|
|
|
+ videoItem.webdav_account_id = account.id.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+ videoItem.remote_rel_path = normalizedPath;
|
|
|
|
|
+ return videoItem;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private smbEntryToFileInfo(entry: SmbDirectoryEntry, basePath: string): FileInfo {
|
|
private smbEntryToFileInfo(entry: SmbDirectoryEntry, basePath: string): FileInfo {
|
|
|
const normalizedPath = this.combineRemotePath(basePath, entry.name, entry.isDirectory);
|
|
const normalizedPath = this.combineRemotePath(basePath, entry.name, entry.isDirectory);
|
|
|
const info = new FileInfo('', entry.name, entry.size, Date.now());
|
|
const info = new FileInfo('', entry.name, entry.size, Date.now());
|