Conversation
On reader close, save the on-screen position (read head minus the client's buffer) into the viewed record as a 0..1 byte-fraction of the file. Duration-free (no ffprobe): consumers multiply by media runtime. Gated by new BTSets fields SavePosition / BufferSizeMB / AutoBuffer; a preload/probe (read < buffer) is skipped. Reader gains startOffset (first Range-seek target) for future auto-buffer measurement.
TorrServer now records where playback actually was, so a position survives the player never reporting back - a TV powering off, an app being killed, or an external player that has no callback. While a stream runs and when it ends, the position is taken as the read head minus the client's buffer and stored in the viewed data in seconds, using the file's real duration from ffprobe (which the feature requires, and which the installers now offer to install). Offset, length and duration are stored alongside so clients can show progress without guessing the runtime. The client buffer is measured from how the player loads data: it fills as fast as it can, then settles to the playback rate, and the difference is the buffer. A configured size is used when that measurement is not available. Verified against a simulated player: within ~1.2s of the true on-screen position. Probes, preloads and short requests are ignored, and our own ffprobe stream is marked so probing can never recurse into itself. Marking a file as viewed no longer clears a stored position.
…able The buffer measurement now reports what it derived and from what (fill size and duration, measured playback rate), so the value can be reviewed after a session. The fallback size stays editable while automatic measurement is on, since it is what gets used until the measurement becomes available - roughly the first minute of playback. It is labelled as the fallback in that state.
…t sooner The buffer log was emitted for every stream, including the many short probe and preload requests that never reach the save, which buried the useful lines. It is now written where the value is actually used. The steady playback phase needed before the buffer can be derived drops from 60s to 30s: real sessions were ending before the measurement became available and falling back to the configured size.
Unexport setDuration and probeMarker, which are only used inside the torr package. Rename durationKey to fileKey, since both caches are keyed by it. Name the fallback buffer constant instead of repeating a literal, and report saving under the same [Position] prefix as the buffer measurement. Also drop the unreachable tail of ListViewed, which go vet flags in the file this branch rewrites, and rebuild the web assets with the locked dependency versions.
…rthy The playback rate is multiplied by the fill duration, so an error in the rate grows with however long filling took. A slow line fills slowly, which made the estimate noisy exactly where it is least affordable. The steady phase now has to last at least as long as the fill did, and never less than the previous 30s. Until then the configured size is used.
Filling was declared over at the first pause longer than two seconds, but a client tops its buffer up in bursts, so pauses are normal, and a brief network hiccup while filling looks exactly like one. That ended the measurement early and understated the buffer, which moves the saved position forward - past content the viewer had not reached. The buffer is how far the reader runs ahead of playback, so it is now taken as the largest such lead across sampled positions: bytes read beyond the start, minus what playback consumed in the same time. A hiccup simply is not the maximum, and neither is the low point of a top-up cycle. Playback speed comes from the tail of the session and is only trusted once two consecutive windows agree, which is what separates filling from playing at its own pace. Verified against a simulated player reporting its true on-screen position: within 1.5s of it.
|
I don't think you need a plugin for TrackTimecode anymore. The Lampa already supports that natively: yumata/lampa-source#367 |
The position used to be a byte fraction scaled by the ffprobe duration, which is a minute out on any file whose bitrate moves. Now the bytes streamed to the player are parsed on the way past for the timestamps the container carries (Matroska, MP4, MPEG-TS, MPEG-PS, FLV, AVI), building a byte-to-time index per file; the picture is the film at (read head - client buffer), and the buffer is measured from the fill at the start of a connection rather than taken from a setting. A reconnecting player inherits what the previous connection knew, so the position no longer jumps by a whole buffer when a paused client drops its socket. The byte-fraction estimate stays as the fallback for containers without timestamps, and the BufferSizeMB setting now only applies there. SmartTimecode can turn the new path off. Matroska: TimestampScale is read only from inside the Info element. Scanning the first megabytes for its three-byte id matched picture data on a real 4K episode and multiplied every timestamp by twelve and a half, which then poisoned the whole index for that file. The web UI shows the live position and measured buffer in the torrent card and details. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion # Conflicts: # server/torr/stream.go # server/web/pages/template/html.go # server/web/pages/template/pages/asset-manifest.json # server/web/pages/template/pages/index.html # server/web/pages/template/pages/static/js/2.f14cb17a.chunk.js.LICENSE.txt # server/web/pages/template/pages/static/js/2.f454f580.chunk.js.LICENSE.txt # server/web/pages/template/pages/static/js/2.f7ad277c.chunk.js.LICENSE.txt # server/web/pages/template/route.go # web/src/utils/Hosts.js
…und unused timeindex: the six container parsers each carried their own copy of the join-the-carry and keep-the-tail code, and two of them the collect-a-box-across-reads state machine. Both live in timeindex.go now (tail, collector), with the offset-before-reslice rule written once. The join reuses a scratch buffer instead of allocating a copy of every read, the emit closure is built once per Feeder rather than per read, and a timestamp past the end of the index is appended without the binary search — which is every timestamp on a forward stream. Frontier/Span and the dropped flag had no readers. torrstor/stream: min/max helpers replaced by the builtins; the unused keptUp field, watchFor constant, handover.head and the ffprobe status fields nothing in the UI read are gone; the local /play probe link is built in one place for both the saver and preload; the per-stream ticker only runs at the reckoning rate when there are timestamps to reckon with. web: one list comparator for the card's memo, comparing what the card shows rather than the read head, and one buffer-mark helper shared by the card and the readout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Обновление: позиция читается из меток времени контейнераВетка слита с текущим Что добавлено
Что исправлено
Что проверено
По поводу замечания о Lampa: спасибо, плагин действительно больше не нужен, раз Lampa поддерживает 🤖 Generated with Claude Code |
…ord back to timecode and duration The viewed record keeps only what a client cannot get elsewhere: the timecode and the real length of the file it was taken from, so progress can be shown against this file rather than a catalogue runtime. Offset and length are gone; records written with them are still read. SavePosition now implies TrackTimecode in the settings instead of a second check in the store. Reader.Playback() reads everything the tracker knows under one lock — anchor, head, picture, buffer, session — and the status poll and the saver take that snapshot instead of a dozen locked calls each. The reckoning is ticked from the torrent's own one-second timer for every reader of its cache, not from a goroutine per stream, so probes and preloads are treated like any other connection and the stream handler only saves. "Watched to the end" is decided in seconds against the duration rather than by an eight-megabyte margin in bytes. Durations and last-saved offsets are forgotten when a torrent closes. The status carries a viewing flag, so the UI shows the reader the saver would trust rather than guessing by session length. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Дисклеймер: код честно навайбкожен (как и описание далее и плагин для лампы). Решал свою давнюю боль (и успешно решил). Буду рад если данный механизм появится в апстриме (мердж ПР или кто-то решит переписать под схожую или более правильную логику)
Сохранение позиции воспроизведения
Внешний плеер, который закрылся штатно, сообщает, где остановился. Тот, который не закрылся —
телевизор выключился на паузе, Android убил приложение — не сообщает ничего, и позиция
теряется. Больнее всего на сериалах: серию потом приходится искать заново.
Здесь позиция записывается на стороне сервера, из того, что и так видно по ридеру.
Включается в Настройках → сохранять позицию воспроизведения. Нужен
ffprobe:установщики предлагают его поставить, а без него переключатель заблокирован с пояснением.
Включить достаточно одну галку.
TrackTimecode— флаг хранения таймкодов, доступный толькочерез API, — подразумевается автоматически, второй настройки искать не нужно.
Что и когда записывается
Пока файл отдаётся, ридер знает смещение в байтах, до которого он выдал данные клиенту. Это
не то место, где находится зритель: плееру отдано всё, что лежит у него в буфере, а смотрит
он позади конца этого буфера.
Смещение переводится в секунды по длительности файла — за этим и нужен
ffprobe. Результаткладётся в запись просмотра каждые 30 секунд и ещё раз при завершении потока.
От мусора в записи защищают три условия:
а не «почти концу» — иначе такое кино предлагалось бы продолжить.
Время меряется двумя способами, берётся большее. Как долго клиент держал поток, зависит от
управления потоком TCP; протяжённость реальных чтений — от того, как быстро торрент отдаёт
данные. Каждая величина по отдельности способна обмануть.
ffprobeспрашивается о файле один раз и не чаще, чем раз в десять минут, по одному процессуза раз. Он читает через тот же самый эндпоинт отдачи, поэтому его собственный запрос несёт
пометку, исключающую его из всего вышеописанного. Без этого замер запускал бы замер.
Как меряется буфер
Чтобы вычесть буфер, его надо знать, но не все плееры о нём не сообщают (или позволяют его настраивать). Зато за ним можно
понаблюдать.
Плеер набивает буфер настолько быстро, насколько позволяет сеть, а затем переходит к
запросам примерно с той скоростью, с какой потребляет данные. Значит на достаточно длинном
отрезке ридер движется со скоростью воспроизведения, а величина, на которую он опережает
зрителя, и есть буфер:
Скорость берётся с хвоста сессии, усредняется по 30-секундным окнам и принимается только
когда два соседних окна сходятся в пределах 40%. Иначе торрент, который подвис и потом
нагнал, был бы прочитан как медленный плеер.
Оценка идёт в дело лишь после того, как воспроизведение наблюдалось не меньше времени, чем
набирался отрыв: до этого отрыв неотличим от первоначального наполнения буфера. Результат
ограничен диапазоном от 4 МБ до 1 ГБ.
Замер против Vimu на 4K-ремуксе: 115 МБ при 100 МБ, выставленных в самом плеере — разница
это то, что плеер держит сверх собственной настройки.
Автозамер — отдельный переключатель, по умолчанию выключен. Когда он выключен или пока ему
не хватает данных, берётся заданное запасное значение (32 МБ, если не менять).
Плагин для Lampa
Вторая половина для пользователей Lampa: сервер хранит
позицию, плагин возвращает её в интерфейс.
https://github.com/s1mptom/lampa-ts-resume
Прямая ссылка для добавления в Lampa (Настройки → Плагины → по ссылке):
Плагин обращается только к тому адресу TorrServer, который уже прописан в настройках Lampa,
и ничего больше не требует.
Пара деталей, объясняющих, почему он вообще понадобился. Открытие торрента не поднимает в
Lampa активити — это модалка, — поэтому плагин цепляется к
Torserver.files(hash), черезкоторую проходят все пути в список файлов. И записывает он два ключа таймлайна: карточка
хеширует серию по оригинальному названию сериала, а список файлов торрента — по
оригинальному заголовку, и правка одного оставляет второй протухшим. Выглядит это как
полоска прогресса, которая сдвинулась на одном экране и не сдвинулась на другом.