529103462c
Related Issues: - Samsung: Initial support for Motion Photos (#439) - Google: Initial support for Motion Photos (#1739) - Metadata: Flag Samsung/Google Motion Photos as Live Photos (#2788) Related Pull Requests: - Live Photos: Add Support for Samsung Motion Photos (#3588) - Samsung: Improved support for Motion Photos (#3660) - Google: Initial support for Motion Photos (#3709) - Google: Add support for Motion Photos (#3722) Signed-off-by: Michael Mayer <michael@photoprism.app>
100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
package video
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
|
)
|
|
|
|
// Reader reads an existing file from an offset until the end.
|
|
type Reader struct {
|
|
fileName string
|
|
file *os.File
|
|
offset int64
|
|
}
|
|
|
|
// NewReader creates a new Reader.
|
|
func NewReader(fileName string, offset int64) (*Reader, error) {
|
|
// Check if the file exists.
|
|
if !fs.FileExists(fileName) {
|
|
return nil, errors.New("file not found")
|
|
}
|
|
|
|
// Open file for reading.
|
|
file, err := os.Open(fileName)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Ensure that the offset is positive and we are starting at the right position.
|
|
if offset < 0 {
|
|
offset = 0
|
|
} else if offset > 0 {
|
|
if _, seekErr := file.Seek(offset, io.SeekStart); seekErr != nil {
|
|
_ = file.Close()
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Create new reader and return it.
|
|
return &Reader{fileName: fileName, file: file, offset: int64(offset)}, nil
|
|
}
|
|
|
|
// Read reads up to len(p) bytes into p. It returns the number of bytes read and any error encountered.
|
|
func (r *Reader) Read(p []byte) (n int, err error) {
|
|
return r.file.Read(p)
|
|
}
|
|
|
|
// Close closes the file after reading.
|
|
func (r *Reader) Close() error {
|
|
r.fileName = ""
|
|
r.offset = 0
|
|
return r.file.Close()
|
|
}
|
|
|
|
// ReadSeeker reads an existing file from an offset until the end.
|
|
type ReadSeeker struct {
|
|
file io.ReadSeeker
|
|
offset int64
|
|
}
|
|
|
|
// NewReadSeeker creates a new ReadSeeker.
|
|
func NewReadSeeker(file io.ReadSeeker, offset int64) *ReadSeeker {
|
|
// Ensure that the offset is positive.
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
return &ReadSeeker{
|
|
file: file,
|
|
offset: offset,
|
|
}
|
|
}
|
|
|
|
// Read reads up to len(p) bytes into p. It returns the number of bytes read and any error encountered.
|
|
func (r *ReadSeeker) Read(p []byte) (n int, err error) {
|
|
return r.file.Read(p)
|
|
}
|
|
|
|
// Seek sets the offset for the next Read or Write to offset, interpreted according to whence:
|
|
// - SeekStart means relative to the start of the file
|
|
// - SeekCurrent means relative to the current offset
|
|
// - SeekEnd means relative to the end (for example, offset = -2 specifies the penultimate byte of the file)
|
|
//
|
|
// Seek returns the new offset relative to the start of the file or an error, if any.
|
|
// Seeking to an offset before the start of the file is an error.
|
|
func (r *ReadSeeker) Seek(offset int64, whence int) (pos int64, err error) {
|
|
switch whence {
|
|
case io.SeekStart:
|
|
pos, err = r.file.Seek(offset+r.offset, whence)
|
|
case io.SeekCurrent, io.SeekEnd:
|
|
pos, err = r.file.Seek(offset, whence)
|
|
default:
|
|
return 0, errors.New("unknown whence")
|
|
}
|
|
|
|
return pos - r.offset, err
|
|
}
|