Skip to content

Commit 0ef56ac

Browse files
committed
fix #75
1 parent 2d8704b commit 0ef56ac

3 files changed

Lines changed: 65 additions & 81 deletions

File tree

SJMediaCacheServer.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'SJMediaCacheServer'
11-
s.version = '2.0.4'
11+
s.version = '2.0.5'
1212
s.summary = <<-DESC
1313
SJMediaCacheServer 是一个高效的 HTTP 媒体缓存框架,旨在代理媒体数据请求并优先提供缓存数据,从而减少网络流量并增强播放的流畅性。该框架支持两种类型的远程资源:基于文件的媒体,如 MP3、AAC、WAV、FLAC、OGG、MP4 和 MOV 等常见格式,以及 HLS(HTTP Live Streaming)流。它会自动解析 HLS 播放列表并代理各个媒体片段。
1414
DESC

SJMediaCacheServer/Core/Asset/HLS/HLSAssetParser.m

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -249,22 +249,22 @@ - (NSString *)hls_restoreOriginalUrl:(NSURL *)URL; // 将路径转换为原始ur
249249

250250
@implementation NSString (HLS)
251251
- (nullable NSString *)hls_findValueForAttribute:(NSString *)name startPos:(NSUInteger *)startPosPtr {
252-
NSArray<NSString *> *attributePairs = [self componentsSeparatedByString:@","];
253-
NSString *prefix = [name stringByAppendingString:@"="];
254-
__block NSUInteger startPos = 0;
255-
NSString *pair = [attributePairs mcs_firstOrNull:^BOOL(NSString * _Nonnull obj) {
256-
if ( [obj hasPrefix:prefix] ) return YES;
257-
startPos += obj.length + 1; // + @",".length();
258-
return NO;
252+
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"([A-Z0-9\\-]+)=((?:\"[^\"]+\")|(?:[^,]+))" options:kNilOptions error:NULL];
253+
NSArray<NSTextCheckingResult *> *results = [regex matchesInString:self options:kNilOptions range:NSMakeRange(0, self.length)];
254+
NSTextCheckingResult *result = [results mcs_firstOrNull:^BOOL(NSTextCheckingResult * _Nonnull obj) {
255+
NSString *matchedName = obj.numberOfRanges == 3 ? [self substringWithRange:[obj rangeAtIndex:1]] : nil;
256+
return matchedName && [matchedName isEqualToString:name];
259257
}];
260-
if ( pair != nil ) {
261-
startPos += prefix.length;
262-
NSString *value = [pair substringFromIndex:name.length + 1];
258+
259+
if ( result != nil ) {
260+
NSRange valueRange = [result rangeAtIndex:2];
261+
NSUInteger valuePos = valueRange.location;
262+
NSString *value = [self substringWithRange:valueRange];
263263
if ( [value hasPrefix:@"\""] ) { // 移除双引号;
264-
startPos += 1; // @"\"".length();
265-
value = [value stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]];
264+
valuePos += 1; // @"\"".length();
265+
value = [value substringWithRange:NSMakeRange(1, value.length - 2)];
266266
}
267-
if ( startPosPtr != NULL ) *startPosPtr = startPos;
267+
if ( startPosPtr != NULL ) *startPosPtr = valuePos;
268268
return value;
269269
}
270270
return nil;
@@ -285,53 +285,14 @@ - (BOOL)hls_toBOOL {
285285
}
286286

287287
- (NSString *)hls_restoreOriginalUrl:(NSURL *)resourceURL {
288-
static NSString *const HLS_PREFIX_LOCALHOST = @"http://localhost";
289-
static NSString *const HLS_PREFIX_DIR_ROOT = @"/";
290-
static NSString *const HLS_PREFIX_DIR_PARENT = @"../";
291-
static NSString *const HLS_PREFIX_DIR_CURRENT = @"./";
292-
293-
NSString *url = nil;
294-
/// /video/name.m3u8
295-
///
296-
if ( [self hasPrefix:HLS_PREFIX_DIR_ROOT] ) {
297-
NSURL *rootDir = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", resourceURL.scheme, resourceURL.host]];
298-
NSString *subpath = self;
299-
url = [rootDir mcs_URLByAppendingPathComponent:subpath].absoluteString;
300-
}
301-
/// ../video/name.m3u8
302-
/// ../../video/name.m3u8
303-
///
304-
else if ( [self hasPrefix:HLS_PREFIX_DIR_PARENT] ) {
305-
NSURL *curDir = resourceURL.mcs_URLByRemovingLastPathComponentAndQuery;
306-
NSURL *parentDir = curDir;
307-
NSString *subpath = self;
308-
while ( [subpath hasPrefix:HLS_PREFIX_DIR_PARENT] ) {
309-
parentDir = parentDir.mcs_URLByRemovingLastPathComponentAndQuery;
310-
subpath = [subpath substringFromIndex:HLS_PREFIX_DIR_PARENT.length];
288+
if ( [self containsString:@"://"] ) {
289+
NSString *localhost = @"http://localhost";
290+
if ( [self hasPrefix:localhost] ) {
291+
return [NSURL URLWithString:[self substringFromIndex:localhost.length] relativeToURL:resourceURL].absoluteString;
311292
}
312-
url = [parentDir mcs_URLByAppendingPathComponent:subpath].absoluteString;
313-
}
314-
/// ./video/name.m3u8
315-
else if ( [self hasPrefix:HLS_PREFIX_DIR_CURRENT] ) {
316-
NSURL *curDir = resourceURL.mcs_URLByRemovingLastPathComponentAndQuery;
317-
NSString *subpath = [self substringFromIndex:HLS_PREFIX_DIR_CURRENT.length];
318-
url = [curDir mcs_URLByAppendingPathComponent:subpath].absoluteString;
293+
return self;
319294
}
320-
/// http://localhost
321-
else if ( [self hasPrefix:HLS_PREFIX_LOCALHOST] ) {
322-
NSURL *rootDir = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", resourceURL.scheme, resourceURL.host]];
323-
NSString *subpath = [self substringFromIndex:HLS_PREFIX_LOCALHOST.length];
324-
url = [rootDir mcs_URLByAppendingPathComponent:subpath].absoluteString;
325-
}
326-
else if ( [self containsString:@"://"] ) {
327-
url = self;
328-
}
329-
else {
330-
NSURL *curDir = resourceURL.mcs_URLByRemovingLastPathComponentAndQuery;
331-
NSString *subpath = self;
332-
url = [curDir mcs_URLByAppendingPathComponent:subpath].absoluteString;
333-
}
334-
return url;
295+
return [NSURL URLWithString:self relativeToURL:resourceURL].absoluteString;
335296
}
336297
@end
337298

@@ -394,6 +355,7 @@ + (nullable NSString *)proxyPlaylistWithAsset:(NSString *)assetName
394355
HLSRenditionGroup *group = renditionGroups[key];
395356
if ( group == nil ) {
396357
group = [HLSRenditionGroup.alloc init];
358+
group.renditionType = rendition.renditionType;
397359
renditionGroups[key] = group;
398360
}
399361
[group addRendition:rendition];
@@ -493,13 +455,19 @@ + (NSString *)parse:(NSString *)playlist shouldTrim:(BOOL)shouldTrim parsingItem
493455
NSInteger lastLineIndex = lines.count - 1;
494456
__block NSUInteger offset = 0;
495457
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
458+
if ( shouldTrim && trimmedPlaylist.length != 0 ) [trimmedPlaylist appendString:@"\n"];
459+
496460
// 去除行首尾的空白字符; 每一行要么是一个标签(以 #EXT 开头), 要么是一个 URI
497-
NSString *trimmedLine = shouldTrim ? [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] : line;
461+
NSString *curLine = shouldTrim ? [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] : line;
498462
// 跳过空行和注释
499-
if ( [trimmedLine hasPrefix:@"#EXT"] ) [self handleTag:trimmedLine offset:offset parsingItems:parsingItems context:ctx]; // 这是一个标签行
500-
else if ( ![trimmedLine isEqualToString:@""] ) [self handleURI:trimmedLine offset:offset context:ctx]; // 这是一个 URI 行
501-
if ( shouldTrim ) idx != lastLineIndex ? [trimmedPlaylist appendFormat:@"%@\n", trimmedLine] : [trimmedPlaylist appendString:trimmedLine];
502-
offset += trimmedLine.length + 1; // + @"\n"
463+
BOOL isTag = [curLine hasPrefix:@"#EXT"];
464+
BOOL isComments = !isTag && [curLine hasPrefix:@"#"];
465+
BOOL isUri = !isTag && !isComments && curLine.length > 0;
466+
if ( isTag ) [self handleTag:curLine offset:offset parsingItems:parsingItems context:ctx]; // 这是一个标签行
467+
else if ( isUri ) [self handleURI:curLine offset:offset context:ctx]; // 这是一个 URI 行
468+
469+
if ( shouldTrim && !isComments ) [trimmedPlaylist appendString:curLine];
470+
offset = (shouldTrim ? trimmedPlaylist.length : (offset + curLine.length)) + 1; // + @"\n"
503471
}];
504472
return shouldTrim ? trimmedPlaylist : playlist;
505473
}
@@ -520,7 +488,7 @@ + (void)handleTag:(NSString *)tagLine offset:(NSUInteger)offset parsingItems:(NS
520488
/// #EXTINF:8.766667,
521489
/// #EXT-X-BYTERANGE:9194528@9662832
522490
/// segment.ts
523-
else if ( [tagLine hasPrefix:EXT_X_BYTERANGE] ) {
491+
else if ( [tagLine hasPrefix:EXT_X_BYTERANGE] ) {
524492
HLSSegment *segment = ctx[HLS_CTX_LAST_ITEM];
525493
NSParameterAssert([segment isKindOfClass:HLSSegment.class]);
526494
NSString *byteRange = [tagLine substringFromIndex:EXT_X_BYTERANGE.length];

SJMediaCacheServer/Core/Asset/MCSAssetManager.m

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,25 @@ - (NSInteger)countOfAllAssets {
8080
- (UInt64)countOfBytesNotIn:(nullable NSDictionary<MCSAssetTypeNumber *, NSArray<MCSAssetIDNumber *> *> *)assets {
8181
@synchronized (self) {
8282
__block UInt64 size = 0;
83-
[assets enumerateKeysAndObjectsUsingBlock:^(MCSAssetTypeNumber * _Nonnull key, NSArray<MCSAssetIDNumber *> * _Nonnull list, BOOL * _Nonnull stop) {
84-
for ( NSString *name in [self _queryAssetNamesInTableForType:key.integerValue assetsNotIn:list] ) {
85-
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
86-
}
87-
}];
83+
for ( NSString *name in [self _queryAssetNamesInTableForType:MCSAssetTypeFILE assetsNotIn:assets[@(MCSAssetTypeFILE)] ?: [NSArray arrayWithObject:@(0)]] ) {
84+
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
85+
}
86+
for ( NSString *name in [self _queryAssetNamesInTableForType:MCSAssetTypeHLS assetsNotIn:assets[@(MCSAssetTypeHLS)] ?: [NSArray arrayWithObject:@(0)]] ) {
87+
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
88+
}
8889
return size;
8990
}
9091
}
9192

9293
- (UInt64)countOfBytesIn:(nullable NSDictionary<MCSAssetTypeNumber *, NSArray<MCSAssetIDNumber *> *> *)assets {
9394
@synchronized (self) {
9495
__block UInt64 size = 0;
95-
[assets enumerateKeysAndObjectsUsingBlock:^(MCSAssetTypeNumber * _Nonnull key, NSArray<MCSAssetIDNumber *> * _Nonnull list, BOOL * _Nonnull stop) {
96-
for ( NSString *name in [self _queryAssetNamesInTableForType:key.integerValue assetsIn:list] ) {
97-
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
98-
}
99-
}];
96+
for ( NSString *name in [self _queryAssetNamesInTableForType:MCSAssetTypeFILE assetsIn:assets[@(MCSAssetTypeFILE)] ?: [NSArray arrayWithObject:@(0)]] ) {
97+
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
98+
}
99+
for ( NSString *name in [self _queryAssetNamesInTableForType:MCSAssetTypeHLS assetsIn:assets[@(MCSAssetTypeHLS)] ?: [NSArray arrayWithObject:@(0)]] ) {
100+
size += [NSFileManager.defaultManager mcs_directorySizeAtPath:[MCSRootDirectory assetPathForFilename:name]];
101+
}
100102
return size;
101103
}
102104
}
@@ -398,16 +400,30 @@ - (void)_syncUsageLogsRecursively {
398400
}
399401

400402
- (nullable NSArray<NSString *> *)_queryAssetNamesInTableForType:(MCSAssetType)type assetsIn:(NSArray<MCSAssetIDNumber *> *)assetIds {
401-
NSArray<NSDictionary *> *values = [mSqlite3 queryDataForClass:MCSAssetUsageLog.class resultColumns:@[@"name"] conditions:@[
402-
[SJSQLite3Condition.alloc initWithCondition:[NSString stringWithFormat:@"(assetType = %ld AND asset IN (%@))", type, assetIds]]
403-
] orderBy:nil error:NULL];
403+
NSString *assetTableName;
404+
switch (type) {
405+
case MCSAssetTypeFILE:
406+
assetTableName = @"FILEAsset";
407+
break;
408+
case MCSAssetTypeHLS:
409+
assetTableName = @"HLSAsset";
410+
break;
411+
}
412+
NSArray<SJSQLite3RowData *> *values = [mSqlite3 exec:[NSString stringWithFormat:@"SELECT name FROM %@ INNER JOIN MCSAssetUsageLog ON (MCSAssetUsageLog.assetType = %ld AND MCSAssetUsageLog.asset IN (%@)) WHERE MCSAssetUsageLog.asset = %@.id;", assetTableName, type, assetIds, assetTableName] error:NULL];
404413
return SJFoundationExtendedValuesForKey(@"name", values);
405414
}
406415

407416
- (nullable NSArray<NSString *> *)_queryAssetNamesInTableForType:(MCSAssetType)type assetsNotIn:(NSArray<MCSAssetIDNumber *> *)assetIds {
408-
NSArray<NSDictionary *> *values = [mSqlite3 queryDataForClass:MCSAssetUsageLog.class resultColumns:@[@"name"] conditions:@[
409-
[SJSQLite3Condition.alloc initWithCondition:[NSString stringWithFormat:@"(assetType = %ld AND asset NOT IN (%@))", type, assetIds]]
410-
] orderBy:nil error:NULL];
417+
NSString *assetTableName;
418+
switch (type) {
419+
case MCSAssetTypeFILE:
420+
assetTableName = @"FILEAsset";
421+
break;
422+
case MCSAssetTypeHLS:
423+
assetTableName = @"HLSAsset";
424+
break;
425+
}
426+
NSArray<SJSQLite3RowData *> *values = [mSqlite3 exec:[NSString stringWithFormat:@"SELECT name FROM %@ INNER JOIN MCSAssetUsageLog ON (MCSAssetUsageLog.assetType = %ld AND MCSAssetUsageLog.asset NOT IN (%@)) WHERE MCSAssetUsageLog.asset = %@.id;", assetTableName, type, assetIds, assetTableName] error:NULL];
411427
return SJFoundationExtendedValuesForKey(@"name", values);
412428
}
413429

0 commit comments

Comments
 (0)