@chronicle.app/youtube 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/auth/GoogleOAuthProvider.d.ts +14 -0
- package/dist/auth/GoogleOAuthProvider.js +15 -0
- package/dist/connectors/YouTubeBaseExtractor.d.ts +36 -0
- package/dist/connectors/YouTubeBaseExtractor.js +40 -0
- package/dist/connectors/YouTubeLikesExtractor.d.ts +17 -0
- package/dist/connectors/YouTubeLikesExtractor.js +54 -0
- package/dist/connectors/YouTubePlaylistsExtractor.d.ts +16 -0
- package/dist/connectors/YouTubePlaylistsExtractor.js +44 -0
- package/dist/connectors/YouTubeSubscriptionsExtractor.d.ts +10 -0
- package/dist/connectors/YouTubeSubscriptionsExtractor.js +27 -0
- package/dist/connectors/YouTubeTakeoutExtractor.d.ts +92 -0
- package/dist/connectors/YouTubeTakeoutExtractor.js +315 -0
- package/dist/connectors/YouTubeTakeoutTransformer.d.ts +27 -0
- package/dist/connectors/YouTubeTakeoutTransformer.js +260 -0
- package/dist/connectors/YouTubeTransformer.d.ts +15 -0
- package/dist/connectors/YouTubeTransformer.js +220 -0
- package/dist/connectors/YouTubeUploadsExtractor.d.ts +11 -0
- package/dist/connectors/YouTubeUploadsExtractor.js +50 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +12 -0
- package/dist/takeout/csv.d.ts +4 -0
- package/dist/takeout/csv.js +12 -0
- package/dist/takeout/parseActivityHtml.d.ts +30 -0
- package/dist/takeout/parseActivityHtml.js +116 -0
- package/dist/utils/YouTubeProxy.d.ts +138 -0
- package/dist/utils/YouTubeProxy.js +138 -0
- package/dist/utils/time.d.ts +12 -0
- package/dist/utils/time.js +30 -0
- package/dist/utils/urls.d.ts +10 -0
- package/dist/utils/urls.js +15 -0
- package/package.json +92 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrew Louis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Chronicle YouTube Plugin
|
|
2
|
+
|
|
3
|
+
Extract liked videos and channel subscriptions from YouTube via the Data API v3 with OAuth authentication.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Liked Videos**: Extract your liked videos (from the account's likes playlist, with like timestamps)
|
|
8
|
+
- **Subscriptions**: Extract your channel subscriptions (with subscribe timestamps)
|
|
9
|
+
- **Playlists**: Extract your own playlists and their videos (creation and item-add timestamps, privacy status)
|
|
10
|
+
- **Uploads**: Extract videos published to your own channel
|
|
11
|
+
- **OAuth Authentication**: Google OAuth 2.0 with offline refresh tokens
|
|
12
|
+
|
|
13
|
+
Watch and search history are not exposed by the Data API (Google Takeout only) and are out of scope for this plugin.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
### 1. Create Google OAuth credentials
|
|
18
|
+
|
|
19
|
+
1. Go to the [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
|
|
20
|
+
2. Create (or reuse) a project and enable the **YouTube Data API v3**
|
|
21
|
+
3. Create an OAuth client ID (web application)
|
|
22
|
+
4. Note your **Client ID** and **Client Secret**
|
|
23
|
+
5. Add Chronicle's callback to the authorized redirect URIs
|
|
24
|
+
|
|
25
|
+
### 2. Authenticate
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
chronicle auth login youtube --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This opens your browser for Google consent and stores the tokens (including the offline refresh token) in Chronicle's credential store. Access tokens expire after an hour and are refreshed automatically.
|
|
32
|
+
|
|
33
|
+
### 3. Extract Data
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Liked videos (default extraction type)
|
|
37
|
+
chronicle extract youtube --type likes
|
|
38
|
+
|
|
39
|
+
# Subscriptions
|
|
40
|
+
chronicle extract youtube --type subscriptions
|
|
41
|
+
|
|
42
|
+
# Playlists and uploads
|
|
43
|
+
chronicle extract youtube --type playlists
|
|
44
|
+
chronicle extract youtube --type uploads
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Data Mapping
|
|
48
|
+
|
|
49
|
+
- Liked video → `LikeAction` by the account owner (`@me`) on a `VideoObject` (title, description, duration, thumbnail, uploading channel as author)
|
|
50
|
+
- Subscription → `FollowAction` on the channel as an `Agent`, keyed on the channel id so re-runs supersede
|
|
51
|
+
- Playlist → `Collection` (with `visibility` from the privacy status), created via a `CreateAction` at the playlist's real creation time; each video in it → `AddAction` keyed on the playlist item id with the `Collection` as `targetCollection`
|
|
52
|
+
- Upload → `PublishAction` with the account owner as agent and the `VideoObject` as result
|
|
53
|
+
- Channels are `Agent`s keyed on their YouTube channel id
|
|
54
|
+
|
|
55
|
+
Tests run the pipeline against a fake Data API served on 127.0.0.1 with a fake
|
|
56
|
+
access token, and against a small synthetic Takeout folder in a temporary
|
|
57
|
+
directory. They never call Google, read stored credentials, or read the host's
|
|
58
|
+
data or accounts.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { OAuthProvider } from '@chronicle.app/auth';
|
|
2
|
+
export declare class GoogleOAuthProvider extends OAuthProvider {
|
|
3
|
+
static providerId: string;
|
|
4
|
+
static authorizationUrl: string;
|
|
5
|
+
static tokenUrl: string;
|
|
6
|
+
static scopes: string[];
|
|
7
|
+
static requiresClientSecret: boolean;
|
|
8
|
+
static extraAuthParams: {
|
|
9
|
+
access_type: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
include_granted_scopes: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=GoogleOAuthProvider.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { OAuthProvider } from '@chronicle.app/auth';
|
|
2
|
+
export class GoogleOAuthProvider extends OAuthProvider {
|
|
3
|
+
static providerId = 'youtube';
|
|
4
|
+
static authorizationUrl = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
5
|
+
static tokenUrl = 'https://oauth2.googleapis.com/token';
|
|
6
|
+
static scopes = ['https://www.googleapis.com/auth/youtube.readonly'];
|
|
7
|
+
static requiresClientSecret = true;
|
|
8
|
+
// Without offline access + forced consent Google issues no refresh token,
|
|
9
|
+
// leaving the stored credential unable to outlive the first hour.
|
|
10
|
+
static extraAuthParams = {
|
|
11
|
+
access_type: 'offline',
|
|
12
|
+
prompt: 'consent',
|
|
13
|
+
include_granted_scopes: 'true',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Extractor, Record } from '@chronicle.app/etl';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import YouTubeTransformer from './YouTubeTransformer.js';
|
|
4
|
+
import YouTubeProxy, { YouTubeSelfChannel } from '../utils/YouTubeProxy.js';
|
|
5
|
+
export declare abstract class YouTubeBaseExtractor<T extends typeof YouTubeBaseExtractor> extends Extractor<T> {
|
|
6
|
+
static source: string;
|
|
7
|
+
static description: string;
|
|
8
|
+
static delivery: "api";
|
|
9
|
+
static strategy: string;
|
|
10
|
+
static defaultTransformer: typeof YouTubeTransformer;
|
|
11
|
+
static schema: z.ZodObject<{
|
|
12
|
+
since: z.ZodOptional<z.ZodDate>;
|
|
13
|
+
until: z.ZodOptional<z.ZodDate>;
|
|
14
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
} & {
|
|
16
|
+
accessToken: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
limit?: number | undefined;
|
|
19
|
+
accessToken?: string | undefined;
|
|
20
|
+
since?: Date | undefined;
|
|
21
|
+
until?: Date | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
limit?: number | undefined;
|
|
24
|
+
accessToken?: string | undefined;
|
|
25
|
+
since?: Date | undefined;
|
|
26
|
+
until?: Date | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
protected proxy: YouTubeProxy;
|
|
29
|
+
protected user?: YouTubeSelfChannel;
|
|
30
|
+
static hasValidCredentials(): Promise<boolean>;
|
|
31
|
+
setup(): Promise<void>;
|
|
32
|
+
protected createRecordWithUser(data: any, options: {
|
|
33
|
+
recordType: string;
|
|
34
|
+
}): Record;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=YouTubeBaseExtractor.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Extractor } from '@chronicle.app/etl';
|
|
2
|
+
import { TokenHelper } from '@chronicle.app/auth';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import YouTubeTransformer from './YouTubeTransformer.js';
|
|
5
|
+
import YouTubeProxy from '../utils/YouTubeProxy.js';
|
|
6
|
+
export class YouTubeBaseExtractor extends Extractor {
|
|
7
|
+
static source = 'youtube';
|
|
8
|
+
static description = 'Base extractor for YouTube data';
|
|
9
|
+
static delivery = 'api';
|
|
10
|
+
static strategy = 'api';
|
|
11
|
+
static defaultTransformer = YouTubeTransformer;
|
|
12
|
+
static schema = Extractor.schema.extend({
|
|
13
|
+
accessToken: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('OAuth access token for the YouTube Data API (auto-retrieved if not provided)'),
|
|
17
|
+
});
|
|
18
|
+
proxy;
|
|
19
|
+
user;
|
|
20
|
+
static async hasValidCredentials() {
|
|
21
|
+
try {
|
|
22
|
+
await TokenHelper.getValidToken('youtube');
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async setup() {
|
|
30
|
+
const config = this.config;
|
|
31
|
+
this.proxy = new YouTubeProxy(config.accessToken);
|
|
32
|
+
this.user = await this.proxy.getUser();
|
|
33
|
+
}
|
|
34
|
+
createRecordWithUser(data, options) {
|
|
35
|
+
return this.createRecord(data, {
|
|
36
|
+
recordType: options.recordType,
|
|
37
|
+
user: this.user,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Record } from '@chronicle.app/etl';
|
|
2
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
3
|
+
import { YouTubePlaylistItem, YouTubeVideo } from '../utils/YouTubeProxy.js';
|
|
4
|
+
export interface YouTubeLikedVideo {
|
|
5
|
+
item: YouTubePlaylistItem;
|
|
6
|
+
video?: YouTubeVideo;
|
|
7
|
+
}
|
|
8
|
+
export declare class YouTubeLikesExtractor extends YouTubeBaseExtractor<typeof YouTubeLikesExtractor> {
|
|
9
|
+
static description: string;
|
|
10
|
+
static recordTypes: string[];
|
|
11
|
+
static default: boolean;
|
|
12
|
+
private likesPlaylistId;
|
|
13
|
+
setup(): Promise<void>;
|
|
14
|
+
extract(): AsyncGenerator<Record>;
|
|
15
|
+
determineCount(): Promise<number | null>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=YouTubeLikesExtractor.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
2
|
+
export class YouTubeLikesExtractor extends YouTubeBaseExtractor {
|
|
3
|
+
static description = 'Liked videos';
|
|
4
|
+
static recordTypes = ['likes'];
|
|
5
|
+
static default = true;
|
|
6
|
+
likesPlaylistId;
|
|
7
|
+
async setup() {
|
|
8
|
+
await super.setup();
|
|
9
|
+
const likesPlaylistId = this.user?.contentDetails?.relatedPlaylists?.likes;
|
|
10
|
+
if (!likesPlaylistId) {
|
|
11
|
+
throw new Error('YouTube API did not expose a likes playlist for this account');
|
|
12
|
+
}
|
|
13
|
+
this.likesPlaylistId = likesPlaylistId;
|
|
14
|
+
}
|
|
15
|
+
async *extract() {
|
|
16
|
+
const { since, until } = this.config;
|
|
17
|
+
// The likes playlist is newest-first, so a `since` bound also caps how far
|
|
18
|
+
// we need to paginate; without one we fetch to the configured limit.
|
|
19
|
+
let items = await this.proxy.getLikes({
|
|
20
|
+
likesPlaylistId: this.likesPlaylistId,
|
|
21
|
+
limit: since ? undefined : (this.getEffectiveLimit() ?? undefined),
|
|
22
|
+
});
|
|
23
|
+
items = items.filter(item => {
|
|
24
|
+
const likedAt = new Date(item.snippet.publishedAt);
|
|
25
|
+
if (since && likedAt <= since)
|
|
26
|
+
return false;
|
|
27
|
+
if (until && likedAt > until)
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
const limit = this.getEffectiveLimit();
|
|
32
|
+
if (limit !== null) {
|
|
33
|
+
items = items.slice(0, limit);
|
|
34
|
+
}
|
|
35
|
+
const details = await this.proxy.getVideoDetails(items.map(item => item.snippet.resourceId.videoId));
|
|
36
|
+
const detailsById = new Map(details.map(video => [video.id, video]));
|
|
37
|
+
this.logger.info(`Retrieved ${items.length} liked videos from YouTube`);
|
|
38
|
+
for (const item of items) {
|
|
39
|
+
const liked = {
|
|
40
|
+
item,
|
|
41
|
+
video: detailsById.get(item.snippet.resourceId.videoId),
|
|
42
|
+
};
|
|
43
|
+
yield this.createRecordWithUser(liked, { recordType: 'likes' });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async determineCount() {
|
|
47
|
+
try {
|
|
48
|
+
return await this.proxy.getPlaylistItemCount(this.likesPlaylistId);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Record } from '@chronicle.app/etl';
|
|
2
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
3
|
+
import { YouTubePlaylist, YouTubePlaylistItem, YouTubeVideo } from '../utils/YouTubeProxy.js';
|
|
4
|
+
export interface YouTubePlaylistItemRecord {
|
|
5
|
+
playlist: YouTubePlaylist;
|
|
6
|
+
item: YouTubePlaylistItem;
|
|
7
|
+
video?: YouTubeVideo;
|
|
8
|
+
}
|
|
9
|
+
export declare class YouTubePlaylistsExtractor extends YouTubeBaseExtractor<typeof YouTubePlaylistsExtractor> {
|
|
10
|
+
static description: string;
|
|
11
|
+
static recordTypes: string[];
|
|
12
|
+
static temporality: "snapshot";
|
|
13
|
+
extract(): AsyncGenerator<Record>;
|
|
14
|
+
determineCount(): Promise<number | null>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=YouTubePlaylistsExtractor.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
2
|
+
export class YouTubePlaylistsExtractor extends YouTubeBaseExtractor {
|
|
3
|
+
static description = 'Your playlists and their videos';
|
|
4
|
+
static recordTypes = ['playlists', 'playlist-items'];
|
|
5
|
+
// Re-running re-reports every playlist and its current membership, so
|
|
6
|
+
// sightings belong at read time and removed items depart via completeness.
|
|
7
|
+
static temporality = 'snapshot';
|
|
8
|
+
async *extract() {
|
|
9
|
+
const playlists = await this.proxy.getPlaylists();
|
|
10
|
+
this.logger.info(`Retrieved ${playlists.length} playlists from YouTube`);
|
|
11
|
+
let recordCount = 0;
|
|
12
|
+
for (const playlist of playlists) {
|
|
13
|
+
if (this.shouldStopExtracting(recordCount))
|
|
14
|
+
break;
|
|
15
|
+
yield this.createRecordWithUser(playlist, { recordType: 'playlists' });
|
|
16
|
+
recordCount++;
|
|
17
|
+
const items = await this.proxy.getPlaylistItems(playlist.id);
|
|
18
|
+
const details = await this.proxy.getVideoDetails(items.map(item => item.snippet.resourceId.videoId));
|
|
19
|
+
const detailsById = new Map(details.map(video => [video.id, video]));
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
if (this.shouldStopExtracting(recordCount))
|
|
22
|
+
break;
|
|
23
|
+
const data = {
|
|
24
|
+
playlist,
|
|
25
|
+
item,
|
|
26
|
+
video: detailsById.get(item.snippet.resourceId.videoId),
|
|
27
|
+
};
|
|
28
|
+
yield this.createRecordWithUser(data, {
|
|
29
|
+
recordType: 'playlist-items',
|
|
30
|
+
});
|
|
31
|
+
recordCount++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async determineCount() {
|
|
36
|
+
try {
|
|
37
|
+
const playlists = await this.proxy.getPlaylists();
|
|
38
|
+
return playlists.reduce((sum, playlist) => sum + 1 + (playlist.contentDetails?.itemCount ?? 0), 0);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Record } from '@chronicle.app/etl';
|
|
2
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
3
|
+
export declare class YouTubeSubscriptionsExtractor extends YouTubeBaseExtractor<typeof YouTubeSubscriptionsExtractor> {
|
|
4
|
+
static description: string;
|
|
5
|
+
static recordTypes: string[];
|
|
6
|
+
static temporality: "snapshot";
|
|
7
|
+
extract(): AsyncGenerator<Record>;
|
|
8
|
+
determineCount(): Promise<number | null>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=YouTubeSubscriptionsExtractor.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { YouTubeBaseExtractor } from './YouTubeBaseExtractor.js';
|
|
2
|
+
export class YouTubeSubscriptionsExtractor extends YouTubeBaseExtractor {
|
|
3
|
+
static description = 'Channel subscriptions';
|
|
4
|
+
static recordTypes = ['subscriptions'];
|
|
5
|
+
// Re-running re-reports the full current subscription list with each
|
|
6
|
+
// channel's latest title/avatar, so sightings belong at read time.
|
|
7
|
+
static temporality = 'snapshot';
|
|
8
|
+
async *extract() {
|
|
9
|
+
const subscriptions = await this.proxy.getSubscriptions({
|
|
10
|
+
limit: this.getEffectiveLimit() ?? undefined,
|
|
11
|
+
});
|
|
12
|
+
this.logger.info(`Retrieved ${subscriptions.length} subscriptions from YouTube`);
|
|
13
|
+
for (const subscription of subscriptions) {
|
|
14
|
+
yield this.createRecordWithUser(subscription, {
|
|
15
|
+
recordType: 'subscriptions',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async determineCount() {
|
|
20
|
+
try {
|
|
21
|
+
return await this.proxy.getSubscriptionsCount();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Extractor, Record } from '@chronicle.app/etl';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import YouTubeTakeoutTransformer from './YouTubeTakeoutTransformer.js';
|
|
4
|
+
export interface TakeoutSelfChannel {
|
|
5
|
+
id: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TakeoutWatchRecord {
|
|
9
|
+
videoId: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
titleUrl?: string;
|
|
12
|
+
channelId?: string;
|
|
13
|
+
channelName?: string;
|
|
14
|
+
time: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TakeoutSearchRecord {
|
|
17
|
+
query: string;
|
|
18
|
+
time: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TakeoutSubscriptionRecord {
|
|
21
|
+
channelId: string;
|
|
22
|
+
channelTitle?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface TakeoutPlaylistRecord {
|
|
25
|
+
playlistId: string;
|
|
26
|
+
title: string;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
visibility?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface TakeoutPlaylistItemRecord {
|
|
31
|
+
playlistId: string;
|
|
32
|
+
playlistTitle: string;
|
|
33
|
+
videoId: string;
|
|
34
|
+
addedAt: string;
|
|
35
|
+
}
|
|
36
|
+
export interface TakeoutUploadRecord {
|
|
37
|
+
videoId: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
durationMs?: string;
|
|
41
|
+
publishedAt: string;
|
|
42
|
+
}
|
|
43
|
+
export interface TakeoutCommentRecord {
|
|
44
|
+
commentId: string;
|
|
45
|
+
videoId: string;
|
|
46
|
+
rawText: string;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
}
|
|
49
|
+
export declare class YouTubeTakeoutExtractor extends Extractor<typeof YouTubeTakeoutExtractor> {
|
|
50
|
+
static source: string;
|
|
51
|
+
static description: string;
|
|
52
|
+
static delivery: "export";
|
|
53
|
+
static strategy: string;
|
|
54
|
+
static recordTypes: string[];
|
|
55
|
+
static default: boolean;
|
|
56
|
+
static temporality: "snapshot";
|
|
57
|
+
static defaultTransformer: typeof YouTubeTakeoutTransformer;
|
|
58
|
+
static schema: z.ZodObject<{
|
|
59
|
+
since: z.ZodOptional<z.ZodDate>;
|
|
60
|
+
until: z.ZodOptional<z.ZodDate>;
|
|
61
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
62
|
+
} & {
|
|
63
|
+
input: z.ZodOptional<z.ZodString>;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
limit?: number | undefined;
|
|
66
|
+
since?: Date | undefined;
|
|
67
|
+
until?: Date | undefined;
|
|
68
|
+
input?: string | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
limit?: number | undefined;
|
|
71
|
+
since?: Date | undefined;
|
|
72
|
+
until?: Date | undefined;
|
|
73
|
+
input?: string | undefined;
|
|
74
|
+
}>;
|
|
75
|
+
private archiveAsOf;
|
|
76
|
+
private selfChannel;
|
|
77
|
+
private inputPath;
|
|
78
|
+
private fileExists;
|
|
79
|
+
setup(): Promise<void>;
|
|
80
|
+
protected asOfTime(): Date;
|
|
81
|
+
private inRange;
|
|
82
|
+
/** `[playlist title, csv path]` for each per-playlist membership file. */
|
|
83
|
+
private playlistItemFiles;
|
|
84
|
+
extract(): AsyncGenerator<Record>;
|
|
85
|
+
private extractWatches;
|
|
86
|
+
private extractSearches;
|
|
87
|
+
private extractSubscriptions;
|
|
88
|
+
private extractPlaylists;
|
|
89
|
+
private extractUploads;
|
|
90
|
+
private extractComments;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=YouTubeTakeoutExtractor.d.ts.map
|