/* This file is part of Libresonic. Libresonic is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Libresonic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Libresonic. If not, see <http://www.gnu.org/licenses/>. Copyright 2016 (C) Libresonic Authors Based upon Subsonic, Copyright 2009 (C) Sindre Mehus */ package org.libresonic.player.domain; import org.libresonic.player.util.BoundedList; import java.io.File; /** * Status for a single transfer (stream, download or upload). * * @author Sindre Mehus */ public class TransferStatus { private static final int HISTORY_LENGTH = 200; private static final long SAMPLE_INTERVAL_MILLIS = 5000; private Player player; private File file; private long bytesTransfered; private long bytesSkipped; private long bytesTotal; private final SampleHistory history = new SampleHistory(); private boolean terminated; private boolean active = true; /** * Return the number of bytes transferred. * * @return The number of bytes transferred. */ public synchronized long getBytesTransfered() { return bytesTransfered; } /** * Adds the given byte count to the total number of bytes transferred. * * @param byteCount The byte count. */ public synchronized void addBytesTransfered(long byteCount) { setBytesTransfered(bytesTransfered + byteCount); } /** * Sets the number of bytes transferred. * * @param bytesTransfered The number of bytes transferred. */ public synchronized void setBytesTransfered(long bytesTransfered) { this.bytesTransfered = bytesTransfered; createSample(bytesTransfered, false); } private void createSample(long bytesTransfered, boolean force) { long now = System.currentTimeMillis(); if (history.isEmpty()) { history.add(new Sample(bytesTransfered, now)); } else { Sample lastSample = history.getLast(); if (force || now - lastSample.getTimestamp() > TransferStatus.SAMPLE_INTERVAL_MILLIS) { history.add(new Sample(bytesTransfered, now)); } } } /** * Returns the number of milliseconds since the transfer status was last updated. * * @return Number of milliseconds, or <code>0</code> if never updated. */ public synchronized long getMillisSinceLastUpdate() { if (history.isEmpty()) { return 0L; } return System.currentTimeMillis() - history.getLast().timestamp; } /** * Returns the total number of bytes, or 0 if unknown. * * @return The total number of bytes, or 0 if unknown. */ public long getBytesTotal() { return bytesTotal; } /** * Sets the total number of bytes, or 0 if unknown. * * @param bytesTotal The total number of bytes, or 0 if unknown. */ public void setBytesTotal(long bytesTotal) { this.bytesTotal = bytesTotal; } /** * Returns the number of bytes that has been skipped (for instance when * resuming downloads). * * @return The number of skipped bytes. */ public synchronized long getBytesSkipped() { return bytesSkipped; } /** * Sets the number of bytes that has been skipped (for instance when * resuming downloads). * * @param bytesSkipped The number of skipped bytes. */ public synchronized void setBytesSkipped(long bytesSkipped) { this.bytesSkipped = bytesSkipped; } /** * Adds the given byte count to the total number of bytes skipped. * * @param byteCount The byte count. */ public synchronized void addBytesSkipped(long byteCount) { bytesSkipped += byteCount; } /** * Returns the file that is currently being transferred. * * @return The file that is currently being transferred. */ public synchronized File getFile() { return file; } /** * Sets the file that is currently being transferred. * * @param file The file that is currently being transferred. */ public synchronized void setFile(File file) { this.file = file; } /** * Returns the remote player for the stream. * * @return The remote player for the stream. */ public synchronized Player getPlayer() { return player; } /** * Sets the remote player for the stream. * * @param player The remote player for the stream. */ public synchronized void setPlayer(Player player) { this.player = player; } /** * Returns a history of samples for the stream * * @return A (copy of) the history list of samples. */ public synchronized SampleHistory getHistory() { return new SampleHistory(history); } /** * Returns the history length in milliseconds. * * @return The history length in milliseconds. */ public long getHistoryLengthMillis() { return TransferStatus.SAMPLE_INTERVAL_MILLIS * (TransferStatus.HISTORY_LENGTH - 1); } /** * Indicate that the stream should be terminated. */ public void terminate() { terminated = true; } /** * Returns whether this stream has been terminated. * Not that the <em>terminated status</em> is cleared by this method. * * @return Whether this stream has been terminated. */ public boolean terminated() { boolean result = terminated; terminated = false; return result; } /** * Returns whether this transfer is active, i.e., if the connection is still established. * * @return Whether this transfer is active. */ public boolean isActive() { return active; } /** * Sets whether this transfer is active, i.e., if the connection is still established. * * @param active Whether this transfer is active. */ public void setActive(boolean active) { this.active = active; if (active) { setBytesSkipped(0L); setBytesTotal(0L); setBytesTransfered(0L); } else { createSample(getBytesTransfered(), true); } } /** * A sample containing a timestamp and the number of bytes transferred up to that point in time. */ public static class Sample { private long bytesTransfered; private long timestamp; /** * Creates a new sample. * * @param bytesTransfered The total number of bytes transferred. * @param timestamp A point in time, in milliseconds. */ public Sample(long bytesTransfered, long timestamp) { this.bytesTransfered = bytesTransfered; this.timestamp = timestamp; } /** * Returns the number of bytes transferred. * * @return The number of bytes transferred. */ public long getBytesTransfered() { return bytesTransfered; } /** * Returns the timestamp of the sample. * * @return The timestamp in milliseconds. */ public long getTimestamp() { return timestamp; } } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TransferStatus-").append(hashCode()).append(" [player: ").append(player.getId()).append(", file: "); builder.append(file).append(", terminated: ").append(terminated).append(", active: ").append(active).append("]"); return builder.toString(); } /** * Contains recent history of samples. */ public static class SampleHistory extends BoundedList<Sample> { public SampleHistory() { super(HISTORY_LENGTH); } public SampleHistory(SampleHistory other) { super(HISTORY_LENGTH); addAll(other); } } }