diff --git a/scripts/commands/playlist/format.ts b/scripts/commands/playlist/format.ts index a7c69ee013..f38c797be4 100644 --- a/scripts/commands/playlist/format.ts +++ b/scripts/commands/playlist/format.ts @@ -4,11 +4,10 @@ import { Stream, Playlist } from '../../models' import { Storage } from '@freearhey/storage-js' import { STREAMS_DIR } from '../../constants' import { PlaylistParser } from '../../core' -import { getStreamInfo } from '../../utils' +import { getStoragePath, getStreamInfo } from '../../utils' import { loadData, data } from '../../api' import cliProgress from 'cli-progress' import { eachLimit } from 'async' -import path from 'node:path' import os from 'node:os' program @@ -42,7 +41,7 @@ async function main() { storage: streamsStorage }) let files = program.args.length ? program.args : await streamsStorage.list('**/*.m3u') - files = files.map((filepath: string) => path.basename(filepath)) + files = files.map((filepath: string) => getStoragePath(filepath, STREAMS_DIR)) let streams = await parser.parse(files) streams = streams.map((stream: Stream) => { stream.setGuides(data.guidesGroupedByStreamId.get(stream.getId())) diff --git a/scripts/utils.ts b/scripts/utils.ts index 094d4dd3e0..4674abaea0 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -53,6 +53,31 @@ export function normalizeURL(url: string): string { } } +function isInsideDirectory(rootDir: string, target: string): boolean { + const relative = path.relative(rootDir, target) + + return ( + relative !== '' && + relative !== '..' && + !relative.startsWith(`..${path.sep}`) && + !path.isAbsolute(relative) + ) +} + +export function getStoragePath(filepath: string, rootDir: string): string { + const root = path.resolve(rootDir) + const fromWorkspace = path.resolve(filepath) + const target = isInsideDirectory(root, fromWorkspace) + ? fromWorkspace + : path.resolve(root, filepath) + + if (!isInsideDirectory(root, target)) { + throw new Error(`Filepath "${filepath}" is outside the storage directory`) + } + + return path.relative(root, target).split(path.sep).join('/') +} + export function truncate(string: string, limit: number = 100) { if (!string) return string if (string.length < limit) return string diff --git a/tests/__data__/input/playlist_format/nested/nested.m3u b/tests/__data__/input/playlist_format/nested/nested.m3u new file mode 100644 index 0000000000..34fe6f1cce --- /dev/null +++ b/tests/__data__/input/playlist_format/nested/nested.m3u @@ -0,0 +1,5 @@ +#EXTM3U +#EXTINF:-1 tvg-id="",Zulu (720p) +https://example.com/zulu.m3u8 +#EXTINF:-1 tvg-id="",Alpha (720p) +https://example.com/alpha.m3u8 diff --git a/tests/commands/playlist/format.test.ts b/tests/commands/playlist/format.test.ts index dd36971028..2a01d0a887 100644 --- a/tests/commands/playlist/format.test.ts +++ b/tests/commands/playlist/format.test.ts @@ -31,6 +31,16 @@ describe('playlist:format', () => { }) }) + it('formats playlists in nested directories', () => { + const cmd = `${ENV_VAR} npm run playlist:format` + execSync(cmd, { encoding: 'utf8' }) + + const output = content('tests/__data__/output/streams/nested/nested.m3u') + expect(output).toContain(',Alpha (720p)') + expect(output).toContain(',Zulu (720p)') + expect(output.indexOf(',Alpha (720p)')).toBeLessThan(output.indexOf(',Zulu (720p)')) + }) + it('keeps links with malformed percent escapes', () => { fs.emptyDirSync('tests/__data__/output') fs.copySync('tests/__data__/input/playlist_normalize', 'tests/__data__/output/streams')