All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.alexeylapin.m3u8.parser.ParserUtils Maven / Gradle / Ivy

package com.github.alexeylapin.m3u8.parser;

import com.github.alexeylapin.m3u8.model.ByteRange;
import com.github.alexeylapin.m3u8.model.Channels;
import com.github.alexeylapin.m3u8.model.Resolution;
import com.github.alexeylapin.m3u8.model.ByteRange;
import com.github.alexeylapin.m3u8.model.Channels;
import com.github.alexeylapin.m3u8.model.Resolution;

import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class ParserUtils {
    static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
            .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
            .optionalStart().appendOffset("+HH", "Z").optionalEnd()
            .toFormatter();

    static final String YES = "YES";
    static final String NO = "NO";
    static final String CLIENT_ATTRIBUTE = "CLIENT-ATTRIBUTE";
    static final Pattern ATTRIBUTE_LIST_PATTERN = Pattern.compile("([A-Z0-9\\-]+)=(?:(?:\"([^\"]+)\")|([^,]+))");

    private static final Pattern BYTE_RANGE_PATTERN = Pattern.compile("(\\d+)(?:@(\\d+))?");

    static List split(String string, String delimiter) {
        return Arrays.asList(string.split(delimiter));
    }

    static boolean yesOrNo(String value) throws PlaylistParserException {
        switch (value) {
            case YES:
                return true;
            case NO:
                return false;
            default:
                throw new PlaylistParserException("Expected YES or NO, got " + value);
        }
    }

    static Resolution parseResolution(String string) throws PlaylistParserException {
        String[] fields = string.split("x");
        Resolution resolution;
        try {
            resolution = Resolution.of(Integer.parseInt(fields[0]), Integer.parseInt(fields[1]));
        } catch (NumberFormatException | IndexOutOfBoundsException e) {
            throw new PlaylistParserException("Invalid resolution: " + string);
        }
        return resolution;
    }

    static String writeResolution(Resolution resolution) {
        return resolution.width() + "x" + resolution.height();
    }

    static ByteRange parseByteRange(String attributes) throws PlaylistParserException {
        Matcher matcher = BYTE_RANGE_PATTERN.matcher(attributes);
        if (!matcher.matches()) {
            throw new PlaylistParserException("Invalid byte range " + attributes);
        }
        ByteRange.Builder byteRange = ByteRange.builder();
        byteRange.length(Long.parseLong(matcher.group(1)));
        if (matcher.group(2) != null) {
            byteRange.offset(Long.parseLong(matcher.group(2)));
        }
        return byteRange.build();
    }

    static String writeByteRange(ByteRange byteRange) {
        return byteRange.length() + byteRange.offset().map(offset -> "@" + offset).orElse("");
    }

    static Channels parseChannels(String attributes) throws PlaylistParserException {
        Channels.Builder builder = Channels.builder();
        String[] parameters = attributes.split("/");
        try {
            builder.count(Integer.parseInt(parameters[0]));
        } catch (NumberFormatException e) {
            throw new PlaylistParserException("Invalid channels: " + attributes);
        }
        if (parameters.length > 1) {
            builder.objectCodingIdentifiers(split(parameters[1], ","));
        }
        return builder.build();
    }

    static String writeChannels(Channels channels) {
        String attributes = String.valueOf(channels.count());
        if (!channels.objectCodingIdentifiers().isEmpty()) {
            attributes += "/" + String.join(",", channels.objectCodingIdentifiers());
        }
        return attributes;
    }

    static  Map toMap(T[] values, Function keyMapper) {
        Map map = new LinkedHashMap<>(values.length);
        for (T tag : values) {
            map.put(keyMapper.apply(tag), tag);
        }
        return map;
    }

    static > void readAttributes(
            Map attributeMap, String attributes, B builder, ParsingMode parsingMode) throws PlaylistParserException {

        Matcher matcher = ATTRIBUTE_LIST_PATTERN.matcher(attributes);

        while (matcher.find()) {
            String key = matcher.group(1);
            String value = matcher.group(2) != null ? matcher.group(2) : matcher.group(3);
            boolean clientAttribute = key.startsWith("X-");

            T attribute = attributeMap.get(clientAttribute ? CLIENT_ATTRIBUTE : key);

            if (attribute != null) {
                if (clientAttribute) {
                    attribute.read(builder, key, value);
                } else {
                    attribute.read(builder, value);
                }
            } else if (parsingMode.failOnUnknownAttributes()) {
                throw new PlaylistParserException("Unknown attribute: " + key);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy