예제 #1
0
파일: m3u.cpp 프로젝트: MellonQ/leechcraft
	Playlist Read2Sources (const QString& path)
	{
		QFile file (path);
		if (!file.open (QIODevice::ReadOnly))
		{
			qWarning () << Q_FUNC_INFO
					<< "unable to open"
					<< path
					<< file.errorString ();
			return {};
		}

		const auto& m3uDir = QFileInfo (path).absoluteDir ();

		QVariantMap lastMetadata;

		Playlist result;
		while (!file.atEnd ())
		{
			const auto& line = file.readLine ().trimmed ();
			if (line.startsWith ('#'))
			{
				const auto& pair = ParseMetadata (line);
				if (!pair.first.isEmpty ())
					lastMetadata [pair.first] = pair.second;
				continue;
			}

			const auto& url = QUrl::fromEncoded (line);
			auto src = QString::fromUtf8 (line);

			const auto mdGuard = std::shared_ptr<void> (nullptr,
					[&lastMetadata] (void*) { lastMetadata.clear (); });

#ifdef Q_OS_WIN32
			if (url.scheme ().size () > 1)
#else
			if (!url.scheme ().isEmpty ())
#endif
			{
				result.Append ({ url, lastMetadata });
				continue;
			}

			src.replace ('\\', '/');

			const QFileInfo fi (src);
			if (fi.isRelative ())
				src = m3uDir.absoluteFilePath (src);

			if (fi.suffix () == "m3u" || fi.suffix () == "m3u8")
				result += Read2Sources (src);
			else
				result.Append ({ src, lastMetadata });
		}
		return result;
	}
예제 #2
0
SongList M3UParser::Load(QIODevice* device, const QString& playlist_path,
                         const QDir& dir) const {
  SongList ret;

  M3UType type = STANDARD;
  Metadata current_metadata;

  QString data = QString::fromUtf8(device->readAll());
  data.replace('\r', '\n');
  data.replace("\n\n", "\n");
  QByteArray bytes = data.toUtf8();
  QBuffer buffer(&bytes);
  buffer.open(QIODevice::ReadOnly);

  QString line = QString::fromUtf8(buffer.readLine()).trimmed();
  if (line.startsWith("#EXTM3U")) {
    // This is in extended M3U format.
    type = EXTENDED;
    line = QString::fromUtf8(buffer.readLine()).trimmed();
  }

  forever {
    if (line.startsWith('#')) {
      // Extended info or comment.
      if (type == EXTENDED && line.startsWith("#EXT")) {
        if (!ParseMetadata(line, &current_metadata)) {
          qLog(Warning) << "Failed to parse metadata: " << line;
        }
      }
    } else if (!line.isEmpty()) {
      Song song = LoadSong(line, 0, dir);
      if (!current_metadata.title.isEmpty()) {
        song.set_title(current_metadata.title);
      }
      if (!current_metadata.artist.isEmpty()) {
        song.set_artist(current_metadata.artist);
      }
      if (current_metadata.length > 0) {
        song.set_length_nanosec(current_metadata.length);
      }
      ret << song;

      current_metadata = Metadata();
    }
    if (buffer.atEnd()) {
      break;
    }
    line = QString::fromUtf8(buffer.readLine()).trimmed();
  }

  return ret;
}
예제 #3
0
nsresult
CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
                              nsresult aResult)
{
  LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
       this, aHandle, aResult));

  MOZ_ASSERT(mListener);

  nsresult rv, retval;
  nsCOMPtr<CacheFileMetadataListener> listener;

  if (NS_FAILED(aResult)) {
    LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
         ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult));

    InitEmptyMetadata();
    retval = NS_OK;

    mListener.swap(listener);
    listener->OnMetadataRead(retval);
    return NS_OK;
  }

  // check whether we have read all necessary data
  uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize -
                                                  sizeof(uint32_t));

  int64_t size = mHandle->FileSize();
  MOZ_ASSERT(size != -1);

  if (realOffset >= size) {
    LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
         "empty metadata. [this=%p, realOffset=%d, size=%lld]", this,
         realOffset, size));

    InitEmptyMetadata();
    retval = NS_OK;

    mListener.swap(listener);
    listener->OnMetadataRead(retval);
    return NS_OK;
  }

  uint32_t usedOffset = size - mBufSize;

  if (realOffset < usedOffset) {
    uint32_t missing = usedOffset - realOffset;
    // we need to read more data
    mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing));
    memmove(mBuf + missing, mBuf, mBufSize);
    mBufSize += missing;

    DoMemoryReport(MemoryUsage());

    LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
         "have full metadata. [this=%p]", missing, this));

    rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this);
    if (NS_FAILED(rv)) {
      LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
           "failed synchronously, creating empty metadata. [this=%p, "
           "rv=0x%08x]", this, rv));

      InitEmptyMetadata();
      retval = NS_OK;

      mListener.swap(listener);
      listener->OnMetadataRead(retval);
      return NS_OK;
    }

    return NS_OK;
  }

  // We have all data according to offset information at the end of the entry.
  // Try to parse it.
  rv = ParseMetadata(realOffset, realOffset - usedOffset, true);
  if (NS_FAILED(rv)) {
    LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
         "empty metadata. [this=%p]", this));
    InitEmptyMetadata();
    retval = NS_OK;
  }
  else {
    retval = NS_OK;
  }

  mListener.swap(listener);
  listener->OnMetadataRead(retval);

  return NS_OK;
}
예제 #4
0
nsresult
CacheFileMetadata::SyncReadMetadata(nsIFile *aFile)
{
  LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this));

  MOZ_ASSERT(!mListener);
  MOZ_ASSERT(!mHandle);
  MOZ_ASSERT(!mHashArray);
  MOZ_ASSERT(!mBuf);
  MOZ_ASSERT(!mWriteBuf);
  MOZ_ASSERT(mKey.IsEmpty());

  nsresult rv;

  int64_t fileSize;
  rv = aFile->GetFileSize(&fileSize);
  if (NS_FAILED(rv)) {
    // Don't bloat the console
    return rv;
  }

  PRFileDesc *fd;
  rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd);
  NS_ENSURE_SUCCESS(rv, rv);

  int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET);
  if (offset == -1) {
    PR_Close(fd);
    return NS_ERROR_FAILURE;
  }

  uint32_t metaOffset;
  int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t));
  if (bytesRead != sizeof(uint32_t)) {
    PR_Close(fd);
    return NS_ERROR_FAILURE;
  }

  metaOffset = NetworkEndian::readUint32(&metaOffset);
  if (metaOffset > fileSize) {
    PR_Close(fd);
    return NS_ERROR_FAILURE;
  }

  mBufSize = fileSize - metaOffset;
  mBuf = static_cast<char *>(moz_xmalloc(mBufSize));

  DoMemoryReport(MemoryUsage());

  offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET);
  if (offset == -1) {
    PR_Close(fd);
    return NS_ERROR_FAILURE;
  }

  bytesRead = PR_Read(fd, mBuf, mBufSize);
  PR_Close(fd);
  if (bytesRead != static_cast<int32_t>(mBufSize)) {
    return NS_ERROR_FAILURE;
  }

  rv = ParseMetadata(metaOffset, 0, false);
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}
예제 #5
0
nsresult
CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
                              nsresult aResult)
{
  LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
       this, aHandle, aResult));

  MOZ_ASSERT(mListener);

  nsresult rv;
  nsCOMPtr<CacheFileMetadataListener> listener;

  if (NS_FAILED(aResult)) {
    LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
         ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult));

    InitEmptyMetadata();

    mListener.swap(listener);
    listener->OnMetadataRead(NS_OK);
    return NS_OK;
  }

  if (mFirstRead) {
    Telemetry::AccumulateTimeDelta(
      Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_TIME_MS, mReadStart);
    Telemetry::Accumulate(
      Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_SIZE, mBufSize);
  } else {
    Telemetry::AccumulateTimeDelta(
      Telemetry::NETWORK_CACHE_METADATA_SECOND_READ_TIME_MS, mReadStart);
  }

  // check whether we have read all necessary data
  uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize -
                                                  sizeof(uint32_t));

  int64_t size = mHandle->FileSize();
  MOZ_ASSERT(size != -1);

  if (realOffset >= size) {
    LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating "
         "empty metadata. [this=%p, realOffset=%u, size=%lld]", this,
         realOffset, size));

    InitEmptyMetadata();

    mListener.swap(listener);
    listener->OnMetadataRead(NS_OK);
    return NS_OK;
  }

  uint32_t maxHashCount = size / kChunkSize;
  uint32_t maxMetadataSize = CalcMetadataSize(kMaxElementsSize, maxHashCount);
  if (size - realOffset > maxMetadataSize) {
    LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, metadata would "
         "be too big, creating empty metadata. [this=%p, realOffset=%u, "
         "maxMetadataSize=%u, size=%lld]", this, realOffset, maxMetadataSize,
         size));

    InitEmptyMetadata();

    mListener.swap(listener);
    listener->OnMetadataRead(NS_OK);
    return NS_OK;
  }

  uint32_t usedOffset = size - mBufSize;

  if (realOffset < usedOffset) {
    uint32_t missing = usedOffset - realOffset;
    // we need to read more data
    char *newBuf = static_cast<char *>(realloc(mBuf, mBufSize + missing));
    if (!newBuf) {
      LOG(("CacheFileMetadata::OnDataRead() - Error allocating %d more bytes "
           "for the missing part of the metadata, creating empty metadata. "
           "[this=%p]", missing, this));

      InitEmptyMetadata();

      mListener.swap(listener);
      listener->OnMetadataRead(NS_OK);
      return NS_OK;
    }

    mBuf = newBuf;
    memmove(mBuf + missing, mBuf, mBufSize);
    mBufSize += missing;

    DoMemoryReport(MemoryUsage());

    LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
         "have full metadata. [this=%p]", missing, this));

    mFirstRead = false;
    mReadStart = mozilla::TimeStamp::Now();
    rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, this);
    if (NS_FAILED(rv)) {
      LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() "
           "failed synchronously, creating empty metadata. [this=%p, "
           "rv=0x%08x]", this, rv));

      InitEmptyMetadata();

      mListener.swap(listener);
      listener->OnMetadataRead(NS_OK);
      return NS_OK;
    }

    return NS_OK;
  }

  Telemetry::Accumulate(Telemetry::NETWORK_CACHE_METADATA_SIZE,
                        size - realOffset);

  // We have all data according to offset information at the end of the entry.
  // Try to parse it.
  rv = ParseMetadata(realOffset, realOffset - usedOffset, true);
  if (NS_FAILED(rv)) {
    LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
         "empty metadata. [this=%p]", this));
    InitEmptyMetadata();
  } else {
    // Shrink elements buffer.
    mBuf = static_cast<char *>(moz_xrealloc(mBuf, mElementsSize));
    mBufSize = mElementsSize;

    // There is usually no or just one call to SetMetadataElement() when the
    // metadata is parsed from disk. Avoid allocating power of two sized buffer
    // which we do in case of newly created metadata.
    mAllocExactSize = true;
  }

  mListener.swap(listener);
  listener->OnMetadataRead(NS_OK);

  return NS_OK;
}