1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! # needle
//!
//! needle detects openings/intros and endings/credits across video files. needle can be used standalone
//! via a dedicated CLI, or as a library to implement higher-level tools or plugins (e.g., for intro skipping).
//!
//! The library exposes two central structs:
//!
//! 1. [Analyzer](crate::audio::Analyzer): Decodes one or more videos and converts them into a set of [FrameHashes](crate::audio::FrameHashes).
//! 2. [Comparator](crate::audio::Comparator): Searches for openings and endings across two or more videos.
//!
//! ## Basic Usage
//!
//! First, you need to create and run an [Analyzer](crate::audio::Analyzer).
//!
//! This will decode the audio streams for all provided video files and return a list of [FrameHashes](audio::FrameHashes), one per video. The structure
//! stores a compressed representation of the audio stream that contains _only_ the data we need to search for openings and endings.
//!
//! ```
//! use std::path::PathBuf;
//! use needle::audio::Analyzer;
//! # fn get_sample_paths() -> Vec<PathBuf> {
//! # let resources = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources");
//! # vec![
//! # resources.join("sample-5s.mp4"),
//! # resources.join("sample-shifted-4s.mp4"),
//! # ]
//! # }
//!
//! let video_paths: Vec<PathBuf> = get_sample_paths();
//! let analyzer = Analyzer::from_files(video_paths, false, false);
//!
//! // Use a `hash_period` of 1.0, `hash_duration` of 3.0, do not `persist` frame hash data
//! // and enable `threading`.
//! let frame_hashes = analyzer.run(1.0, 3.0, false, true).unwrap();
//! ```
//!
//! Now you need to create and run a [Comparator](crate::audio::Comparator) using the output [FrameHashes](audio::FrameHashes). You can re-use
//! the videos by constructing an instance from the [Analyzer](crate::audio::Analyzer).
//!
//! ```
//! use std::path::PathBuf;
//! use needle::audio::Comparator;
//! # use needle::audio::Analyzer;
//! # fn get_sample_paths() -> Vec<PathBuf> {
//! # let resources = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources");
//! # vec![
//! # resources.join("sample-5s.mp4"),
//! # resources.join("sample-shifted-4s.mp4"),
//! # ]
//! # }
//! # let video_paths: Vec<PathBuf> = get_sample_paths();
//! # let analyzer = Analyzer::from_files(video_paths, false, false);
//! # let frame_hashes = analyzer.run(1.0, 3.0, false, true).unwrap();
//!
//! let comparator: Comparator = analyzer.into();
//! let results = comparator.run_with_frame_hashes(frame_hashes, true, false, false, true).unwrap();
//!
//! dbg!(results);
//! // {
//! // "/tmp/land-of-lustrous-ep1.mkv": SearchResult {
//! // opening: None,
//! // ending: Some(
//! // (
//! // 1331.664387072s,
//! // 1419.024930474s,
//! // ),
//! // ),
//! // },
//! // "/tmp/land-of-lustrous-ep2.mkv": SearchResult {
//! // opening: Some(
//! // (
//! // 44.718820458s,
//! // 131.995463634s,
//! // ),
//! // ),
//! // ending: Some(
//! // (
//! // 1331.664387072s,
//! // 1436.560077708s,
//! // ),
//! // ),
//! // },
//! // "/tmp/land-of-lustrous-ep3.mkv": SearchResult {
//! // opening: Some(
//! // (
//! // 41.11111074s,
//! // 127.800452334s,
//! // ),
//! // ),
//! // ending: Some(
//! // (
//! // 1331.664387072s,
//! // 1436.560077708s,
//! // ),
//! // ),
//! // },
//! // },
//! ```
//!
//! [Comparator::run_with_frame_hashes](crate::audio::Comparator::run_with_frame_hashes) runs a search for openings and endings
//! using the provided frame hash data. Note that there is an equivalent method that can read existing frame hash data files from disk
//! ([Comparator::run](crate::audio::Comparator::run)).
//!
//! The output of this method is a map from each video file to a [SearchResult](crate::audio::SearchResult). This
//! structure contains the actual times for any detected openings and endings.
use PathBuf;
/// Detects opening and endings across videos using just audio streams.
/// Detects opening and endings across videos using just video streams.
/// Common utility functions.
/// Common error type.
/// Common result type.
pub type Result<T> = Result;