WIP next: FTP 동영상 스트리밍 다리 · 경로 복사 ftp:// (합친 뒤 고침, 관리자 지시) (검토 대기)
- 스트리밍 다리 (remote_stream): ftp:// 의 부분 읽기를 FtpRemote.openReadFrom (REST) 로 - 예전에는 처음부터 읽으며 건너뛰어 동영상 건너뛰기가 느렸다 (15 가 알린 것). 시험: ftp_test "동영상 스트리밍 다리". - 경로 복사: FTP 는 ftp://아이디@서버:포트/시작 폴더/경로 (암시 FTPS 990 은 ftps://, 처음 값 포트는 빼고, 비밀번호 없음). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>hint_fix
parent
8d6f288fab
commit
9f94d8ea2c
|
|
@ -5,6 +5,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'ftp.dart' show FtpRemote;
|
||||
import 'sftp.dart' show SftpRemote;
|
||||
import 'smb.dart' show SmbRemote;
|
||||
import 'vfs.dart';
|
||||
|
|
@ -61,6 +62,8 @@ class RemoteStreamBridge {
|
|||
final client = d.client;
|
||||
if (client is SftpRemote) return client.openReadFrom(d.rel, offset: start, length: length);
|
||||
if (client is SmbRemote) return client.openReadFrom(d.rel, start: start, length: length);
|
||||
// l FTP: REST 로 그 자리부터 (15 가 알린 것 - 합친 뒤 넣음)
|
||||
if (client is FtpRemote) return client.openReadFrom(d.rel, offset: start, length: length);
|
||||
// 그 밖 (WebDAV 는 다리를 쓰지 않지만 혹시 오면): 처음부터 읽으며 건너뛴다
|
||||
return _skip(await client.openRead(d.rel), start, length);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -794,6 +794,15 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
final base = s.root.isEmpty ? '/~' : (s.root.startsWith('/') ? s.root : '/${s.root}');
|
||||
return 'sftp://${s.user.isEmpty ? '' : '${s.user}@'}${s.host}${s.port == 22 ? '' : ':${s.port}'}$base${d.isRoot ? '' : d.rel}';
|
||||
}
|
||||
// l: FTP 는 ftp://아이디@서버:포트/경로 (암호화 990 은 ftps://, 비밀번호 없음)
|
||||
if (d.isFtp) {
|
||||
final s = FtpRegistry.server(d.server);
|
||||
if (s == null) return _displayPath(path);
|
||||
final implicit = s.security == 'implicit';
|
||||
final r = s.root.trim().replaceAll(RegExp(r'^/+|/+$'), '');
|
||||
return '${implicit ? 'ftps' : 'ftp'}://${s.user.isEmpty ? '' : '${Uri.encodeComponent(s.user)}@'}${s.host}'
|
||||
'${s.port == (implicit ? 990 : 21) ? '' : ':${s.port}'}${r.isEmpty ? '' : '/$r'}${d.isRoot ? '' : d.rel}';
|
||||
}
|
||||
// 88: SMB 는 smb://서버/공유/경로 (아이디 · 비밀번호 없음)
|
||||
if (d.isSmb) {
|
||||
final s = SmbRegistry.server(d.server);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'dart:io';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:jj_mkvmaker/app/settings.dart';
|
||||
import 'package:jj_mkvmaker/core/ftp.dart';
|
||||
import 'package:jj_mkvmaker/core/remote_stream.dart';
|
||||
import 'package:jj_mkvmaker/services/secret_store.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
|
|
@ -142,6 +143,16 @@ void main() {
|
|||
expect(bad, isA<FtpCertException>().having((x) => x.changed, 'changed', isTrue));
|
||||
}, skip: hasPy ? false : '시험용 파이썬 없음');
|
||||
|
||||
test('동영상 스트리밍 다리: ftp:// 경로의 부분 읽기 (FtpRemote.openReadFrom - REST 로 그 자리부터)', () async {
|
||||
await start('plain');
|
||||
FtpRegistry.configure([srv(security: 'none')]);
|
||||
await FtpRegistry.client('f1').write('/v.mkv', Stream.value(utf8.encode('0123456789')));
|
||||
Future<String> range(int start, int? length) async =>
|
||||
utf8.decode(await (await RemoteStreamBridge.openRange('ftp://f1/v.mkv', start, length)).expand((x) => x).toList());
|
||||
expect(await range(4, 3), '456');
|
||||
expect(await range(7, null), '789');
|
||||
}, skip: hasPy ? false : '시험용 파이썬 없음');
|
||||
|
||||
test('FTPS (암시, 990 꼴)', () async {
|
||||
await start('implicit');
|
||||
final c = FtpRemote(srv(security: 'implicit', cert: fp));
|
||||
|
|
|
|||
Loading…
Reference in New Issue