diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/playlists/PlayListM3U.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/playlists/PlayListM3U.cpp')
-rw-r--r-- | xbmc/playlists/PlayListM3U.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/xbmc/playlists/PlayListM3U.cpp b/xbmc/playlists/PlayListM3U.cpp new file mode 100644 index 0000000..d39cdce --- /dev/null +++ b/xbmc/playlists/PlayListM3U.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PlayListM3U.h" + +#include "FileItem.h" +#include "URL.h" +#include "Util.h" +#include "filesystem/File.h" +#include "music/tags/MusicInfoTag.h" +#include "utils/CharsetConverter.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "video/VideoInfoTag.h" + +#include <inttypes.h> + +using namespace PLAYLIST; +using namespace XFILE; + +const char* CPlayListM3U::StartMarker = "#EXTCPlayListM3U::M3U"; +const char* CPlayListM3U::InfoMarker = "#EXTINF"; +const char* CPlayListM3U::ArtistMarker = "#EXTART"; +const char* CPlayListM3U::AlbumMarker = "#EXTALB"; +const char* CPlayListM3U::PropertyMarker = "#KODIPROP"; +const char* CPlayListM3U::VLCOptMarker = "#EXTVLCOPT"; +const char* CPlayListM3U::StreamMarker = "#EXT-X-STREAM-INF"; +const char* CPlayListM3U::BandwidthMarker = "BANDWIDTH"; +const char* CPlayListM3U::OffsetMarker = "#EXT-KX-OFFSET"; + +// example m3u file: +// #EXTM3U +// #EXTART:Demo Artist +// #EXTALB:Demo Album +// #KODIPROP:name=value +// #EXTINF:5,demo +// E:\Program Files\Winamp3\demo.mp3 + + + +// example m3u8 containing streams of different bitrates +// #EXTM3U +// #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1600000 +// playlist_1600.m3u8 +// #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=3000000 +// playlist_3000.m3u8 +// #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=800000 +// playlist_800.m3u8 + + +CPlayListM3U::CPlayListM3U(void) = default; + +CPlayListM3U::~CPlayListM3U(void) = default; + + +bool CPlayListM3U::Load(const std::string& strFileName) +{ + char szLine[4096]; + std::string strLine; + std::string strInfo; + std::vector<std::pair<std::string, std::string> > properties; + + int lDuration = 0; + int iStartOffset = 0; + int iEndOffset = 0; + + Clear(); + + m_strPlayListName = URIUtils::GetFileName(strFileName); + URIUtils::GetParentPath(strFileName, m_strBasePath); + + CFile file; + if (!file.Open(strFileName) ) + { + file.Close(); + return false; + } + + while (file.ReadString(szLine, 4095)) + { + strLine = szLine; + StringUtils::Trim(strLine); + + if (StringUtils::StartsWith(strLine, InfoMarker)) + { + // start of info + size_t iColon = strLine.find(':'); + size_t iComma = strLine.find(','); + if (iColon != std::string::npos && + iComma != std::string::npos && + iComma > iColon) + { + // Read the info and duration + iColon++; + std::string strLength = strLine.substr(iColon, iComma - iColon); + lDuration = atoi(strLength.c_str()); + iComma++; + strInfo = strLine.substr(iComma); + g_charsetConverter.unknownToUTF8(strInfo); + } + } + else if (StringUtils::StartsWith(strLine, OffsetMarker)) + { + size_t iColon = strLine.find(':'); + size_t iComma = strLine.find(','); + if (iColon != std::string::npos && + iComma != std::string::npos && + iComma > iColon) + { + // Read the start and end offset + iColon++; + iStartOffset = atoi(strLine.substr(iColon, iComma - iColon).c_str()); + iComma++; + iEndOffset = atoi(strLine.substr(iComma).c_str()); + } + } + else if (StringUtils::StartsWith(strLine, PropertyMarker) + || StringUtils::StartsWith(strLine, VLCOptMarker)) + { + size_t iColon = strLine.find(':'); + size_t iEqualSign = strLine.find('='); + if (iColon != std::string::npos && + iEqualSign != std::string::npos && + iEqualSign > iColon) + { + std::string strFirst, strSecond; + properties.emplace_back( + StringUtils::Trim((strFirst = strLine.substr(iColon + 1, iEqualSign - iColon - 1))), + StringUtils::Trim((strSecond = strLine.substr(iEqualSign + 1)))); + } + } + else if (strLine != StartMarker && + !StringUtils::StartsWith(strLine, ArtistMarker) && + !StringUtils::StartsWith(strLine, AlbumMarker)) + { + std::string strFileName = strLine; + + if (!strFileName.empty() && strFileName[0] == '#') + continue; // assume a comment or something else we don't support + + // Skip self - do not load playlist recursively + // We compare case-less in case user has input incorrect case of the current playlist + if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strFileName), m_strPlayListName)) + continue; + + if (strFileName.length() > 0) + { + g_charsetConverter.unknownToUTF8(strFileName); + + // If no info was read from from the extended tag information, use the file name + if (strInfo.length() == 0) + { + strInfo = URIUtils::GetFileName(strFileName); + } + + // should substitution occur before or after charset conversion?? + strFileName = URIUtils::SubstitutePath(strFileName); + + // Get the full path file name and add it to the the play list + CUtil::GetQualifiedFilename(m_strBasePath, strFileName); + CFileItemPtr newItem(new CFileItem(strInfo)); + newItem->SetPath(strFileName); + if (iStartOffset != 0 || iEndOffset != 0) + { + newItem->SetStartOffset(iStartOffset); + newItem->m_lStartPartNumber = 1; + newItem->SetProperty("item_start", iStartOffset); + newItem->SetEndOffset(iEndOffset); + // Prevent load message from file and override offset set here + newItem->GetMusicInfoTag()->SetLoaded(); + newItem->GetMusicInfoTag()->SetTitle(strInfo); + if (iEndOffset) + lDuration = static_cast<int>(CUtil::ConvertMilliSecsToSecsIntRounded(iEndOffset - iStartOffset)); + } + if (newItem->IsVideo() && !newItem->HasVideoInfoTag()) // File is a video and needs a VideoInfoTag + newItem->GetVideoInfoTag()->Reset(); // Force VideoInfoTag creation + if (lDuration && newItem->IsAudio()) + newItem->GetMusicInfoTag()->SetDuration(lDuration); + for (auto &prop : properties) + { + newItem->SetProperty(prop.first, prop.second); + } + + newItem->SetMimeType(newItem->GetProperty("mimetype").asString()); + if (!newItem->GetMimeType().empty()) + newItem->SetContentLookup(false); + + Add(newItem); + + // Reset the values just in case there part of the file have the extended marker + // and part don't + strInfo = ""; + lDuration = 0; + iStartOffset = 0; + iEndOffset = 0; + properties.clear(); + } + } + } + + file.Close(); + return true; +} + +void CPlayListM3U::Save(const std::string& strFileName) const +{ + if (!m_vecItems.size()) + return; + std::string strPlaylist = CUtil::MakeLegalPath(strFileName); + CFile file; + if (!file.OpenForWrite(strPlaylist,true)) + { + CLog::Log(LOGERROR, "Could not save M3U playlist: [{}]", strPlaylist); + return; + } + std::string strLine = StringUtils::Format("{}\n", StartMarker); + if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size())) + return; // error + + for (int i = 0; i < (int)m_vecItems.size(); ++i) + { + CFileItemPtr item = m_vecItems[i]; + std::string strDescription=item->GetLabel(); + g_charsetConverter.utf8ToStringCharset(strDescription); + strLine = StringUtils::Format("{}:{},{}\n", InfoMarker, + item->GetMusicInfoTag()->GetDuration(), strDescription); + if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size())) + return; // error + if (item->GetStartOffset() != 0 || item->GetEndOffset() != 0) + { + strLine = StringUtils::Format("{}:{},{}\n", OffsetMarker, item->GetStartOffset(), + item->GetEndOffset()); + file.Write(strLine.c_str(),strLine.size()); + } + std::string strFileName = ResolveURL(item); + g_charsetConverter.utf8ToStringCharset(strFileName); + strLine = StringUtils::Format("{}\n", strFileName); + if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size())) + return; // error + } + file.Close(); +} + +std::map< std::string, std::string > CPlayListM3U::ParseStreamLine(const std::string &streamLine) +{ + std::map< std::string, std::string > params; + + // ensure the line has something beyond the stream marker and ':' + if (streamLine.size() < strlen(StreamMarker) + 2) + return params; + + // get the actual params following the : + std::string strParams(streamLine.substr(strlen(StreamMarker) + 1)); + + // separate the parameters + std::vector<std::string> vecParams = StringUtils::Split(strParams, ","); + for (std::vector<std::string>::iterator i = vecParams.begin(); i != vecParams.end(); ++i) + { + // split the param, ensure there was an = + StringUtils::Trim(*i); + std::vector<std::string> vecTuple = StringUtils::Split(*i, "="); + if (vecTuple.size() < 2) + continue; + + // remove white space from name and value and store it in the dictionary + StringUtils::Trim(vecTuple[0]); + StringUtils::Trim(vecTuple[1]); + params[vecTuple[0]] = vecTuple[1]; + } + + return params; +} + |