Compare commits

...

975 Commits

Author SHA1 Message Date
dependabot[bot]
1aebed4916 Bump the actions group with 5 updates
Bumps the actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `3` | `4` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3` | `4` |
| [docker/login-action](https://github.com/docker/login-action) | `3.7.0` | `4.1.0` |
| [docker/metadata-action](https://github.com/docker/metadata-action) | `5.10.0` | `6.0.0` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | `6` | `7` |


Updates `docker/setup-qemu-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

Updates `docker/setup-buildx-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

Updates `docker/login-action` from 3.7.0 to 4.1.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](c94ce9fb46...4907a6ddec)

Updates `docker/metadata-action` from 5.10.0 to 6.0.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](c299e40c65...030e881283)

Updates `docker/build-push-action` from 6 to 7
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/login-action
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/metadata-action
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 23:45:57 +00:00
Miguel Sozinho Ramalho
afbe4fac50 Merge pull request #430 from bellingcat/dev
bug fixes and maintenance
2026-04-27 15:52:39 +01:00
msramalho
e633be1721 version bump 2026-04-27 12:35:54 +01:00
msramalho
bc06de8e5c fixes incomplete yt-dlp parts download 2026-04-27 12:34:47 +01:00
Miguel Sozinho Ramalho
20fddce3a3 Merge pull request #427 from PeterUpfold/deno-container
Fix missing JS runtime config for bguils_po_token_method
2026-04-24 11:08:28 +01:00
msramalho
6efa439cdb dependencies bump 2026-04-23 17:20:54 +01:00
Miguel Sozinho Ramalho
ef77d1fc86 Merge branch 'main' into dev 2026-04-23 14:21:01 +01:00
msramalho
a57a5ee005 adds an extra check when calling pypi as it's led to uncaught ssl errors 2026-04-23 14:20:07 +01:00
msramalho
2582f567ac removes curl/unzip from dockerfile 2026-04-23 14:04:46 +01:00
msramalho
4e5c1a6218 suggested alternative change to deno install 2026-04-23 14:02:51 +01:00
Peter Upfold
12d9c469b2 Add Deno to Dockerfile 2026-04-13 18:19:23 +01:00
Miguel Sozinho Ramalho
792838f1a1 Merge pull request #419 from bellingcat/dev
Dependencies bump, new ghostarchive enricher
2026-04-07 14:44:35 +01:00
Miguel Sozinho Ramalho
17c4ae15eb Merge branch 'main' into dev 2026-04-07 10:51:10 +01:00
msramalho
a08af07348 version bump 2026-04-06 18:34:20 +01:00
Miguel Sozinho Ramalho
e54077f4e8 Merge pull request #418 from bellingcat/feat/ghostarchive
Feat/ghostarchive
2026-04-06 18:33:15 +01:00
msramalho
319c0528da dependencies bump 2026-04-06 18:27:47 +01:00
msramalho
ae0e53e434 adds tests for new ghostarchive enricher feature 2026-04-06 17:15:32 +01:00
msramalho
82fc786d56 implements new enricher to submit URLs to ghostarchive 2026-04-06 17:13:48 +01:00
Miguel Sozinho Ramalho
aa65299844 Merge pull request #408 from bellingcat/dev
telethon compatibility with celery workers, dependency bumps
2026-03-16 11:28:21 +00:00
msramalho
1b69ec1f00 dependencies bump 2026-03-16 11:11:57 +00:00
Miguel Sozinho Ramalho
304e5d40b1 Merge branch 'main' into dev 2026-03-16 11:10:26 +00:00
msramalho
3194fee95d fix telethon bug when running in celery workers that close the event loop 2026-03-12 10:20:11 +00:00
msramalho
0040810e2e dependencies bump 2026-03-10 14:33:25 +00:00
Miguel Sozinho Ramalho
63cfe34e23 Merge pull request #407 from bellingcat/dev
minor bug fix: handles failed get downloads
2026-03-02 17:10:46 +00:00
msramalho
23a88e3cf4 ci issues 2026-03-02 17:07:09 +00:00
msramalho
3cac160cc1 version bump 2026-03-02 17:01:33 +00:00
msramalho
e9a92272c5 bug fix: missing filename on url download 2026-03-02 17:01:16 +00:00
Miguel Sozinho Ramalho
5d6c5ac2b1 Merge pull request #406 from bellingcat/dev
1.2.3
2026-03-02 15:42:08 +00:00
msramalho
f1de07c9aa version bump 2026-03-02 15:41:03 +00:00
msramalho
1e1e060a77 closes #342 2026-03-02 15:37:55 +00:00
msramalho
b43d229326 closes #358 2026-03-02 14:27:48 +00:00
msramalho
077b03fc61 minor tests change to work in gh actions 2026-03-02 14:08:14 +00:00
Miguel Sozinho Ramalho
cf77cfa64d Merge pull request #405 from bellingcat/feat/nitter-alternative
closes #400 Feat twitter drop-in alternative
2026-03-02 12:33:34 +00:00
msramalho
bc66dd4f2a fxtwitter working instead of nitter 2026-03-02 12:31:28 +00:00
msramalho
139d647197 Merge branch 'dev' into feat/nitter-alternative 2026-03-02 12:16:22 +00:00
msramalho
f465b570cd adding missing tests (no download) 2026-03-02 12:14:47 +00:00
Miguel Sozinho Ramalho
52a7cabaf1 Merge pull request #402 from bellingcat/dev
bug fix: wacz screenshots leak in shared session
2026-02-25 10:39:54 +00:00
msramalho
a739361e12 bug fix: wacz screenshots leak in shared session 2026-02-23 16:26:36 +00:00
Miguel Sozinho Ramalho
9a97fede43 Merge pull request #401 from bellingcat/dev
Dependencies maintenance.
2026-02-23 13:27:51 +00:00
msramalho
2d13077fad bumping ruff version 2026-02-23 12:36:53 +00:00
msramalho
8a4a314cf9 ruff python version to dev version 2026-02-23 12:32:24 +00:00
msramalho
75e8b788ae revert ruff workflow changes 2026-02-23 12:31:20 +00:00
msramalho
defe2315bf docs updates 2026-02-23 12:28:25 +00:00
msramalho
b9ab26ed5a see #400 WIP nitter not working as of now 2026-02-23 12:20:10 +00:00
msramalho
ba0dffdd5e Merge branch 'dev' of github.com:bellingcat/auto-archiver into dev 2026-02-23 12:18:58 +00:00
msramalho
a09927c507 minor docs fix 2026-02-23 12:18:47 +00:00
Miguel Sozinho Ramalho
6c938c489a Merge pull request #392 from bellingcat/dependabot/github_actions/actions-bc0df0c757
Bump the actions group with 5 updates
2026-02-23 11:28:24 +00:00
msramalho
0e39768da9 version bumping settings script 2026-02-23 11:27:12 +00:00
msramalho
1e5d6ec4a6 version bump: minor 2026-02-23 11:23:40 +00:00
msramalho
3385d004cf yt-dlp to latest version 2026-02-23 11:23:26 +00:00
msramalho
7f27f7fce0 closes #383 fixing browsertrix-crawler at 1.11.4 2026-02-23 11:23:06 +00:00
msramalho
a6e3240af1 closes #399 and global dependency updates 2026-02-23 11:13:31 +00:00
dependabot[bot]
bf4c196cc2 Bump the actions group with 5 updates
Bumps the actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `4` | `6` |
| [docker/login-action](https://github.com/docker/login-action) | `3.4.0` | `3.7.0` |
| [docker/metadata-action](https://github.com/docker/metadata-action) | `5.7.0` | `5.10.0` |
| [actions/setup-python](https://github.com/actions/setup-python) | `5` | `6` |
| [actions/cache](https://github.com/actions/cache) | `4` | `5` |


Updates `actions/checkout` from 4 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

Updates `docker/login-action` from 3.4.0 to 3.7.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](74a5d14239...c94ce9fb46)

Updates `docker/metadata-action` from 5.7.0 to 5.10.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](902fa8ec7d...c299e40c65)

Updates `actions/setup-python` from 5 to 6
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/login-action
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: docker/metadata-action
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 20:17:43 +00:00
Miguel Sozinho Ramalho
c640cc898a Merge pull request #385 from bellingcat/dev
1.2.0 dependencies, small bugs, 1st time contributors
2026-01-08 15:55:40 +00:00
msramalho
3e2c0b564b wiki fix 2026-01-08 15:49:42 +00:00
msramalho
5fd23baa55 this is ruff 2026-01-08 15:48:08 +00:00
msramalho
8a450310c7 version bump for new release 2026-01-08 15:41:27 +00:00
msramalho
bef8a14089 pyperclip version bump closes #339 2026-01-08 15:40:17 +00:00
msramalho
cd0b093e7a browsertrix-crawler to 1.9.2 see #383 2026-01-08 15:33:40 +00:00
msramalho
096c9d09ef fix for unexpected types for json.dump 2026-01-08 15:18:19 +00:00
Miguel Sozinho Ramalho
df3521e9ca Merge pull request #377 from m4cd4r4/fix/improve-deleted-post-detection
Fix #335: Add comprehensive deletion detection for removed/unavailable content
2026-01-08 15:06:21 +00:00
msramalho
a89d0193e4 removes patch file 2026-01-08 15:02:00 +00:00
msramalho
536cbd905f puts tests file in correct directory 2026-01-08 14:55:40 +00:00
msramalho
a936921c4e updates new utils file and test 2026-01-08 14:54:06 +00:00
Miguel Sozinho Ramalho
68f672a4fa Merge branch 'dev' into fix/improve-deleted-post-detection 2026-01-08 14:36:17 +00:00
Miguel Sozinho Ramalho
4ee0ad1cf8 Merge pull request #359 from mjgaughan/specify-medatada-feature
implementing default metadata omission/user metadata selection
2026-01-08 14:34:50 +00:00
msramalho
bac809451c expands tests to included non predefined metadata keys 2026-01-08 14:33:16 +00:00
msramalho
53dc9904ce refactorws PR to obey standard code approach 2026-01-08 14:30:26 +00:00
Miguel Sozinho Ramalho
c1f312d42a Merge branch 'dev' into specify-medatada-feature 2026-01-08 14:04:42 +00:00
msramalho
23c9dfe717 updating dependencies 2026-01-08 13:53:44 +00:00
m4cd4r4
d02e7e0f02 Add comprehensive deletion detection for removed/unavailable content
Implements issue #335: improve detection of deleted/missing posts

## Changes

### New Deletion Detection System
- Created `deletion_detection.py` utility module with platform-specific
  indicators for Twitter, Facebook, Instagram, TikTok, YouTube, Reddit,
  VK, and Telegram
- Detects deletion via HTML content, page titles, error messages, and
  video metadata
- Stores detailed deletion context (indicator, source, platform) in
  metadata for investigators

### Integration Points
- **Antibot Extractor**: Checks HTML and page titles after page load;
  resolves TODO about detecting deleted videos
- **Generic Extractor**: Checks yt-dlp video data and error messages
  for deletion indicators
- **Twitter Dropin**: Enhanced detection when user/created_at fields
  are missing

### Test Coverage
- Comprehensive test suite covering all platforms
- Tests for HTML, title, error message, and metadata detection
- Validates that normal content is not falsely flagged

## Impact for Conflict Documentation

This fix is critical for evidence preservation in war-torn regions:
- Investigators can now document that evidence existed but was deleted
- Prevents wasted archival attempts on deleted content
- Tracks patterns of content removal
- Preserves metadata about what was deleted and when

Twitter example: Detects "Hmm...this page doesn't exist. Try searching
for something else" and flags content as deleted_or_unavailable.
2025-12-17 18:40:58 +08:00
Miguel Sozinho Ramalho
56526a9ac7 Merge pull request #365 from bellingcat/dev
Facebook reels fix
2025-10-23 10:40:43 +01:00
msramalho
3a22cc28c0 skip tiktok antibot test in CI 2025-10-23 10:17:14 +01:00
msramalho
dbb3dfa04f fixes wikipedia test 2025-10-23 10:04:44 +01:00
msramalho
01bdb35f5d version bump 2025-10-23 09:51:31 +01:00
msramalho
43cbc6ac56 generic extractor improvements 2025-10-23 09:51:14 +01:00
msramalho
9c7cab1ae2 dependencies update 2025-10-22 21:07:12 +01:00
msramalho
a9a0bae083 dependencies update 2025-10-22 18:11:36 +01:00
Miguel Sozinho Ramalho
97d133ce79 Merge pull request #357 from bellingcat/dev
small improvements on tiktok and verison bumps
2025-10-22 16:02:26 +01:00
msramalho
432ee3dcfd version bump 2025-10-22 15:50:50 +01:00
mgaughan
94e0803fb3 implementing default metadata omission/user metadata selection 2025-09-22 20:16:40 -05:00
msramalho
794b4f6052 Merge branch 'dev' of https://github.com/bellingcat/auto-archiver into dev 2025-09-11 15:06:27 +01:00
msramalho
965d7d41dd dependency updates 2025-09-11 15:06:25 +01:00
Miguel Sozinho Ramalho
e73faa70cc Merge pull request #352 from mjgaughan/developer-documentation-updates
updating the style-checking code in the documentation
2025-08-11 10:42:53 +01:00
mgaughan
80beab9f23 ruff-fix -> ruff-clean; there is no ruff-fix in the Makefile. Maybe the command /should/ be ruff-fix to align with the underlying ruff command; for later discussion. This at least reconciles the documentation to the Makefile 2025-08-05 21:36:32 -04:00
Miguel Sozinho Ramalho
200cea4e12 Merge pull request #345 from mjgaughan/main
Correction of small documentation typos
2025-07-29 09:36:10 +01:00
mgaughan
1256fde159 updating location of .env.test.example in documentation 2025-07-23 13:04:48 -04:00
mgaughan
65e222e177 fixing typo in documentation pytest -> poetry 2025-07-22 17:20:59 -04:00
mgaughan
f2eb9ef784 correcting to double-dash in the poetry install documentation 2025-07-21 17:55:48 -04:00
msramalho
2081c16555 embed retry into timestamping 2025-07-10 14:49:53 +01:00
msramalho
d3efd7121c avoid empty metadata comments 2025-07-06 14:05:17 +01:00
msramalho
9d3cd5774b an improved approach for #295 2025-07-06 14:04:01 +01:00
Miguel Sozinho Ramalho
80d61e8b85 Merge pull request #341 from bellingcat/dev
Address several small bugs, includes tiktok photos extraction, and data-saving for proxy usage in generic_extractor.
2025-07-05 20:28:00 +01:00
msramalho
d36cdbfa87 fixing pypaperclip see issue #339 2025-07-05 19:07:23 +01:00
msramalho
c1506ee1cf some wayback errors are expected and should be warnings 2025-07-05 18:31:39 +01:00
msramalho
3a34a49822 adds antibot tiktok logic for photos closes #295 2025-07-05 18:31:12 +01:00
msramalho
37c6d97275 new auth wall check logic and escaped CSS selector in selenium 2025-07-05 18:30:31 +01:00
msramalho
7234eda85f expands Sheets API retries for really large spreadsheets 2025-07-05 18:29:33 +01:00
msramalho
a8c1ef3912 generic_extractor config to use proxy only when needed to avoid overzealousness 2025-07-05 16:54:58 +01:00
msramalho
52ed8196a5 updates dependencies 2025-07-05 16:03:47 +01:00
msramalho
2051e8e491 adds further exponential backoff for Sheets API worksheet enumeration 2025-07-05 16:02:07 +01:00
msramalho
21255db86a stops using service that is not up for timestamping 2025-07-05 16:00:46 +01:00
msramalho
eae0da08b3 fix issue with two runs of anitbot extractor 2025-07-05 16:00:03 +01:00
msramalho
0d1447117c updates docs to reflect new general approach extractor 2025-07-05 15:56:13 +01:00
Miguel Sozinho Ramalho
0f56a5aae5 Merge pull request #331 from bellingcat/dev
1.1.1 multiple small fixes, and new logging strategy
2025-06-30 02:36:25 +01:00
msramalho
649412053e exclude non-ready code 2025-06-30 02:27:21 +01:00
msramalho
c2c9718f73 make python api tests work on gh when no env is set 2025-06-30 02:20:51 +01:00
msramalho
30ea8a0ba4 bumps dependencies 2025-06-30 02:20:09 +01:00
msramalho
73c8dc583f closes #333 2025-06-30 01:52:22 +01:00
msramalho
b2648fa3cd follow docs advice on exponential backoff of SheetsAPI 2025-06-30 01:47:12 +01:00
msramalho
4ad71b3589 adds retry to worksheet read for slow worksheets 2025-06-30 01:42:34 +01:00
msramalho
7c9475cde2 allow for human readable console logs, but defaults to JSON on file logs. 2025-06-30 00:53:10 +01:00
msramalho
afd9090a4c concludes logging standardization refactor 2025-06-26 17:20:04 +01:00
msramalho
ad29cb4447 adds post_data to metadata for instagram 2025-06-26 15:48:10 +01:00
msramalho
ce4d7ac649 WIP refactor logging 2025-06-21 15:54:51 +01:00
msramalho
ade7feb5a0 version bump 2025-06-18 17:38:17 +01:00
msramalho
12b457706b closes #166 adds story URL feature to telethon extractor 2025-06-18 17:37:44 +01:00
msramalho
592dc30415 closes #330 2025-06-18 16:40:55 +01:00
msramalho
4a36e6f6b0 fix tests 2025-06-18 13:50:21 +01:00
msramalho
d46eeee9b6 docs improved 2025-06-18 13:35:51 +01:00
msramalho
302e6f4258 logs improved 2025-06-18 13:35:43 +01:00
Miguel Sozinho Ramalho
e803c5d0e3 Merge branch 'main' into dev 2025-06-18 13:35:21 +01:00
msramalho
e1d0314a9e Merge branch 'dev' of https://github.com/bellingcat/auto-archiver into dev 2025-06-18 13:26:48 +01:00
Miguel Sozinho Ramalho
5d5119e053 Merge pull request #329 from bellingcat/dev
installs ffmpeg in readthedocs
2025-06-18 00:31:09 +01:00
msramalho
d6c90d87f1 installs ffmpeg in readthedocs 2025-06-18 00:30:45 +01:00
msramalho
212bf67ab1 installs ffmpeg in readthedocs 2025-06-18 00:29:36 +01:00
Miguel Sozinho Ramalho
6abe2edb13 Merge pull request #328 from bellingcat/dev
fix to configuration editor npm versions
2025-06-18 00:22:39 +01:00
msramalho
03c0cf09ae fix issue with grid in scripts/config_editor @mui lib upgrade 2025-06-18 00:20:31 +01:00
Miguel Sozinho Ramalho
0db77c7e68 Merge pull request #326 from bellingcat/dependabot/npm_and_yarn/scripts/settings/actions-27795ad889
Bump @types/react from 19.1.7 to 19.1.8 in /scripts/settings in the actions group across 1 directory
2025-06-18 00:12:51 +01:00
dependabot[bot]
cd6607943d Bump @types/react
Bumps the actions group with 1 update in the /scripts/settings directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react).


Updates `@types/react` from 19.1.7 to 19.1.8
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-17 22:58:23 +00:00
Miguel Sozinho Ramalho
3869ea73d7 Merge pull request #312 from bellingcat/dev v1.1.0
v1.1.0 WIP
2025-06-17 23:57:22 +01:00
msramalho
918cb220be minor indentation issue 2025-06-17 23:51:10 +01:00
msramalho
76fd329fe5 twitter tests fix 2025-06-17 23:51:03 +01:00
msramalho
a3ae9ebbb3 log level updates 2025-06-17 20:36:33 +01:00
msramalho
23b781c866 new check for edge case 2025-06-17 20:36:22 +01:00
msramalho
2aec240128 thumbnail enricher always run probe by default 2025-06-17 20:28:20 +01:00
msramalho
c5a2fd45f9 log levels updated 2025-06-17 20:04:40 +01:00
msramalho
216226e7cc browsertrix version bump 2025-06-17 19:22:20 +01:00
msramalho
ad168785e7 retry for Google API 503s 2025-06-17 19:22:09 +01:00
msramalho
74a1561c3d logging and clean up 2025-06-17 19:21:40 +01:00
msramalho
55d9ffaacd typo 2025-06-17 18:51:21 +01:00
msramalho
f19fb575a7 logging updates 2025-06-17 18:50:54 +01:00
msramalho
f53b2075ba fixes gdrive error 2025-06-17 18:45:55 +01:00
Miguel Sozinho Ramalho
d20486c02a Merge pull request #320 from djhmateer/v1-dm-changes
V1 dm changes including logging
2025-06-17 16:13:37 +01:00
msramalho
6085a66c58 revert metadata json renaming 2025-06-17 16:10:24 +01:00
msramalho
33cca734d9 original_url changes still constitute empty result 2025-06-17 16:06:25 +01:00
msramalho
2f1a07abbf renaming and code improvements to json_e richer 2025-06-17 16:06:04 +01:00
msramalho
664ee8d037 fixes bugs and limited configuration of multi-level logs 2025-06-17 14:10:46 +01:00
msramalho
1b260788de do not add commit comments to code 2025-06-17 13:18:12 +01:00
msramalho
f0b876e67c removes dev specific instructions 2025-06-17 13:16:36 +01:00
msramalho
8067da0f60 custom user to its own file 2025-06-17 13:15:13 +01:00
Miguel Sozinho Ramalho
6f949738a3 Merge branch 'dev' into v1-dm-changes 2025-06-17 13:05:34 +01:00
msramalho
1b6d85884b complements authentication changes 2025-06-17 12:54:43 +01:00
msramalho
7ab804d163 dependencies update 2025-06-17 12:50:35 +01:00
Dave Mateer
b3adc5603a metadata.json hardcode in storage. add new metadata_json_enricher. log level change in orchestrator 2025-06-17 09:51:19 +01:00
Dave Mateer
ba3f1a52e8 Logging each_level_in_separate_file feature 2025-06-16 16:15:54 +01:00
Dave Mateer
a60d800b31 Changed log level for media 2025-06-16 15:07:39 +01:00
Dave Mateer
f2e80758a7 typo on authentication docs. Updated install docs. 2025-06-16 14:59:55 +01:00
Dave Mateer
f07fdbc500 Custom local version comment in toml file 2025-06-16 14:54:15 +01:00
Dave Mateer
b236f2510d Updates to installation docs 2025-06-16 14:40:40 +01:00
Dave Mateer
529d8b60bf Gitgnore to include launch.json and installtion docs to include build script. 2025-06-16 14:37:21 +01:00
msramalho
cd6a2b6031 generic_extractor download tests adaptations 2025-06-11 20:05:35 +01:00
msramalho
dfb361e3a0 reset generic_extractor description in result 2025-06-11 19:55:54 +01:00
Miguel Sozinho Ramalho
3d31c7605b Merge pull request #319 from bellingcat/feat/linkedin-antibot
Antibot Dropin for Linkedin
2025-06-11 19:42:38 +01:00
msramalho
d7a48e465b fix copypasta 2025-06-11 18:04:49 +01:00
msramalho
aaa9ead39d adds documentation for dropins 2025-06-11 17:58:53 +01:00
msramalho
f5be7a50c1 Testing Linkedin Dropin for Antibot 2025-06-11 16:52:03 +01:00
msramalho
2adcf231f7 new LinkedIn Dropin for Antibot 2025-06-11 16:51:52 +01:00
msramalho
cd19181d8f minor improvements 2025-06-11 16:51:42 +01:00
msramalho
b60469767a more flexibility to antibot dropins media finding process 2025-06-11 16:51:22 +01:00
msramalho
d60d02c16e improves download_from_url 2025-06-11 16:50:31 +01:00
msramalho
e567bba6f9 improves docs for how-to and migrations 2025-06-11 13:37:03 +01:00
msramalho
3cf51dd874 adds tracker remove feature and tests 2025-06-11 11:56:42 +01:00
msramalho
69ddb72146 separate reddit tests 2025-06-11 11:27:11 +01:00
msramalho
1039e9631f new reddit tests with .env.test 2025-06-11 11:22:23 +01:00
Miguel Sozinho Ramalho
79f42c3c41 Merge pull request #318 from bellingcat/feat/antibot-reddit
Adds RedditDropin and other flow improvements
2025-06-10 18:39:34 +01:00
msramalho
8314833ae8 removes exclude_media_extensions option 2025-06-10 18:34:33 +01:00
msramalho
6279610a43 updates docs 2025-06-10 18:28:45 +01:00
msramalho
fc89d96517 escape sequence 2025-06-10 18:04:33 +01:00
msramalho
54fda9cad4 antibot in docker uses a different user_data_dir 2025-06-10 18:04:27 +01:00
msramalho
71636233cb adds migration information and VkDropin info. 2025-06-10 17:07:10 +01:00
msramalho
fdbe96f2e4 vk and reddit should work without credentials but log the error 2025-06-10 16:44:14 +01:00
msramalho
22bd8727df python dependencies bump 2025-06-10 16:43:55 +01:00
msramalho
499c272260 dependabot switch to monthly 2025-06-10 16:37:52 +01:00
Miguel Sozinho Ramalho
f232bc45b8 Merge pull request #315 from bellingcat/dependabot/docker/webrecorder/browsertrix-crawler-1.6.2
Bump webrecorder/browsertrix-crawler from 1.6.1 to 1.6.2
2025-06-10 16:34:30 +01:00
msramalho
4270e06728 npm update on scripts/settings 2025-06-10 16:33:47 +01:00
msramalho
ca00aa302d version bump breaking 2025-06-10 16:31:32 +01:00
msramalho
773fa82f06 introduces reddit dropin 2025-06-10 16:31:19 +01:00
msramalho
ef0e909a72 extractor to auto detect best quality 2025-06-10 16:29:35 +01:00
msramalho
6bbc7fb47a improves antibot flow and makes auth_wall detection optional 2025-06-10 16:29:07 +01:00
msramalho
809b8c7749 default dropin introduced 2025-06-10 16:14:42 +01:00
msramalho
6d82655cc4 manifest improvement for antibot 2025-06-10 16:14:34 +01:00
msramalho
6bd493a791 dropin with new ytdlp feature and helper method 2025-06-10 16:11:55 +01:00
msramalho
287e823f43 improves twitter URL cleaning and introduces another bestquality check 2025-06-10 16:09:38 +01:00
msramalho
c815488daa adds new URLs to ignore 2025-06-10 15:44:52 +01:00
dependabot[bot]
f53e34d6bd Bump webrecorder/browsertrix-crawler from 1.6.1 to 1.6.2
Bumps webrecorder/browsertrix-crawler from 1.6.1 to 1.6.2.

---
updated-dependencies:
- dependency-name: webrecorder/browsertrix-crawler
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 20:55:07 +00:00
Miguel Sozinho Ramalho
4cfbc3008b Merge pull request #313 from bellingcat/feat/antibot-auth
Introduces more flexibility to the Antibot Extractor
2025-06-08 14:42:35 +01:00
msramalho
6f02493ff1 adds clips extraction to VK, though generic_extractor should still be run for those 2025-06-08 14:36:55 +01:00
msramalho
1f2d637928 minor improvements 2025-06-08 14:16:21 +01:00
msramalho
18cc05a2fe allows auth_for_site to receive do.main directly 2025-06-08 14:16:12 +01:00
msramalho
c96fd71f35 minor cleanup 2025-06-07 20:06:53 +01:00
msramalho
b3183510ea installs ffmpeg in GH actions 2025-06-07 20:03:26 +01:00
msramalho
d13a5ef003 adds tests in minor improvements 2025-06-07 19:58:18 +01:00
msramalho
48c1ab3c1f doc improvements 2025-06-07 19:14:16 +01:00
msramalho
b2ee42ee95 adds the first antibot dropin: VKontakte 2025-06-07 19:10:01 +01:00
msramalho
07ff5baf07 adds Dropin flexible integration for antibot 2025-06-07 19:09:37 +01:00
msramalho
d202d79e0f lint 2025-06-07 19:06:14 +01:00
msramalho
e2e6490b49 minimal changes 2025-06-07 18:15:21 +01:00
msramalho
952487da30 adds missing bin dependency 2025-06-07 18:14:42 +01:00
msramalho
c7a84bc97a generalizes ydl info to filename method for reusing 2025-06-07 18:14:08 +01:00
msramalho
c0be41950d Merge branch 'dev' of https://github.com/bellingcat/auto-archiver into dev 2025-06-04 17:06:42 +01:00
Miguel Sozinho Ramalho
ae547ef83f Merge pull request #308 from bellingcat/dependabot/npm_and_yarn/scripts/settings/actions-a541a3dacb
Bump the actions group in /scripts/settings with 4 updates
2025-06-04 15:06:59 +01:00
msramalho
8a897cf601 minimal changes: standard naming 2025-06-04 15:06:08 +01:00
Miguel Sozinho Ramalho
14c8af5cc8 Merge pull request #310 from djhmateer/waczscreenshot bug fix
counter_screenshots to counter_warc_files in wacz_extractor so don't …
2025-06-04 15:01:12 +01:00
Miguel Sozinho Ramalho
8e2e18ef75 Merge pull request #311 from bellingcat/feat/seleniumbase
Replaces ScreenshotEnricher with AntibotExtractorEnricher, removes VkExtractor
2025-06-04 14:53:31 +01:00
msramalho
5491f3e9e7 fixing s3 storage tests 2025-06-04 14:41:00 +01:00
msramalho
264ba82ea0 finish removing screenshot_enricher references 2025-06-04 14:31:07 +01:00
msramalho
05231445d9 removes unnecessary ignored files 2025-06-04 14:19:25 +01:00
msramalho
2c6be4447f linting 2025-06-04 14:17:38 +01:00
msramalho
5f68c151a0 removes webdriver utils used by screenshot enricher 2025-06-04 14:17:19 +01:00
msramalho
6d2aec032f Merge remote-tracking branch 'origin/main' into dev 2025-06-04 14:15:14 +01:00
msramalho
bc8cf2fb29 minor TODO 2025-06-04 14:10:19 +01:00
msramalho
f066111d49 removes geckodriver dependencies following screenshot enricher removal 2025-06-04 14:09:13 +01:00
msramalho
e6f3826a3a dropping screenshot enricher 2025-06-04 12:08:59 +01:00
msramalho
e5a78a5d06 antibot can be used out of the box 2025-06-04 12:01:42 +01:00
msramalho
258fb4faaf visual HTML preview improvements 2025-06-04 12:00:40 +01:00
msramalho
5ec00f7811 adds dependencies for seleniumbase 2025-06-04 12:00:22 +01:00
msramalho
22408e2a98 adds test for antibot 2025-06-04 11:59:59 +01:00
msramalho
378b1a6d22 expand S3 objects content type for better preview results in non-latin languages 2025-06-04 11:53:41 +01:00
msramalho
d130c1b3fa WIP attempt at ytdlp impersonation 2025-06-04 11:53:18 +01:00
msramalho
cbd189c97d general cleanup 2025-06-04 11:53:01 +01:00
msramalho
d2e8f1a512 introduces antibot step with seleniumbase 2025-06-04 11:20:46 +01:00
msramalho
488802b632 poetry update 2025-06-04 11:08:44 +01:00
Dave Mateer
c772082f0e counter_screenshots to counter_warc_files in wacz_extractor so don't get error about add mulitple items with same id. 2025-06-03 12:34:41 +01:00
msramalho
ee68f3efee Merge remote-tracking branch 'origin/main' into feat/seleniumbase 2025-06-03 11:05:16 +01:00
dependabot[bot]
efe2a1a8b6 Bump the actions group in /scripts/settings with 4 updates
Bumps the actions group in /scripts/settings with 4 updates: [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material), [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material), [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom).


Updates `@mui/icons-material` from 6.4.12 to 7.1.1
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v7.1.1/packages/mui-icons-material)

Updates `@mui/material` from 6.4.12 to 7.1.1
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v7.1.1/packages/mui-material)

Updates `react` from 19.0.0 to 19.1.0
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.1.0/packages/react)

Updates `react-dom` from 19.0.0 to 19.1.0
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.1.0/packages/react-dom)

---
updated-dependencies:
- dependency-name: "@mui/icons-material"
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@mui/material"
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: react
  dependency-version: 19.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: react-dom
  dependency-version: 19.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 20:21:07 +00:00
Miguel Sozinho Ramalho
6735fa890b v1.0.1 dependency updates, generic extractor improvements (#307)
* wacz: allow exceptional cases where more than one resource image is available

* improves generic extractor edge-cases and yt-dlp updates

* REMOVES vk_extractor until further notice

* bumps browsertrix in docker image

* npm version bump on scripts/settings

* poetry updates

* Changed log level on gsheet_feeder_db started from warning to info (#301)

* closes 305 and further fixes finding local downloads from uncommon ytdlp extractors

* use ffmpeg -bitexact to reduce duplicate content storing

* formatting

* adds yt-dlp curl-cffi

* version bump

* linting

---------

Co-authored-by: Dave Mateer <davemateer@gmail.com>
2025-06-02 20:57:12 +01:00
msramalho
69028588b3 linting 2025-06-02 20:04:34 +01:00
msramalho
b351a33593 version bump 2025-06-02 20:03:48 +01:00
msramalho
87e1cdc102 adds yt-dlp curl-cffi 2025-06-02 20:02:35 +01:00
msramalho
4170c2011c formatting 2025-06-02 19:33:55 +01:00
msramalho
dd4e372703 use ffmpeg -bitexact to reduce duplicate content storing 2025-06-02 19:33:53 +01:00
msramalho
b9f7927a3b closes 305 and further fixes finding local downloads from uncommon ytdlp extractors 2025-06-02 19:14:09 +01:00
msramalho
d99b7c9efe Merge remote-tracking branch 'origin/main' into dev 2025-06-02 13:25:34 +01:00
Dave Mateer
48be13fb2a catch for if self.comments are true but no actual comments in video (#303)
* catch for if self.comments are true but no actual comments in video

* simplifies check code

---------

Co-authored-by: Miguel Sozinho Ramalho <19508417+msramalho@users.noreply.github.com>
2025-06-02 13:02:19 +01:00
Dave Mateer
4aae5047f5 Changed log level on gsheet_feeder_db started from warning to info (#301) 2025-06-02 12:53:21 +01:00
msramalho
258e56aa26 poetry updates 2025-06-02 12:52:26 +01:00
msramalho
9ad6213efa npm version bump on scripts/settings 2025-06-02 12:47:31 +01:00
msramalho
2f36e50e0b bumps browsertrix in docker image 2025-06-02 12:06:14 +01:00
msramalho
2d7206f99d REMOVES vk_extractor until further notice 2025-06-02 12:06:02 +01:00
msramalho
ac24fd8f49 improves generic extractor edge-cases and yt-dlp updates 2025-06-02 12:03:51 +01:00
msramalho
ee3e871dd8 wacz: allow exceptional cases where more than one resource image is available 2025-05-28 11:53:29 +01:00
msramalho
e6fdef66df improves instructions on docker setup with an example URL 2025-04-28 11:16:01 +01:00
msramalho
5cf640af8a experiments with seleniumbase 2025-04-28 11:08:00 +01:00
Miguel Sozinho Ramalho
33cacd145f Update tests-download.yaml
to shift ownership of notifications
2025-04-07 21:15:18 +01:00
Miguel Sozinho Ramalho
0f69b5fe0c update repo badges 2025-03-31 16:19:29 +01:00
Erin Clark
ad2e8397b2 Merge pull request #287 from bellingcat/fix/insta_tbot_empty
Only return success for instagram_tbot_extractor.py with content.
2025-03-31 14:31:46 +01:00
erinhmclark
144adaad5b Only return success for instagram_tbot_extractor.py with content. 2025-03-31 14:14:36 +01:00
Erin Clark
c7c7eb00a1 Merge pull request #286 from bellingcat/version_comparison
Small code fixes and GH Actions cache
2025-03-31 12:40:42 +01:00
erinhmclark
7e4ba62918 Small code change 2025-03-31 12:05:39 +01:00
erinhmclark
9c2b506189 update runner os to matrix os. 2025-03-31 12:00:24 +01:00
erinhmclark
8940580638 Add poetry cache clear, and small code change 2025-03-31 11:41:26 +01:00
erinhmclark
c2821d7c83 Fix poetry install deletion 2025-03-31 11:25:51 +01:00
erinhmclark
a590647279 Small code tidy to trigger tests. 2025-03-31 11:23:49 +01:00
erinhmclark
1edfdae03e Update download tests to match cache process. 2025-03-31 11:17:40 +01:00
erinhmclark
6c7f6af4b4 Add cache action with key to OS, py version and lockfile hash, and install packages from source. 2025-03-31 11:11:56 +01:00
Erin Clark
8685b6bf13 Merge pull request #285 from bellingcat/fix-ubuntu-22
[WIP] Change order of poetry install - in case this fixes core tests
2025-03-28 15:38:03 +00:00
Patrick Robertson
0ce7f5a1b5 Disable caching 2025-03-28 18:40:02 +04:00
Patrick Robertson
85d3f2fa02 Revert changes 2025-03-28 18:36:11 +04:00
Patrick Robertson
fd540bd03a Code change to trigger tests 2025-03-28 18:29:59 +04:00
Patrick Robertson
86f328515c Use cache key that includes os version 2025-03-28 18:29:52 +04:00
erinhmclark
68992025b0 Update version comparison. 2025-03-28 14:29:44 +00:00
Patrick Robertson
6544934825 Merge pull request #283 from bellingcat/1.0-release
v1.0.0 release 🎉
2025-03-28 18:06:59 +04:00
Patrick Robertson
197599b406 Merge pull request #284 from bellingcat/revert-downloads-test
Revert downloads CI tests changes
2025-03-28 18:06:49 +04:00
Erin Clark
96efdcbba1 Merge pull request #281 from bellingcat/add_inst_api_script
Add InstagrAPI server script to be used with the Instagram API Extractor.
2025-03-28 13:58:37 +00:00
Patrick Robertson
2ec494b4b9 Revert downloads CI tests changes
It wasn't properly being triggered after the core tests. this reverts so that the download tests just run whatever
2025-03-28 17:55:58 +04:00
Erin Clark
1d18399d70 Merge pull request #222 from bellingcat/feat/yt-dlp-pots
yt-dlp proposed extractor_args and PO Token client.
2025-03-28 13:54:27 +00:00
Patrick Robertson
3550a009e6 v1.0.0 release 🎉 2025-03-28 13:53:29 +00:00
erinhmclark
dd7d85b4b4 Lock 2025-03-28 13:47:18 +00:00
erinhmclark
c510c04643 Update config reference in test_generic_extractor.py 2025-03-28 13:43:46 +00:00
erinhmclark
a0d955fe84 lock 2025-03-28 13:39:58 +00:00
erinhmclark
5e7c57650b Update "default" to "auto" for clarity, update docs 2025-03-28 13:16:16 +00:00
erinhmclark
1db7d6702d Update the documentation 2025-03-28 12:27:18 +00:00
erinhmclark
b1a8792f9f Remove duplicate line 2025-03-28 11:44:37 +00:00
erinhmclark
f715100dd5 Add run_instagrapi_server.sh and update docs 2025-03-28 11:31:23 +00:00
erinhmclark
dbcf19d1b8 Update update path reference 2025-03-28 10:55:21 +00:00
erinhmclark
0840b7283c Format 2025-03-28 10:43:00 +00:00
erinhmclark
b5dc1854a2 Merge branch 'main' into feat/yt-dlp-pots 2025-03-28 10:42:24 +00:00
erinhmclark
efab0f9a91 Add test 2025-03-28 10:37:22 +00:00
erinhmclark
bc35116975 Update poetry.lock 2025-03-28 10:37:13 +00:00
Patrick Robertson
25f1f5dc93 Merge pull request #279 from bellingcat/telethon_tweaks
Fix calling extractor.cleanup (fixes telethon issue) + tidy up telethon extractor session file naming
2025-03-28 14:13:26 +04:00
erinhmclark
f99dcc63a1 Minor updates 2025-03-28 09:46:44 +00:00
Patrick Robertson
48fbfc3b86 Merge pull request #280 from bellingcat/download-tests
Download tests
2025-03-28 13:33:30 +04:00
Erin Clark
e7aae76ffe Merge pull request #271 from bellingcat/dependabot/github_actions/actions-7fa5136294
Bump the actions group with 3 updates
2025-03-28 09:33:25 +00:00
erinhmclark
1466700b45 Small updates to docs, poetry.lock 2025-03-28 08:23:10 +00:00
erinhmclark
00b29db390 Update documentaion for instagrapi api 2025-03-28 00:41:35 +00:00
erinhmclark
2a0dfaead2 Add instagrapi server scripts 2025-03-28 00:41:05 +00:00
Patrick Robertson
a448e2532c Code tweak for clarity 2025-03-27 15:20:52 +04:00
Patrick Robertson
46a51cce11 Fix up tests-download to properly run once core tests completed 2025-03-27 15:18:58 +04:00
Patrick Robertson
b7949a489f Simplify telethon unit tests for CI (don't use TestExtractorBase - it causes loading issues) 2025-03-26 23:51:21 +04:00
Patrick Robertson
e0e9f93065 Skip update checks for ytdlp when running tests 2025-03-26 23:41:20 +04:00
Patrick Robertson
e06b0c0585 Skip checking if docker is running for tests + more graceful test for filename 2025-03-26 23:03:48 +04:00
Patrick Robertson
95ea9fb231 Telethon unit tests + tidyup 2025-03-26 22:53:27 +04:00
Patrick Robertson
17d2d14680 Fix running 'cleanup' method on extractors that fail to start 2025-03-26 22:52:52 +04:00
erinhmclark
f54b5c5f18 Update poetry.lock 2025-03-26 18:05:04 +00:00
erinhmclark
456b2746c8 Update the docs 2025-03-26 18:01:20 +00:00
erinhmclark
2cad5edea8 Fix default config 2025-03-26 17:33:00 +00:00
Patrick Robertson
580de88366 Set the new session filename *before* copying
Fixes a potential bug whereby if the copy fails for some reason, the 'cleanup' command would remove the original session file
2025-03-26 21:32:23 +04:00
erinhmclark
093ce34a6a Ruff format. 2025-03-26 17:02:20 +00:00
erinhmclark
7872d9356c Merge branch 'main' into feat/yt-dlp-pots 2025-03-26 17:00:38 +00:00
erinhmclark
23e7dd0995 Remove old implementaion 2025-03-26 17:00:31 +00:00
erinhmclark
565275ac37 Basic documentation for POT process 2025-03-26 16:59:01 +00:00
erinhmclark
4a02407659 Typo fix. 2025-03-26 16:46:21 +00:00
erinhmclark
ae523eb06f Udpate PO token generation script method 2025-03-26 16:45:29 +00:00
erinhmclark
d87c0dc3a9 Implement update for pot plugin. 2025-03-26 16:02:29 +00:00
dependabot[bot]
1612fef59b Bump the actions group with 3 updates
Bumps the actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [docker/login-action](https://github.com/docker/login-action) and [docker/metadata-action](https://github.com/docker/metadata-action).


Updates `actions/checkout` from 3 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

Updates `docker/login-action` from 3.3.0 to 3.4.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](9780b0c442...74a5d14239)

Updates `docker/metadata-action` from 5.6.1 to 5.7.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](369eb591f4...902fa8ec7d)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 12:09:25 +00:00
Patrick Robertson
fbf51f61b9 Merge pull request #276 from bellingcat/actions_updates
Only run 'download' actions once core completes + re-add `ubuntu-latest` to matrix
2025-03-26 12:08:13 +00:00
Patrick Robertson
a9ff55a36e Merge pull request #278 from bellingcat/dependabot_fix
This force-pins cryptography to >44.0.1 to fix dependabot warning
2025-03-26 11:57:35 +00:00
Patrick Robertson
20bc80b9ef Slightly more consistent/tidier naming for the session files
Don't add/remove .session from name, keep the file name without .session at all times
2025-03-26 15:57:11 +04:00
Patrick Robertson
5bb0cbf3ff Lock poetry file 2025-03-26 15:43:03 +04:00
Patrick Robertson
3eb9ffddfe This force-pins cryptography to >44.0.1 to fix dependabot warning
pyOpenSSL also no longer needed
2025-03-26 15:39:53 +04:00
Patrick Robertson
76e90dd23a Small code tidy ups 2025-03-26 15:34:33 +04:00
Patrick Robertson
0450d3fcb9 Merge branch 'main' into actions_updates 2025-03-26 15:29:38 +04:00
Patrick Robertson
e9ee4d67ba Re-add 'ubuntu-latest' - now that we're rid of tsp_client 2025-03-26 15:29:36 +04:00
Patrick Robertson
43a80dbcda Merge pull request #224 from bellingcat/timestamping_rewrite
Timestamping rewrite
2025-03-26 11:25:55 +00:00
Patrick Robertson
cb3ae055d6 Also remove certvalidator from poetry/project 2025-03-26 15:11:25 +04:00
Patrick Robertson
4cfa6455c7 Only make the downloads action run if the core action was successful 2025-03-26 15:07:57 +04:00
Patrick Robertson
0073a08525 Update manifest dependencies to remove tsp_client et al. 2025-03-26 14:57:55 +04:00
Patrick Robertson
46e31808f6 Version bump 2025-03-26 14:54:33 +04:00
Patrick Robertson
4af23e13d1 Bump rfc3161-client to 1.0.1 2025-03-26 14:50:12 +04:00
Patrick Robertson
d6be1ff84f Merge branch 'main' into timestamping_rewrite 2025-03-26 14:37:51 +04:00
erinhmclark
633290a9cc Update for pot providers list 2025-03-25 18:27:06 +00:00
erinhmclark
040a864d5c Merge branch 'refs/heads/main' into feat/yt-dlp-pots
# Conflicts:
#	poetry.lock
2025-03-25 18:26:43 +00:00
erinhmclark
b4c33318c4 Merge branch 'main' into feat/yt-dlp-pots
# Conflicts:
#	src/auto_archiver/modules/generic_extractor/__manifest__.py
#	tests/test_modules.py
2025-03-25 15:16:31 +00:00
Patrick Robertson
74974ef0ed Merge pull request #268 from bellingcat/minor_improvements
Minor improvements
2025-03-25 12:52:08 +00:00
Patrick Robertson
5c6005d843 Merge pull request #269 from bellingcat/update-dependabot
Add explicit dependabots for pip/poetry, GH actions and npm
2025-03-25 06:30:24 +00:00
Patrick Robertson
d6a7f31248 Add note that authentication only works for some modules 2025-03-24 18:28:35 +04:00
Patrick Robertson
8aba663534 Update node module versions 2025-03-24 18:28:30 +04:00
Patrick Robertson
ace97ac7fd Don't run ruff on non-python file changes 2025-03-24 18:00:14 +04:00
Patrick Robertson
ad373ae733 Add explicit dependabots for pip/poetry, GH actiona and npm 2025-03-24 17:57:53 +04:00
Patrick Robertson
260e76dd3d Update dependencies 2025-03-24 17:48:25 +04:00
Patrick Robertson
a9fe959ea1 Fix unit tests for latest yt-dlp
(Yt-dlp title is now truncated)
2025-03-24 17:48:15 +04:00
Patrick Robertson
beb7f3893d Add comments/notes to WACZ enricher about browser profiles 2025-03-24 17:39:47 +04:00
Patrick Robertson
5055402c2a Bump browsertrix version 2025-03-24 17:39:44 +04:00
Patrick Robertson
3c4625d708 Further ruff tweaks 2025-03-24 16:39:59 +04:00
Patrick Robertson
31fa7380f5 Fix up unit tests + issue when working with self-signed certs 2025-03-24 16:00:40 +04:00
Patrick Robertson
396ec03bae Tidy up unit tests further + make more non-download 2025-03-24 15:26:22 +04:00
Patrick Robertson
e811196711 Ruff fixes 2025-03-24 15:10:46 +04:00
Patrick Robertson
dfde6f1995 Merge main into timestamping_enricher 2025-03-24 15:09:29 +04:00
Miguel Sozinho Ramalho
7b454baa02 Create dependabot.yml 2025-03-24 10:49:36 +00:00
Patrick Robertson
0f9c6a9a5c Update yt-dlp to latest 2025-03-24 14:49:18 +04:00
Patrick Robertson
c980500978 Actually restart AA after updating yt-dlp.
A simple 'importlib.reload()' doesn't take into account all imports
2025-03-24 14:33:59 +04:00
Patrick Robertson
01516724d3 Merge pull request #264 from bellingcat/minor_fixes
Minor fixes
2025-03-21 10:49:39 +00:00
Patrick Robertson
a066bf4ca9 Clean up comments 2025-03-21 14:47:50 +04:00
Patrick Robertson
2233af81f7 Version bump 2025-03-21 14:33:08 +04:00
Patrick Robertson
aacb874b56 removeprefix for www. is required here 2025-03-21 12:23:45 +04:00
Patrick Robertson
4b5a8c0199 Add warning *inside* instagram_extractor that it's not actively maintained 2025-03-21 12:09:58 +04:00
Patrick Robertson
14c56f4916 Provide better logs for screenshot enricher when auth is/isn't supported (cookies only) 2025-03-21 12:05:47 +04:00
Patrick Robertson
5b131996c6 Add return type for auth_for_site 2025-03-21 11:55:12 +04:00
Patrick Robertson
168dfb6254 Unit tests for url utils 2025-03-21 11:53:47 +04:00
Patrick Robertson
42e16aebd6 Merge pull request #255 from bellingcat/autogenerate_services_account
Script to auto-generate a service account
2025-03-20 18:00:45 +00:00
Patrick Robertson
d6d5a08204 Allow user to save downloaded keyfile to a different folder 2025-03-20 20:45:28 +04:00
Patrick Robertson
e6c5705f70 Merge pull request #261 from bellingcat/wacz_separate_profile
Wacz minor adjustments
2025-03-20 15:51:56 +00:00
Erin Clark
613ba0c05d Merge pull request #262 from bellingcat/generic_extractor_args
Add flexible extractor_args to generic_extractor.py

This allows users to pass any of the options listed [here](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#extractor-arguments) to yt-dlp extractor_args.

example usage:

```
generic_extractor:
  facebook_cookie:
  ...
  extractor_args:
    youtube:
      player_client: web,tv
    generic:
      is_live: true
```
2025-03-20 15:38:20 +00:00
Patrick Robertson
b997bbea2b Merge pull request #263 from bellingcat/wrong_steps
When loading modules, check they have been added to the right 'step' in the config
2025-03-20 15:31:38 +00:00
erinhmclark
54f53886ef Update tests for default config values 2025-03-20 14:57:26 +00:00
Patrick Robertson
0a5ba3385e Fix small bug in twitter dropin
- previously the 'content' was being set to a json dump of the tweet, it should be set to full_text
2025-03-20 18:55:22 +04:00
Patrick Robertson
034857075d Merge branch 'main' into wrong_steps 2025-03-20 18:44:19 +04:00
Patrick Robertson
6700250891 Add a test for checking module type on setup 2025-03-20 18:18:53 +04:00
Patrick Robertson
5e5e1c43a1 When loading modules, check they have been added to the right 'step' in the config
Fixes an issue seen on discord where a user accidentally set up metadata_enricher under 'extractors'
2025-03-20 18:09:26 +04:00
Patrick Robertson
1e19ad77c6 Fix tests 2025-03-20 18:08:19 +04:00
Patrick Robertson
f22af5e123 Tweak WACZ enricher docs + add comment on WACZ_ENABLE_DOCKER 2025-03-20 16:48:30 +04:00
Patrick Robertson
799cef3a8c Cleanup docker-compose 2025-03-20 16:48:30 +04:00
erinhmclark
2921061fde Add flexible extractor_args to generic_extractor.py 2025-03-19 19:19:28 +00:00
Patrick Robertson
e531906d73 Create an independent profile file for each wacz_extractor_enricher instance 2025-03-19 18:08:24 +04:00
Patrick Robertson
244341d22c Skip check for 'docker' bin dependency if already running in docker 2025-03-19 18:08:04 +04:00
Erin Clark
90932a7bc8 Merge pull request #259 from bellingcat/fix_youtube_generic
Small fix for generic_extractor.py for general/ youtube extraction.
2025-03-19 11:52:56 +00:00
Patrick Robertson
488675056b Download generate_google_services.sh script from GH - it's not packaged with the app 2025-03-19 15:52:39 +04:00
erinhmclark
93921e71d4 Clarify comments in pot scripts. 2025-03-19 11:33:35 +00:00
erinhmclark
675de50ee7 Update module test to test for default config keys within loaded 2025-03-19 10:47:28 +00:00
erinhmclark
fc6946f78a Run format. 2025-03-18 21:43:18 +00:00
erinhmclark
2fdf6b7564 Update generic_extractor.py for general/ youtube extraction. 2025-03-18 21:33:21 +00:00
erinhmclark
a577228465 Update generic_extractor.py for general/ youtube extraction. 2025-03-18 21:10:06 +00:00
erinhmclark
ba9d67e4bb Merge branch 'main' into feat/yt-dlp-pots 2025-03-18 20:10:38 +00:00
erinhmclark
c4e63ebd8c Add conditional check to setup bgutils token generation script.
TODO: Update tests
2025-03-18 14:54:57 +00:00
Miguel Sozinho Ramalho
f6863b8eb2 Update src/auto_archiver/modules/gsheet_feeder_db/__manifest__.py 2025-03-18 14:10:47 +00:00
erinhmclark
b83bfda187 Update directory location, add .gitignore 2025-03-18 14:10:20 +00:00
Miguel Sozinho Ramalho
5c34ac1293 Update docs/source/how_to/gsheets_setup.md 2025-03-18 14:05:23 +00:00
erinhmclark
cb632723bd Add scripts to pull only /server/ section of pots generator, adn only install at runtime. 2025-03-18 13:47:01 +00:00
Patrick Robertson
7d972ee9b8 Merge pull request #258 from bellingcat/version_bump
Version bump
2025-03-18 12:18:09 +00:00
Patrick Robertson
b64826dc16 Merge pull request #257 from bellingcat/standardise_parsedates
Standardise parse dates to get_datetime_from_str
2025-03-18 12:17:51 +00:00
erinhmclark
0c892f3cf1 Temp fix for tests by setting path in manifest. 2025-03-18 11:44:08 +00:00
Patrick Robertson
23e74803ee Version bump 2025-03-18 10:52:23 +00:00
Patrick Robertson
d03ecdb037 Standardise parse dates to get_datetime_from_str 2025-03-18 10:22:58 +00:00
Patrick Robertson
a5ebbf4726 Merge pull request #256 from bellingcat/dropin_cleanup
Refactor the dropin 'is_suitable' method + fix for tikwm
2025-03-18 10:08:24 +00:00
Patrick Robertson
89e387030d Tests for suitable URLs for tikwm 2025-03-18 10:04:03 +00:00
Patrick Robertson
8ec053ed1b Refactor the dropin 'is_suitable' method + fix tikwm implementation
Makes it easier to maintain/understand.
2025-03-18 09:14:14 +00:00
erinhmclark
43ef8f2aeb Add update to POT setup script. 2025-03-17 20:59:34 +00:00
erinhmclark
e6b1a8c893 Add POT setup script. 2025-03-17 20:34:00 +00:00
erinhmclark
8548b7def7 Refactor setup method to pull and transpile the token generator. 2025-03-17 18:53:59 +00:00
Patrick Robertson
29db537fab Docs on using the script to auto-generate service accounts 2025-03-17 18:11:18 +00:00
erinhmclark
bbe25537c7 Merge branch 'main' into feat/yt-dlp-pots 2025-03-17 16:54:29 +00:00
Patrick Robertson
c4a3a45bf7 Script to auto-generate a service account 2025-03-17 15:42:43 +00:00
erinhmclark
5daeae994a Fix the extractor args for new list structure. 2025-03-17 14:17:31 +00:00
Patrick Robertson
3ea02c115e Merge pull request #254 from bellingcat/rtd_docs
Add info on building RTD versions + automated building of tagged versions
2025-03-17 13:01:20 +00:00
Patrick Robertson
ab03e48708 Add info on building RTD versions + automated building of tagged versions 2025-03-17 12:52:04 +00:00
Patrick Robertson
3d4056ef70 Merge pull request #223 from bellingcat/facebook_extractor
Create facebook dropin - working for images + text.
2025-03-17 12:45:05 +00:00
Patrick Robertson
51041bf91e Merge pull request #253 from bellingcat/settings_page
Update material version, minify code
2025-03-17 11:59:37 +00:00
erinhmclark
f5bbfe5d1c Merge branch 'main' into feat/yt-dlp-pots 2025-03-17 10:43:35 +00:00
Patrick Robertson
f56cd6891b Finish incomplete sentence 2025-03-17 10:33:50 +00:00
Patrick Robertson
0765640bff Fix up tiktok dropin for slightly modified generic_extractor format 2025-03-17 10:31:22 +00:00
Patrick Robertson
06b1f4c0ca Fix lingering merge conflict issues 2025-03-17 10:12:55 +00:00
Patrick Robertson
59b910ec30 Merge main 2025-03-17 10:05:11 +00:00
Patrick Robertson
7e360240bf Copy ytdlp code into AA project - seems like ytdlp won't be merged anytime soon 2025-03-17 09:57:05 +00:00
Patrick Robertson
9e03d745d8 Add '-it' to the list of docker flags, so that docker gives a colour log output 2025-03-17 09:45:12 +00:00
Patrick Robertson
7badf89c28 Create the 'secrets' folder if it doesn't exist on first run
Easier setup for users
2025-03-17 09:40:46 +00:00
Patrick Robertson
d59530c8e7 Fix if logic bug 2025-03-17 09:40:27 +00:00
Patrick Robertson
0ec5451f66 Nicer error log when no URLs provided for CLI feeder - don't need the stacktrace 2025-03-17 09:34:33 +00:00
Patrick Robertson
99e9ac2465 Fix 'Syntax Error' warning in python3.12+ 2025-03-17 09:29:51 +00:00
Patrick Robertson
42162c5e3f Various docs improvements based on Friday Office Hours discussion 2025-03-17 09:23:43 +00:00
Patrick Robertson
3afe519176 Fix link to module types in config editor 2025-03-17 09:17:17 +00:00
Patrick Robertson
f13349bacf Fix incorrect path in cp 2025-03-16 10:33:52 +00:00
Patrick Robertson
92c79ed994 Remove schema.json file from git - is auto-generated on release 2025-03-16 10:27:08 +00:00
Patrick Robertson
2643b8e717 Update material version, minify code 2025-03-16 10:22:54 +00:00
Patrick Robertson
b2238427a0 Merge pull request #251 from bellingcat/ruff-check
Fix pre-commit for ruff check
2025-03-14 14:22:03 +00:00
Patrick Robertson
282380d8cc Add note on skipping pre-commit hook 2025-03-14 14:20:32 +00:00
Patrick Robertson
6920585f6d Version bump to 0.13.6 2025-03-14 13:42:58 +00:00
Patrick Robertson
17463de937 Merge pull request #247 from bellingcat/opentimestamps
Opentimestamps Module
2025-03-14 13:41:46 +00:00
Patrick Robertson
29cc1d317f Fix pre-commit for ruff check 2025-03-14 13:40:57 +00:00
Patrick Robertson
733aef0b08 Merge pull request #246 from bellingcat/webdriver-cookies
Better checking of cookies to add to webdriver + generic extractor tweaks
2025-03-14 13:35:58 +00:00
erinhmclark
562d06916e Revert pre commit 2025-03-14 13:08:57 +00:00
Patrick Robertson
b21467c922 Fix ruff checks 2025-03-14 12:59:37 +00:00
Patrick Robertson
a8e5585e6c github format 2025-03-14 12:52:01 +00:00
Patrick Robertson
abaeec0cc6 Add ruff check 2025-03-14 12:48:06 +00:00
Patrick Robertson
19715c8ec2 Merge branch 'main' into webdriver-cookies 2025-03-14 12:44:48 +00:00
Patrick Robertson
17ae75fb95 Ruff fixes 2025-03-14 12:38:12 +00:00
Patrick Robertson
b8da7607e8 Merge branch 'main' into opentimestamps 2025-03-14 12:36:03 +00:00
Erin Clark
a01a873f37 Merge pull request #244 from bellingcat/linting_etc
Add Linting and Formatting with Ruff.

- Add pre-commit hook for formatting (not lint fixing, this should be done manually)
- Move Makefile to project root and add commands for linting, test, docs, Docker
- Add GH Action for ruff to check linting and formatting
- Make suggested changes from current ruff configs
2025-03-14 12:27:04 +00:00
erinhmclark
72f48f0147 Fix merge conflicts. 2025-03-14 12:11:24 +00:00
erinhmclark
846474a4e2 Merge branch 'main' into linting_etc 2025-03-14 10:50:13 +00:00
Patrick Robertson
f504d2e304 Merge branch 'main' into webdriver-cookies 2025-03-14 09:37:12 +00:00
Patrick Robertson
5f7a8b1ac0 Merge pull request #249 from bellingcat/tikwm_dropin
Move tikwm extractor into a droping for the generic extractor
2025-03-14 09:28:37 +00:00
erinhmclark
4af3cd7b2a Revert ruff to separate commands. 2025-03-13 21:47:09 +00:00
erinhmclark
ad2784c5de Update style_guide.md 2025-03-13 20:52:21 +00:00
erinhmclark
c7c24fbaf2 Update style_guide.md to clarify pre-commit setup, add Docker commands to Makefile and merge ruff actions. 2025-03-13 20:26:29 +00:00
msramalho
4d67dce4c8 minor log fix 2025-03-13 19:24:05 +00:00
Patrick Robertson
f6b13327f0 Tweaks and additional debug logging 2025-03-13 17:41:41 +00:00
Patrick Robertson
589c834047 Fix parsing ytdlp args - we should first run them through the parse_options method 2025-03-13 17:41:40 +00:00
Patrick Robertson
0efeaaabb1 Revert to using time.sleep and .click() - since we only want to be waiting the first time (for the page to load) 2025-03-13 17:41:16 +00:00
Patrick Robertson
b908655cc8 Remove references to litecoin + several tidy-ups 2025-03-13 17:40:00 +00:00
Patrick Robertson
2e25e59fa6 Fix unit tests - make caplog checks more robust, having added a new logger/debug call 2025-03-13 16:07:49 +00:00
Patrick Robertson
10ceb7aa15 Move tikwm extractor into a droping for the generic extractor 2025-03-13 15:59:42 +00:00
erinhmclark
0bef78b0b4 Remove autouse property of mock_sleep. 2025-03-13 15:23:35 +00:00
Patrick Robertson
15222199d9 Add unit test for if one calendar fails 2025-03-13 14:45:38 +00:00
Patrick Robertson
e7489ac4c4 Tidy up opentimestamps
* Simplify
* Don't add fake (pending) attestations if the calendar urls all have issues
* Remove unnecessary configs
* Improve docs on upgrading + verifying
2025-03-13 14:30:33 +00:00
erinhmclark
16012df30b Revert exception check in test. 2025-03-13 13:57:04 +00:00
erinhmclark
8673bc5979 Fix unused imports and include rule. 2025-03-13 13:55:31 +00:00
erinhmclark
e76551ba22 Add documentation, pre-commit hook, more make commands and 2025-03-13 13:21:32 +00:00
erinhmclark
6e52a534e7 More fixes from Bugbear suggestions 2025-03-12 16:07:05 +00:00
erinhmclark
753c3c6214 Linting tests 2025-03-12 14:27:45 +00:00
Patrick Robertson
1d664524eb Add info on last check/last updated to the metadata 2025-03-12 11:54:25 +00:00
Patrick Robertson
394b8b2dd1 Improvements to opentimestamps enricher - make OTS file a sub-file of original media 2025-03-12 11:45:13 +00:00
erinhmclark
79f576be1d Run fix on tests. 2025-03-12 10:38:16 +00:00
erinhmclark
94aeee8313 Move Makefile to the root of the project and add commands for tests, linting and running docker. 2025-03-12 10:37:30 +00:00
erinhmclark
abc90b19d5 Update pyproject.toml 2025-03-12 10:35:56 +00:00
Patrick Robertson
1423c10363 Finish off timestamping module 2025-03-12 10:24:57 +00:00
erinhmclark
8ca7698fa0 Move Makefile and fix import error with unused import. 2025-03-11 19:58:02 +00:00
Patrick Robertson
28041d94d9 Add unit tests for opentimestamps enricher 2025-03-11 17:33:54 +00:00
Patrick Robertson
b70ed97ffd Create opentimestamps module 2025-03-11 17:28:28 +00:00
erinhmclark
28c5396b74 Move ruff to dev dependencies. 2025-03-11 17:25:24 +00:00
Patrick Robertson
94543e9a67 Merge branch 'main' into opentimestamps 2025-03-11 17:21:34 +00:00
Patrick Robertson
37eac64442 Remove desc 2025-03-11 17:10:44 +00:00
Patrick Robertson
89ee6f19b6 List out all valid TSAs + option for users to allow self signed if they want 2025-03-11 16:12:13 +00:00
Patrick Robertson
294033f156 Fix bug ordering tsr that only have one cert + more unit tests 2025-03-11 15:44:04 +00:00
Patrick Robertson
7a81ab617a Better checking of cookies to add to webdriver 2025-03-11 11:57:25 +00:00
Patrick Robertson
2ffe124d95 Add unit test for invalid digicert tsrs 2025-03-11 11:13:36 +00:00
Patrick Robertson
1db8be91db Improved unit tests for timestamping 2025-03-11 11:08:52 +00:00
erinhmclark
81aa343f21 Merge main. 2025-03-11 10:45:07 +00:00
erinhmclark
441f341139 Merge branch 'main' into linting_etc
# Conflicts:
#	src/auto_archiver/core/consts.py
#	src/auto_archiver/core/orchestrator.py
#	src/auto_archiver/core/storage.py
#	src/auto_archiver/modules/local_storage/local_storage.py
#	src/auto_archiver/modules/s3_storage/s3_storage.py
#	tests/storages/test_S3_storage.py
#	tests/storages/test_local_storage.py
#	tests/storages/test_storage_base.py
2025-03-11 10:39:47 +00:00
Patrick Robertson
e2442b2f6b Merge pull request #243 from bellingcat/fix-long-path-names
Unit tests for storage types + fix storage too long issues for local storage
2025-03-11 10:05:09 +00:00
Patrick Robertson
3f6acc0917 fully working timestamping enricher 2025-03-11 10:04:46 +00:00
erinhmclark
e7fa88f1c7 Implementing ruff suggestions. 2025-03-10 21:45:30 +00:00
erinhmclark
ca44a40b88 Ruff fix on src. 2025-03-10 19:03:45 +00:00
erinhmclark
85abe1837a Ruff format with defaults. 2025-03-10 18:44:54 +00:00
Miguel Sozinho Ramalho
3fcec57492 minor string fix 2025-03-10 17:17:59 +00:00
Patrick Robertson
2b91dc9514 Fix up unit tests 2025-03-10 16:51:16 +00:00
Patrick Robertson
a9c3477289 Improve docs on the path_generator and filename_generator config options 2025-03-10 16:43:14 +00:00
Patrick Robertson
770f4c8a3d Refactoring of storage code:
1. Fix some bugs in local_storage
2. Refactor unit tests to not set Media.key explicitly (unless it's well-known beforehand, which it isn't)
3. Limit length of URL for 'url' type path_generator
4. Throw an error if 'save_to' of local storage is too long
5. A few other tidyups
2025-03-10 16:39:48 +00:00
erinhmclark
cbb0414e5f Switch to ruff 2025-03-10 16:05:23 +00:00
erinhmclark
f4f2424eb5 Add black and flake8 2025-03-10 13:15:11 +00:00
Miguel Sozinho Ramalho
58bd38e292 Adds new extractor for tiktok via unofficial API (#237)
* minor update to defaults in api_db

* readme typo

* adds and tests new tikwm tiktok downloader

* addresses PR comments
2025-03-10 11:56:45 +00:00
Patrick Robertson
e89a8da3b4 Unit tests for storage types + fix storage too long issues for local storage 2025-03-10 11:30:15 +00:00
Erin Clark
ce46a8a7ac Merge pull request #240 from bellingcat/update_release
Update project version to: 0.13.5.

Update the release process docs and the latest version in pyproject.toml
2025-03-07 18:21:17 +00:00
erinhmclark
7e10040bbd Update the release description to tag on release 2025-03-07 18:04:51 +00:00
erinhmclark
b386ae6287 Add poetry.lock and pyproject.toml paths to trigger tests. 2025-03-07 18:01:08 +00:00
erinhmclark
1a2d9de819 Update the release process docs and the latest version in pyproject.toml 2025-03-07 17:33:32 +00:00
erinhmclark
76bb1496c8 Merge branch 'main' into feat/yt-dlp-pots
# Conflicts:
#	src/auto_archiver/modules/generic_extractor/__manifest__.py
2025-03-07 16:54:01 +00:00
Erin Clark
4c21795d5f Merge pull request #226 from bellingcat/merge_modules
Merge modules with multi-functionality:
- gsheet_feeder and gsheet_db are now one module, gsheet_feeder_db
- atlos_feeder, atlos_db and atlos_storage are now one module, atlos_feeder_db_storage.

This pull request also add documentation and updates references.
2025-03-07 16:47:30 +00:00
Patrick Robertson
e519ba2433 Add 'reject all' cookie button 2025-03-07 16:40:34 +00:00
Patrick Robertson
a8fcd0b9a0 Further info in how to for the new config format 2025-03-07 16:37:58 +00:00
Patrick Robertson
09e09e9ab9 Document module renames in 'upgrading from 0.12' how to 2025-03-07 16:28:17 +00:00
Patrick Robertson
be513e95aa Merge branch 'main' into merge_modules 2025-03-07 16:19:51 +00:00
Patrick Robertson
3fac353407 Merge pull request #217 from bellingcat/settings_page
Settings page user interface
2025-03-07 16:10:50 +00:00
Erin Clark
928c6f88a9 Merge pull request #239 from bellingcat/fix_docker_registry_ref
Fix docker registry reference
2025-03-07 15:57:29 +00:00
erinhmclark
8fcec692b7 Add comments to highlight different steps of atlos_feeder_db_storage.py 2025-03-07 15:42:20 +00:00
erinhmclark
65109e377f Remove raising exception in atlos_feeder_db_storage.py 2025-03-07 15:39:15 +00:00
Erin Clark
85a75755e2 Merge pull request #236 from bellingcat/cleanup_fixes
Cleanup fixes
2025-03-07 15:37:05 +00:00
erinhmclark
4949e9bcd2 Update docker-publish.yaml to use docker registry. 2025-03-07 15:34:56 +00:00
erinhmclark
3877b538be Update docker-publish.yaml to use docker registry. 2025-03-07 15:31:25 +00:00
Erin Clark
2e0e989793 Merge pull request #238 from bellingcat/cache_docker_to_registry
Add cache-from and cache-to to docker-publish.yaml, using Dockerhub Registry as the cache.
2025-03-07 15:23:50 +00:00
Patrick Robertson
333201acec Merge branch 'main' into settings_page 2025-03-07 15:17:42 +00:00
erinhmclark
87ab98c270 Update docker/build-push version 2025-03-07 15:14:20 +00:00
Patrick Robertson
027985024b Merge pull request #234 from bellingcat/update_suggestions
Auto Updates
2025-03-07 15:12:03 +00:00
erinhmclark
7bbf0da0d1 Add cache-from and cache-to to docker-publish.yaml. 2025-03-07 15:09:10 +00:00
Patrick Robertson
48b29d43f7 Merge pull request #233 from bellingcat/docker-webdriver-aarch64
Docker webdriver aarch64
2025-03-07 15:04:45 +00:00
Erin Clark
8ae3d9c031 Merge pull request #235 from bellingcat/instagram_extractor_bugfix
Instagram extractor bugfix:
- Fix typo from config changes
- Add warning message to documentation to alert to it not being maintained.
2025-03-07 15:02:05 +00:00
Patrick Robertson
158e6be0b1 Don't force enable a module under all types - allows modules to be set as only feeder/only extractor etc. 2025-03-07 14:59:11 +00:00
erinhmclark
4df03255a4 Fix typo in __manifest__.py 2025-03-07 14:56:35 +00:00
Patrick Robertson
503ba3d1c1 Add note on auto updates to readme 2025-03-07 14:46:50 +00:00
erinhmclark
40e5fe7a7e Update __manifest__.py for merged Atlos module. 2025-03-07 13:46:09 +00:00
Patrick Robertson
f6f397700e Fix typo + use hidden password fields for secret info 2025-03-07 12:41:09 +00:00
erinhmclark
89d2a8bb54 Update the __manifest__.py of the Instagram Extractor. 2025-03-07 12:34:19 +00:00
Patrick Robertson
e72b3e14ba Change default height of screenshots to attempt to capture more information 2025-03-07 12:08:29 +00:00
Patrick Robertson
dba44b1ac1 Use WebDriverWait when waiting for elements in screenshot enricher 2025-03-07 12:07:54 +00:00
Patrick Robertson
e756f1504f Remove geckodriver .tar file 2025-03-07 11:52:14 +00:00
Patrick Robertson
2c5e138263 Add a note on disabling the auto-update for yt-dlp 2025-03-07 11:44:24 +00:00
erinhmclark
fb56aac15e Catch edge case to ensure iterator is reached in instagram_tbot_extractor.py 2025-03-07 11:24:25 +00:00
erinhmclark
bdd35408ce Fix ref before assignment in orchestrator.py 2025-03-07 11:23:51 +00:00
Patrick Robertson
478f0b2171 Tidy-ups to auto-updating code 2025-03-07 09:59:18 +00:00
erinhmclark
32329c6b2c Update Google Sheet how to docs. 2025-03-07 00:11:43 +00:00
erinhmclark
fa1e65f54c Fix instagram_extractor.py typo, add warning to docs, and add basic regex test. 2025-03-06 16:25:38 +00:00
erinhmclark
b9c2f98f46 Update Atlos tests 2025-03-05 21:24:38 +00:00
erinhmclark
0f911543cd Atlos refactor 2025-03-05 13:49:11 +00:00
erinhmclark
6cb7afefdc Initial Atlos merge 2025-03-05 10:24:54 +00:00
Patrick Robertson
358884c5d1 Fix unit tests for yt-dlp update 2025-03-04 17:04:23 +00:00
Patrick Robertson
be09aa927d Make 'STARTED' command INFO not warning 2025-03-04 16:51:17 +00:00
Patrick Robertson
e6a578e60e Check for auto-archiver updates and present warning if there's a newer version available 2025-03-04 16:51:17 +00:00
Patrick Robertson
0eb112431b Auto-update yt-dlp based on generic_extractor.ytdlp_update_interval (default=5 days) 2025-03-04 16:43:46 +00:00
erinhmclark
22932645aa Merge remote-tracking branch 'origin/merge_modules' into merge_modules 2025-03-04 14:07:12 +00:00
erinhmclark
d1c8d4ba0e Initial merge of Atlos Feeder and DB 2025-03-04 14:06:46 +00:00
erinhmclark
d775e4612e Update gsheet_feeder references in tests. 2025-03-04 14:06:46 +00:00
erinhmclark
077b56c150 Merge GSheet Feeder and Database. 2025-03-04 14:05:19 +00:00
erinhmclark
7e4b44883b Add temp options for testing 2025-03-04 14:03:39 +00:00
Patrick Robertson
f54d6519a8 Fix sorting of steps in the outputted file 2025-03-04 11:51:26 +00:00
Patrick Robertson
07ee773a54 Better drag & drop + keep comments in file 2025-03-04 10:54:16 +00:00
erinhmclark
77b517cfc1 Merge remote-tracking branch 'origin/feat/yt-dlp-pots' into feat/yt-dlp-pots 2025-03-03 22:02:14 +00:00
erinhmclark
2c1753e14b Added Bgutils PO token provider. 2025-03-03 21:11:41 +00:00
erinhmclark
dd07b0b830 Allow flexible extractor_args in generic_extractor.py. 2025-03-03 21:11:34 +00:00
erinhmclark
0eae2bee6a Add yt-dlp-get-pot and yt-dlp-getpot-wpc requirements. 2025-03-03 21:08:00 +00:00
erinhmclark
a705a78632 Fix instagram_extractor.py typo in config value. 2025-03-03 21:06:09 +00:00
Patrick Robertson
dcaf7639be Rename 'upgrading...' page to 'upgrading from...' because it's also valid for 0.13+ versions 2025-03-03 19:58:47 +00:00
Patrick Robertson
0b5a0fcb32 Better error logs if users have XXXX_archiver modules enabled in config 2025-03-03 19:57:09 +00:00
Patrick Robertson
1fe023cd70 Throw a nicer error if a user has an orchestration.yaml file in the old format (feeder: / archivers: / formatter: ) 2025-03-03 19:51:55 +00:00
Patrick Robertson
a47e18ef9a Bump gecko driver to 0.36.0 2025-03-03 16:00:11 +00:00
Patrick Robertson
0dfab2d1bc Add some code to attempt to click the cookies banners on various websites 2025-03-03 15:55:04 +00:00
Patrick Robertson
dea0a49600 Download correct gecko-driver for the platform + fix setting executable path when running in Docker
Fixes #232
2025-03-03 15:41:44 +00:00
Erin Clark
011ded2bde Merge pull request #225 from bellingcat/small_issues
## GSheets Columns updates
- Update the available columns in the Google Sheet Feeder and Database.
- Update the Sheet Template to reflect this.

## Other Fixes
- Ensure test file cleanup.
- Additional tests.
- Correctly mark download test.
- Small typos.
2025-03-03 13:06:27 +00:00
Patrick Robertson
a88a37d0a5 Hook in to RTD theme to set react theme 2025-03-03 11:56:23 +00:00
Patrick Robertson
a0869bb3b2 Fixed up timestamp verifying - waiting on issue with rfc-client to be fixed
Ref: https://github.com/trailofbits/rfc3161-client/issues/104#issuecomment-2693890607
2025-03-03 10:28:30 +00:00
Patrick Robertson
9845804277 Fix up TODO plus add comments on integration into RTD page 2025-03-03 09:18:19 +00:00
Patrick Robertson
cc14e5cb9f Remove extra html/head tag from page - now it's embedded in RTD 2025-03-03 09:06:40 +00:00
Patrick Robertson
6ba79049d9 Capitalize help text 2025-02-27 22:16:33 +00:00
Patrick Robertson
7620a671d1 Overwrite settings_base file 2025-02-27 22:02:44 +00:00
Patrick Robertson
54a2a19dd7 Also build auto-archiver 2025-02-27 21:56:36 +00:00
Patrick Robertson
3eb4ab41b8 Also generate the schema on each run 2025-02-27 21:38:55 +00:00
Patrick Robertson
65a9885d86 A few more manifest types 2025-02-27 21:33:04 +00:00
Patrick Robertson
4ee1e75aa2 Fix readthedocs config file 2025-02-27 21:24:34 +00:00
Patrick Robertson
1141c00e9a Remove unused files, set up for RTD 2025-02-27 21:23:38 +00:00
Patrick Robertson
15da907e81 Add a bit of typescripting 2025-02-27 15:58:30 +00:00
Patrick Robertson
2ec44f4170 Documentation on building the settings page 2025-02-27 15:42:37 +00:00
Patrick Robertson
1e92c03b1d Tweaks to settings page + more declarations in manifests 2025-02-27 15:21:11 +00:00
Patrick Robertson
efe9fdf915 Tidy ups to config editor page 2025-02-27 13:02:50 +00:00
erinhmclark
4280791f07 Fix mocking in test_wayback_enricher.py. 2025-02-27 11:25:58 +00:00
Patrick Robertson
f58f110436 Check at least 1 URL provided for new cli_feeder module rewrite 2025-02-26 17:59:13 +00:00
Patrick Robertson
70d89c71ce Fully-working settings page editor 2025-02-26 17:02:49 +00:00
Patrick Robertson
bb961b131c Turn cli_feeder *back* into a module, it's better like this for settings etc, documentation etc. 2025-02-26 15:41:33 +00:00
Patrick Robertson
e467fc90c2 Merge branch 'main' into settings_page 2025-02-26 15:32:07 +00:00
erinhmclark
8124bb831d Merge branch 'main' into small_issues
# Conflicts:
#	src/auto_archiver/core/base_module.py
#	src/auto_archiver/utils/misc.py
2025-02-26 13:19:49 +00:00
erinhmclark
b2e654aef9 Remove context manager from test_pdq_hash_enricher.py 2025-02-26 12:57:33 +00:00
erinhmclark
9157846930 Add docstrings to explain date formats. 2025-02-26 10:01:52 +00:00
Patrick Robertson
600f43e790 Set up structure for react 2025-02-26 09:34:44 +00:00
Patrick Robertson
afc117a229 Get downloading certs working 2025-02-26 09:33:56 +00:00
erinhmclark
696aafb52d Update gsheet_feeder references in tests. 2025-02-25 21:38:41 +00:00
erinhmclark
75380b0716 Merge GSheet Feeder and Database. 2025-02-25 21:32:32 +00:00
erinhmclark
35b5ab2eb1 Update poetry.lock 2025-02-25 20:17:48 +00:00
erinhmclark
83a08dd215 Update date parsing to use dateutil.parser in misc.py 2025-02-25 20:17:31 +00:00
erinhmclark
9bc6dd5c3c Add set_content into generic_extractor.py. 2025-02-25 20:07:00 +00:00
erinhmclark
cf1219f798 Add text content into gsheet. 2025-02-25 20:06:44 +00:00
Patrick Robertson
4dcb77c29f Merge branch 'main' into timestamping_rewrite 2025-02-25 17:10:55 +00:00
Patrick Robertson
1ad158c016 Merge pull request #211 from bellingcat/docs_improvements
Docs tidyups, howto on logging and authentication, remove exit(), small fixes
2025-02-25 14:13:13 +00:00
erinhmclark
1df5129268 Small typos. 2025-02-25 14:08:38 +00:00
erinhmclark
73b434aafc Tests for test_vk_extractor.py. 2025-02-25 14:08:28 +00:00
erinhmclark
2d276cb9c4 Fix tmp test file. 2025-02-25 14:08:14 +00:00
Patrick Robertson
898faf6fe4 Further WIP - currently working on verify_signed 2025-02-25 12:08:08 +00:00
Patrick Robertson
6987a4827e Set poetry packages - remove tsp_client and update cryptography 2025-02-25 11:57:20 +00:00
Patrick Robertson
f8e846d59a Create facebook dropin - working for images + text. CAVEAT: only gets the first ~100 chars of the post at the moment 2025-02-25 11:44:35 +00:00
erinhmclark
2d4f1b5b79 Added Bgutils PO token provider. 2025-02-25 10:49:57 +00:00
Patrick Robertson
d10c7fbe55 Better documentation based on the discord feedbackgst 2025-02-24 22:42:57 +00:00
Patrick Robertson
ca1ed418aa Throw an error for invalid __manifest__ syntax + fix: allow default values of False/None 2025-02-24 21:46:24 +00:00
Patrick Robertson
01bf88a695 Merge branch 'main' into timestamping_rewrite 2025-02-24 12:03:14 +00:00
erinhmclark
c5127f5fd1 Allow flexible extractor_args in generic_extractor.py. 2025-02-24 11:40:44 +00:00
erinhmclark
158d448cbc Add yt-dlp-get-pot and yt-dlp-getpot-wpc requirements. 2025-02-24 11:40:39 +00:00
Patrick Robertson
73a2e2d752 Fix tests for moving orchestration to secrets/orchestration.yaml 2025-02-21 19:05:39 +00:00
Patrick Robertson
1c17629ac6 Tweaks 2025-02-21 18:54:27 +00:00
Patrick Robertson
7562938151 Proof of concept for settings page 2025-02-21 18:04:48 +00:00
Patrick Robertson
091a19e25c Further docs improvements/tidy ups 2025-02-21 16:52:30 +00:00
Patrick Robertson
77212e8e3f Finishing touches to the how-tos 2025-02-20 15:45:48 +00:00
Patrick Robertson
9661e90a05 Allow disabling logging in auto_archiver with logging: enabled: false 2025-02-20 15:45:32 +00:00
Patrick Robertson
0bec71d203 Finish how to on authentication 2025-02-20 15:33:50 +00:00
Patrick Robertson
4174285898 Fix unit tests 2025-02-20 13:18:06 +00:00
Patrick Robertson
eda359a1ef Fix json loader - it should go in 'validators' not 'utils'
Fixes #214
2025-02-20 13:10:39 +00:00
Patrick Robertson
40488e0869 Use 'Auto Archiver' naming for consistency.
auto-archiver is reserved in the docs for when talking about the command line usage
2025-02-20 11:50:29 +00:00
Patrick Robertson
061f29c885 How-to on updating config file to version 0.13+ 2025-02-20 11:46:57 +00:00
Patrick Robertson
cbea551876 Better display name for wayback machine to emphasise it's typically used as an enricher 2025-02-20 11:46:57 +00:00
Patrick Robertson
b978484a89 Rename wacz_enricher to wacz_extractor_enricher. Fixes #205 2025-02-20 11:46:57 +00:00
Patrick Robertson
49b6c32058 Fix the 'full' mode which creates a complete config file 2025-02-20 11:34:05 +00:00
Patrick Robertson
4b51ec9ad5 Remove dangling import 2025-02-20 11:20:16 +00:00
Patrick Robertson
7734a551fa Move 'assert_valid_url' out into utils, don't use assert but raise
assert is recommended only for debugging
2025-02-20 11:19:29 +00:00
Patrick Robertson
77b2b099c6 Replace exit() with raise exceptions. Better for code implementations
exit() is reserved solely for command line-called areas now
also assert is only recommended for debugging
2025-02-20 11:19:13 +00:00
Patrick Robertson
40b8359348 Implementation test with 2 x orchestrators with different configs 2025-02-20 11:18:28 +00:00
Patrick Robertson
5ccea8e44a Absolute paths in README for Github/PyPi/Dockerhub etc. 2025-02-20 11:18:28 +00:00
Patrick Robertson
7dde8d609d Merge main 2025-02-20 10:29:57 +00:00
Patrick Robertson
6ea943b680 Fix link 2025-02-20 10:27:24 +00:00
Patrick Robertson
5211c5de18 Merge pull request #210 from bellingcat/logger_fix
Fix issue #200 + Refactor _LAZY_LOADED_MODULES
2025-02-19 15:11:42 +00:00
Erin Clark
6cdefaa751 Merge pull request #194 from bellingcat/tests/add_module_tests
Add unit tests for individual modules.
Includes a couple of small bug fixes and light refactoring.
2025-02-19 13:51:43 +00:00
Patrick Robertson
04507577b6 Version bump 2025-02-19 13:36:50 +00:00
erinhmclark
47a634fc63 Add WACZ, Wayback and local storage tests. 2025-02-19 13:14:08 +00:00
Patrick Robertson
a9802dd004 Remove the global _LAZY_LOADED_MODULES and allow each instance of ArchivingOrchestrator to load its own modules 2025-02-19 12:25:35 +00:00
erinhmclark
a8ffb19325 Fix auth key name for cookies_from_browser. 2025-02-19 10:40:54 +00:00
Patrick Robertson
222a94563f WIP: Docs tidyups+add howto on logging and authentication
(Authentication is WIP)
2025-02-19 10:37:04 +00:00
Patrick Robertson
eb60b271b9 Fix issue #200 2025-02-19 10:35:14 +00:00
erinhmclark
ddf2e76624 Include Atlos Storage __init__.py for module recognition. 2025-02-19 09:24:34 +00:00
erinhmclark
10a5ad62b8 Include Atlos tests, metadata fixture. 2025-02-19 09:18:41 +00:00
erinhmclark
f0fd9bf445 Updates tests to use pytest-mock. 2025-02-18 23:32:03 +00:00
erinhmclark
657fbd357d Merge branch 'main' into tests/add_module_tests 2025-02-18 19:47:47 +00:00
erinhmclark
7b88df72cb Update test_metadata_enricher.py 2025-02-18 19:46:57 +00:00
Patrick Robertson
3c543a3a6a Various fixes for issues with new architecture (#208)
* Add formatters to the TOC - fixes #204

* Add 'steps' settings to the example YAML in the docs. Fixes #206

* Improved docs on authentication architecture

* Fix setting modules on the command line - they now override any module settings in the orchestration as opposed to appending

* Fix tests for gsheet-feeder: add a test service_account.json (note: not real keys in there)

* Rename the command line entrypoint to _command_line_run

Also: make it clear that code implementation should not call this
Make sure the command line entry returns (we don't want a generator)

* Fix unit tests to use now code-entry points

* Version bump

* Move iterating of generator up to __main__

* Breakpoint

* two minor fixes

* Fix unit tests + add new '__main__' entry point implementation test

* Skip youtube tests if running on CI. Should still run them locally

* Fix full implementation run on GH actions

* Fix skipif test for GH Actions CI

* Add skipifs for truth - it blocks GH:

---------

Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2025-02-18 19:10:09 +00:00
erinhmclark
ce5a200d1f Added tests, updated instagram_tbot_extractor.py raise failure. 2025-02-18 12:59:10 +00:00
erinhmclark
f4c623b11b Merge branch 'main' into tests/add_module_tests 2025-02-17 09:03:04 +00:00
Patrick Robertson
6d43bc7d4d Fix generator programmatic setup (#197)
* Fix returning a generator of a generator

* Move download test test to pytest.mark.download
2025-02-15 17:36:44 +00:00
Miguel Sozinho Ramalho
9297697ef5 makes orchestrator.run return the results to allow for code integration (#196) 2025-02-15 12:41:26 +00:00
erinhmclark
8ed3ef2f33 Merge branch 'main' into tests/add_module_tests 2025-02-14 12:47:40 +00:00
Miguel Sozinho Ramalho
5614af3f63 removes fixed oscrypto dependency, it blocked pypi publishing (#195)
* disables tests on ubuntu-latest

* drops fixed oscrypto version for git commit

* version bump
2025-02-14 10:51:56 +00:00
erinhmclark
71b41dd901 Remove accidental path, yet again. 2025-02-14 10:05:32 +00:00
erinhmclark
b0756a6a34 Remove accidental full path. 2025-02-14 09:57:44 +00:00
erinhmclark
319c1e8f92 Add more tests. 2025-02-14 09:48:37 +00:00
erinhmclark
3fce593aad Merge branch 'main' into tests/add_module_tests 2025-02-12 19:33:29 +00:00
erinhmclark
cbe98c729d Enricher tests 2025-02-12 19:32:40 +00:00
Miguel Sozinho Ramalho
27f9287b65 markdown fixes 2025-02-12 17:37:36 +00:00
erinhmclark
d9d936c2ca Thumbnail enricher fix seconds to minutes. 2025-02-12 12:22:27 +00:00
Patrick Robertson
d849678137 Merge pull request #193 from bellingcat/links
Fix links to docs
2025-02-12 13:11:59 +01:00
erinhmclark
da267f20d7 Update screenshot refs 2025-02-12 11:54:40 +00:00
Patrick Robertson
70f155dfce add more of the USPs to the readme 2025-02-12 11:48:51 +00:00
Patrick Robertson
86254bdd4e Fix link in how to 2025-02-12 11:48:01 +00:00
Patrick Robertson
17f13db56c Make that code block a shell 2025-02-12 11:45:09 +00:00
Patrick Robertson
d776be8a81 Fix links to docs 2025-02-12 11:41:54 +00:00
Patrick Robertson
460a71649c Merge pull request #190 from bellingcat/docs_update
Docs improvement
2025-02-12 12:38:04 +01:00
Patrick Robertson
a0c4a82825 Improved docstrings for base modules 2025-02-12 11:32:13 +00:00
Patrick Robertson
8054ea96b3 Document double dash between command line args and feeder urls on command line 2025-02-12 11:20:15 +00:00
Patrick Robertson
de79e17128 better wording in link 2025-02-12 11:19:21 +00:00
Patrick Robertson
d28d99daa6 Docs tidy ups and re-organising 2025-02-12 11:16:17 +00:00
Patrick Robertson
5b481f72ab Tidy ups to manifests for docs 2025-02-11 20:03:53 +00:00
Patrick Robertson
4c119b4db8 Add missing manifest for atlos_storage 2025-02-11 20:03:45 +00:00
Patrick Robertson
d8f47ff9e4 Add multi-type modules to all headings on TOC 2025-02-11 19:46:57 +00:00
Patrick Robertson
1ee7981c6e Add YAML config to the module docs 2025-02-11 19:42:03 +00:00
Patrick Robertson
22fa9ba456 further tweaks 2025-02-11 18:52:28 +00:00
Patrick Robertson
1b976f4c09 Remove unused atlos util functions 2025-02-11 18:49:54 +00:00
Patrick Robertson
756f46012b Remove empty file 2025-02-11 18:47:54 +00:00
Patrick Robertson
1d69053dd5 Upgrade certifi 2025-02-11 18:41:37 +00:00
Patrick Robertson
40a95f7348 Add likify deps 2025-02-11 18:40:44 +00:00
Patrick Robertson
a307d09e67 Readthedocs now requires all packages for running the pre-build scripts 2025-02-11 18:38:41 +00:00
Patrick Robertson
2c87474186 Change path for scripts 2025-02-11 18:37:03 +00:00
Patrick Robertson
e65a99078f Slight tweaks of toctrees + reordering 2025-02-11 18:28:21 +00:00
Patrick Robertson
e43dda2817 Merge pull request #185 from bellingcat/load_modules
Refactor auto-archiver to use a modular structure for feeders/extractors/enrichers etc.
2025-02-11 19:21:46 +01:00
Patrick Robertson
3787577a96 Screenshot enricher depends on geckodriver not chromedriver 2025-02-11 18:18:52 +00:00
Patrick Robertson
d0c379a3ba WIP - timestamping enricher 2025-02-11 18:18:19 +00:00
Patrick Robertson
ea728a7a97 TODO on facebook dropin not working 2025-02-11 15:56:12 +00:00
Patrick Robertson
3163cb793a Fix timestamping enricher for new module structure (temp paths) 2025-02-11 15:26:40 +00:00
msramalho
91f1ebf7b3 fix temp for yandex new shortlink 2025-02-11 15:23:16 +00:00
msramalho
c720541de2 merge conflicts 2025-02-11 15:22:06 +00:00
Patrick Robertson
7bb4d68a22 Merge branch 'load_modules' into timestamping_rewrite 2025-02-11 15:21:31 +00:00
msramalho
e507fc81d2 improves mimetype guessing, previously file.sub.something would not have an extension 2025-02-11 15:02:49 +00:00
msramalho
5478ed3860 bsky fix media fetching 2025-02-11 15:02:00 +00:00
msramalho
47d1dc9d47 typing warnings fixed 2025-02-11 15:01:37 +00:00
Patrick Robertson
2d87935042 Start on opentimestamps enricher 2025-02-11 14:54:46 +00:00
Patrick Robertson
62154ddfef Further tweaks and fixes 2025-02-11 14:37:29 +00:00
Patrick Robertson
29901da601 Merge branch 'load_modules' into docs_update 2025-02-11 14:10:56 +00:00
Patrick Robertson
895c843f04 Add a cheat sheet for configs and better folder structure for core modules 2025-02-11 14:06:53 +00:00
Patrick Robertson
2f51d3917a Further addition to docs: creating modules, configurations, installation 2025-02-11 13:49:30 +00:00
Erin Clark
aa5ac18d6a Merge pull request #189 from bellingcat/add_module_tests
- Add module tests
- Fix some storage related bugs 
- Separate the modules setup() method and base module config_setup()
2025-02-11 13:11:41 +00:00
Patrick Robertson
7d87b858d6 Merge branch 'load_modules' into docs_update 2025-02-11 13:09:38 +00:00
erinhmclark
c8cd7ea63c Merge branch 'load_modules' into add_module_tests
# Conflicts:
#	src/auto_archiver/modules/telethon_extractor/telethon_extractor.py
2025-02-11 13:08:08 +00:00
msramalho
977618b4ce doc: adds note about telethon vs telegram extractors 2025-02-11 13:04:59 +00:00
msramalho
d90d3cec28 fix telethon_extractor setup 2025-02-11 13:03:18 +00:00
msramalho
977f06c37a renames api_db property for clarity 2025-02-11 12:56:33 +00:00
msramalho
5c59029221 updates api_db for new API endpoint 2025-02-11 12:53:58 +00:00
msramalho
4eeb39477c improves gsheetdb feedback on retrieve sheet failure 2025-02-11 12:53:46 +00:00
msramalho
6fdd5f0e66 fix cases of single : vs :: in entrypoint 2025-02-11 12:53:12 +00:00
msramalho
e6594ad3dc merge result into cached results for context preservation 2025-02-11 12:52:42 +00:00
msramalho
7309cd32e7 fix: context to be updated on Metadata.merge 2025-02-11 12:51:17 +00:00
erinhmclark
d1d6cde008 Set mock timestamp without z format 2025-02-11 12:27:48 +00:00
erinhmclark
5e2e93382f Test fixes for 3.10 compliance. 2025-02-11 12:17:42 +00:00
erinhmclark
f97ec6a9e0 Fixed S3 module import 2025-02-11 11:58:28 +00:00
erinhmclark
89d9140d15 Fixed setup/ config_setup reference 2025-02-11 11:47:11 +00:00
erinhmclark
1792e02d1d skip authenticated tests in test_gdrive_storage.py 2025-02-11 11:34:36 +00:00
erinhmclark
18666ff027 skip authenticated tests in test_gsheet_feeder.py 2025-02-11 11:28:24 +00:00
erinhmclark
a69ac3e509 Fix file hash reference in S3 tests 2025-02-11 09:46:22 +00:00
Patrick Robertson
ed81dcdaf0 Remove dangling 'b = ' from config.py 2025-02-10 23:07:03 +00:00
Patrick Robertson
e7273bc741 Fix link 2025-02-10 23:04:41 +00:00
Patrick Robertson
dbc564e18b Add sphinx_book_theme theme to poetry 2025-02-10 22:58:52 +00:00
Patrick Robertson
2650cd8fb2 Use a script to auto-generate documentation for the core modules from the manifest file 2025-02-10 22:51:04 +00:00
erinhmclark
8d894066f2 Merge branch 'load_modules' into add_module_tests
# Conflicts:
#	src/auto_archiver/modules/gsheet_feeder/gsheet_feeder.py
#	src/auto_archiver/utils/misc.py
2025-02-10 19:00:05 +00:00
erinhmclark
3dae2337a1 remove cdn_url check before storage. 2025-02-10 18:56:46 +00:00
erinhmclark
e97ccf8a73 Separate setup() and module_setup(). 2025-02-10 18:07:47 +00:00
erinhmclark
2c3d1f591f Separate setup() and module_setup(). 2025-02-10 17:25:15 +00:00
msramalho
12f14cccc9 fixes gsheet feeder<->db connection via context. 2025-02-10 16:58:35 +00:00
msramalho
ab6cf52533 fixes bad hash initialization 2025-02-10 16:45:28 +00:00
Patrick Robertson
824728739a Start fleshing out the docs more - rearrange, separate out modules section, move files over to md (from rst) 2025-02-10 16:24:16 +00:00
erinhmclark
c4bb667cec Merge branch 'load_modules' into add_module_tests
# Conflicts:
#	src/auto_archiver/modules/s3_storage/s3_storage.py
#	src/auto_archiver/utils/gsheet.py
#	src/auto_archiver/utils/misc.py
2025-02-10 16:17:08 +00:00
erinhmclark
f311621e58 Small fixes.
Add timestamp helper method.
2025-02-10 15:57:42 +00:00
msramalho
15abf686b1 decouples s3_storage from hash_enricher 2025-02-10 15:48:54 +00:00
msramalho
8fb3dc754b fixing telethon extractor to use default entrypoint 2025-02-10 14:59:51 +00:00
msramalho
7c848046e8 adds better info about wrong/missing modules 2025-02-10 14:59:32 +00:00
Patrick Robertson
f3f6b92817 Implementation test cleanup 2025-02-10 12:43:21 +00:00
Patrick Robertson
74207d7821 Implementation tests for auto-archiver 2025-02-10 13:27:11 +01:00
Patrick Robertson
e9dd321dcd Fix setting cli_feeder as default feeder on clean install 2025-02-10 13:06:24 +01:00
Patrick Robertson
1fad37fd93 Remove blank file 2025-02-07 23:08:30 +01:00
Patrick Robertson
63aba6ad39 Fix sphinx-autoapi imports 2025-02-07 21:54:49 +01:00
erinhmclark
950624dd4b Fix S3 storage to media in whisper_enricher.py. 2025-02-07 20:26:00 +00:00
erinhmclark
2920cf685f Small fixes to whisper_enricher.py. 2025-02-07 12:35:40 +00:00
erinhmclark
e9ad1e1b85 Pass media to storage cdn_call 2025-02-06 22:01:55 +00:00
erinhmclark
266c7a14e6 Context related fixes, some more tests. 2025-02-06 16:53:00 +00:00
erinhmclark
67504a683e Merge branch 'load_modules' into add_module_tests 2025-02-06 10:13:37 +00:00
erinhmclark
5b0bad832f Updated test, test metadata 2025-02-06 10:11:56 +00:00
Patrick Robertson
a506f2a88f Clarify that an extractor's method can also return False if no valid data was found 2025-02-06 10:20:05 +01:00
Patrick Robertson
6ab8fd2ee4 Tidy up setting modules as Orchestrator attributes on startup.
Don't override the values in config['steps'] – the config should be left as is
2025-02-06 10:20:05 +01:00
erinhmclark
52542812dc Merge tests from version with context. 2025-02-05 16:42:58 +00:00
Patrick Robertson
48abb5e66b Remove dangling screenshot_enricher file. Moved to modules/screenshot_enricher 2025-02-04 18:16:03 +01:00
Patrick Robertson
91ca325fd5 Update yt-dlp to latest version + remove code no longer needed from bluesky dropin 2025-02-04 17:46:46 +01:00
Patrick Robertson
0633e17998 Close the facebook 'login' window if it's there - to allow for proper screenshots 2025-02-04 14:18:46 +01:00
Patrick Robertson
034197a81f Fix typos in csv feeder docs (in manifest) 2025-02-04 13:40:07 +01:00
Patrick Robertson
78e6418249 Unit tests for csv feeder + fix some bugs 2025-02-04 13:37:26 +01:00
Patrick Robertson
b301f60ea3 Fix using validators set in __manifest__.py
E.g. you can use the validator 'is_file' to check if a config is a valid file
2025-02-04 13:37:26 +01:00
Patrick Robertson
a873e56b87 Remove old csv_feeder file - now inside a module 2025-02-04 12:57:35 +01:00
Patrick Robertson
72b5ea9ab6 Restore headless arg 2025-02-03 17:40:40 +01:00
Patrick Robertson
c574b694ed Set up screenshot enricher to use authentication/cookies 2025-02-03 17:25:59 +01:00
Patrick Robertson
7ec328ab40 Remove cookie options from generic_extractor - it now uses 'authentication' global settings :D 2025-02-03 16:04:36 +01:00
Patrick Robertson
7a2be5a0da Add cookie extraction to 'authentication' options, get generic_extractor working using this info 2025-02-03 16:03:07 +01:00
Patrick Robertson
9c9e9b370e Remove lingering reference to ArchivingContext 2025-02-03 16:02:38 +01:00
Patrick Robertson
9a8c94b641 Fix getting/setting folder context for metadata 2025-02-03 16:02:17 +01:00
Patrick Robertson
c25d5cae84 Remove ArchivingContext completely
Context for a specific url/item is now passed around via the metadata (metadata.set_context('key', 'val') and metadata.get_context('key', default='something')
The only other thing that was passed around in ArchivingContext was the storage info, which is already accessible now via self.config
2025-01-30 17:50:54 +01:00
Patrick Robertson
d76063c3f3 Fix unit tests 2025-01-30 16:46:53 +01:00
Patrick Robertson
d6b4b7a932 Further cleanup
* Removes (partly) the ArchivingOrchestrator
* Removes the cli_feeder module, and makes it the 'default', allowing you to pass URLs directly on the command line, without having to use the cumbersome --cli_feeder.urls. Just do auto-archiver https://my.url.com
* More unit tests
* Improved error handling
2025-01-30 16:44:40 +01:00
Patrick Robertson
953011f368 Don't make modules 'dataclasses' 2025-01-30 16:44:40 +01:00
erinhmclark
527438826c Fix manifests for required configs. 2025-01-30 13:04:51 +00:00
Patrick Robertson
fade68c6f4 Fix up unit tests - dataclass + subclasses not having @dataclass was breaking it 2025-01-30 13:45:24 +01:00
Patrick Robertson
b7d9145f6c Further tidyups + refactoring for new structure
* Add implementation tests for orchestrator + logging tests
* Standardise method/class vars for extractors to see if they are suitable
* Fix bugs with removing default loguru logger (allows further customisation)
* Fix bug loading required fields from file
*
2025-01-30 13:21:10 +01:00
erinhmclark
cddae65a90 Update modules for new core structure. 2025-01-30 08:42:23 +00:00
Patrick Robertson
18ff36ce15 Add ruamel to dependencies (replaces pyyaml) 2025-01-29 19:37:41 +01:00
Patrick Robertson
00a7018f36 Fix up dependency checking (use 'dependencies' instead of 'external_dependencies' -> simpler/easier to remember 2025-01-29 19:25:22 +01:00
Patrick Robertson
3d37c494aa Tidy ups + unit tests:
1. Allow loading modules from --module_paths=/extra/path/here
2. Improved unit tests for module loading
3. Further small tidy ups/clean ups
2025-01-29 18:42:49 +01:00
Patrick Robertson
4c1c8953ca Add unit tests for timestamping_enricher 2025-01-29 12:20:52 +01:00
Patrick Robertson
dcd5576f29 set metadata enricher to requires_setup=True (requires exiftool which isn't installed by default on most machines) 2025-01-29 00:10:40 +01:00
Patrick Robertson
7a4871db6b Fix up unit tests for new structure 2025-01-28 14:40:12 +01:00
Patrick Robertson
9635449ac0 more user friendly error logging when config issues are found 2025-01-28 11:44:52 +01:00
Patrick Robertson
27b25c5bd4 Validate orchestration.yaml file inputs - so if a user enters invalid values, it also validates them 2025-01-28 11:37:23 +01:00
Patrick Robertson
1d2a1d4db7 Allow framework for config settings that should not be stored in config (e.g. cli_feeder.urls
Use 'do_not_store': True in the config settings to apply this. Also: fix up generic archiver dropins loading + local_storage defaults (same as what's in example orchestration)
2025-01-28 11:14:12 +01:00
erinhmclark
57b3bec935 Google sheets feeder and database implemented. 2025-01-27 20:13:12 +00:00
erinhmclark
6c67effd8c remove name reference in local_storage.py 2025-01-27 19:17:18 +00:00
erinhmclark
e1a9373336 Refactoring for new config setup 2025-01-27 19:03:02 +00:00
Patrick Robertson
e3074013d0 Fix loading/saving to orchestration file with comments 2025-01-27 14:28:04 +01:00
Patrick Robertson
f68e2726f2 Refactor loader + step into module, use LazyBaseModule and BaseModule 2025-01-27 14:01:36 +01:00
Patrick Robertson
7fd95866a1 Further fixes/changes to loading 'types' for config + manifest edits 2025-01-27 11:48:04 +01:00
Patrick Robertson
14e2479599 Merge branch 'more_mainifests' into load_modules 2025-01-27 11:05:56 +01:00
Patrick Robertson
0b03f54f4e Fix up config validation, and allow for custom 'validators' 2025-01-27 11:00:52 +01:00
erinhmclark
ebebd27897 Fix archiver to extractor naming 2025-01-27 09:11:45 +00:00
erinhmclark
21a7ff0520 Fix types in manifests 2025-01-27 08:43:18 +00:00
erinhmclark
96b35a272c Rm gsheet references in utils 2025-01-24 18:51:15 +00:00
erinhmclark
dd402b456f Fix and add types to manifest 2025-01-24 18:50:11 +00:00
Patrick Robertson
3fc6ddfe85 Tweaks to logging strings 2025-01-24 15:30:00 +01:00
Patrick Robertson
f1e9ab6751 Merge branch 'main' into load_modules 2025-01-24 15:23:15 +01:00
Patrick Robertson
e8138eac1c Add ubuntu-latest to the matrix of test runners (#181)
* Don't clutter logs with info about generic dropin

* Add ubuntu-latest to unit tests

This is currently failing due to an issue with oscrypto and newer openssl https://github.com/wbond/oscrypto/issues/78#issuecomment-1756317472

* fix oscrypto version for ubuntu 24 compatibility (boto3 too see #180)

---------

Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2025-01-24 14:03:55 +00:00
Miguel Sozinho Ramalho
a6fc4e1bb1 modifies base docker image to use browsertrix 1.4.2 (#182)
* modifies base image to newest browsertrix version

* modify browsertrix cmd args based on recent experience
2025-01-24 13:59:29 +00:00
erinhmclark
1942e8b819 Gsheets utility revert 2025-01-24 13:34:30 +00:00
erinhmclark
024fe58377 fix config parsing in manifests, remove module level configs 2025-01-24 13:33:12 +00:00
erinhmclark
0453d95f56 fix config parsing in manifests 2025-01-24 13:24:54 +00:00
erinhmclark
aa7ca93a43 Update manifests and modules 2025-01-24 12:58:16 +00:00
erinhmclark
ba4b330881 Merge remote-tracking branch 'origin/more_mainifests' into more_mainifests 2025-01-24 08:04:27 +00:00
erinhmclark
cbafbfab3f Revert Dockerfile changes 2025-01-24 08:04:09 +00:00
Patrick Robertson
9befb9776c Fix loading modules when entry_point isn't set 2025-01-23 21:08:54 +01:00
Patrick Robertson
06f6e34d9d Revert changes to orchestrator to avoid merge conflicts 2025-01-23 20:38:36 +01:00
Patrick Robertson
b27bf8ffeb Fix up loading/storing configs + unit tests 2025-01-23 20:32:19 +01:00
erinhmclark
50f4ebcdc3 Move storage configs into individual manifests, assert format on useage. 2025-01-23 17:01:30 +00:00
erinhmclark
c3403ced26 Rename storages for clarity 2025-01-23 16:51:17 +00:00
erinhmclark
1274a1b231 More manifests, base modules and rename from archiver to extractor. 2025-01-23 16:40:48 +00:00
erinhmclark
9db26cdfc2 Merge branch 'load_modules' into more_mainifests
# Conflicts:
#	src/auto_archiver/core/orchestrator.py
2025-01-23 09:19:54 +00:00
erinhmclark
79684f8348 Set up feeder manifests (not merged by source yet) 2025-01-23 09:16:42 +00:00
Patrick Robertson
65ef46d01e Fix loading already loaded modules - don't load them twice 2025-01-23 00:09:39 +01:00
Patrick Robertson
550097ab7b Get module loading working properly 2025-01-22 23:54:21 +01:00
erinhmclark
c517d35bdf Merge branch 'load_modules' into more_mainifests
# Conflicts:
#	src/auto_archiver/databases/__init__.py
2025-01-22 18:19:43 +00:00
erinhmclark
99c8c69085 Manifests for databases 2025-01-22 18:18:13 +00:00
Patrick Robertson
ade5ea0f6f Tidy up imports + start on loading modules - program now starts much faster 2025-01-22 18:45:58 +01:00
Patrick Robertson
b6b085854c Switch back to using yaml with dot notation
(two simple helper functions to convert between dot and dict notation)
2025-01-22 17:40:51 +01:00
Patrick Robertson
54995ad6ab Further tweaks based on __manifest__.py files
Loading configs now works
2025-01-22 13:11:43 +01:00
erinhmclark
7b3a1468cd Create manifest files for archiver modules. 2025-01-22 10:21:27 +01:00
Patrick Robertson
4830f99300 Get parsing of manifest and combining with config file working 2025-01-21 20:03:10 +01:00
Patrick Robertson
241b35002c Initial changes to move to '__manifest__' format 2025-01-21 19:02:38 +01:00
Patrick Robertson
03f3770223 Add __manifest__.py for generic_extractor 2025-01-21 18:00:45 +01:00
Patrick Robertson
bdfc855297 Ignore pylint statements for manifest files 2025-01-21 17:59:52 +01:00
Patrick Robertson
c41d93a634 Use already implemented helper to get version 2025-01-21 17:53:37 +01:00
Patrick Robertson
d4fff0b6eb Merge pull request #175 from bellingcat/youtubedlp-rewrite
Create generic archiver for all valid youtube-dl URLs, add truthsocial extractor, unit tests for twitter_api extractor, utility methods for cleaning HTML and traversing objects
2025-01-21 17:33:39 +01:00
Patrick Robertson
cd2ae3763f Minor adjustments
Co-authored-by: Miguel Sozinho Ramalho <19508417+msramalho@users.noreply.github.com>
2025-01-21 16:24:37 +00:00
Patrick Robertson
d3e3eb7639 unit tests for loading dropins 2025-01-21 16:59:45 +01:00
Patrick Robertson
9dde9b26d0 Patch in upstream changes to ytdlp for now
Seems like ytdlp may not merge https://github.com/yt-dlp/yt-dlp/pull/12098 anytime soon
2025-01-21 16:49:49 +01:00
Patrick Robertson
7c0dcbfd81 Re-add doc string to generic_archiver
(renamed from youtube_archiver)
2025-01-21 16:49:30 +01:00
Patrick Robertson
6388983815 Merge branch 'main' into youtubedlp-rewrite 2025-01-21 16:43:14 +01:00
Patrick Robertson
4bb4ebdf82 Further cleanup, abstracts 'dropins' out into generic files 2025-01-21 16:36:45 +01:00
Erin Clark
113a4db251 Merge pull request #177 from bellingcat/feat/documentation
Add Sphinx documentation and publish to RTD.
2025-01-21 09:54:41 +00:00
erinhmclark
e83ccc0d7f Cleaning up configs reference and module level. 2025-01-21 09:48:46 +00:00
Patrick Robertson
dff0105659 Small fixups + implement Truth code for posts with multiple media 2025-01-20 18:40:46 +01:00
Patrick Robertson
fd2e7f973b Further tidy-ups, also adds some ytdlp utils to 'utils' 2025-01-20 16:31:28 +01:00
Patrick Robertson
befc92deb4 Further unit test tidy ups 2025-01-17 17:29:13 +01:00
Patrick Robertson
d4893ee05e Fix unit tests for base_archiver->generic_archiver rename 2025-01-17 17:08:00 +01:00
Patrick Robertson
9c5a9e1bcd Rename BaseArchiver to GenericArchiver + some other tidyups 2025-01-17 17:06:04 +01:00
Patrick Robertson
5aa717452e Quick test that the app actually runs in core tests 2025-01-17 17:02:54 +01:00
Patrick Robertson
5b20288d06 Add a 'version' arg to get the current running version 2025-01-17 16:59:57 +01:00
Patrick Robertson
59eb8f7520 Add TWITTER_BEARER_TOKEN to env for running download tests 2025-01-17 12:04:40 +01:00
Patrick Robertson
17c1c9c360 Fix up core unit tests when a twitter api key isn't provided 2025-01-17 12:02:38 +01:00
Patrick Robertson
394bcd8d47 Further refactoring of youtubedl_archiver->base_archiver
* Keep twitter_api_archiver
* Remove unit tests for obsolete archivers
* Guess filename of media using the 'Content-Type' header
* Add mechanism to run 'expensive' tests last (see conftest.py) and also flag expensive tests to fail straight off (pytest.mark.incremental)
2025-01-17 11:56:08 +01:00
erinhmclark
170f8d18a6 Add instructions to README.md, include build directories in .gitignore and do a bit more tidying, 2025-01-16 20:46:10 +00:00
Erin Clark
f03ec42026 Merge pull request #174 from bellingcat/version_updates
Update versions for GH Actions and Geckodriver.
2025-01-16 14:31:26 +00:00
erinhmclark
6fabe2a189 Fixed twitter_archiver.py changes. 2025-01-16 09:56:54 +00:00
erinhmclark
a6aacfa3fb Add example pre-generated configs.rst 2025-01-16 09:31:50 +00:00
erinhmclark
bbb3269c2b Changes from main. 2025-01-16 09:30:32 +00:00
erinhmclark
235da33a1a Update .readthedocs.yaml path 2025-01-16 09:24:46 +00:00
erinhmclark
d3eec5d90f Basic docs structure for RTD 2025-01-15 21:45:29 +00:00
Patrick Robertson
3168bed0d9 Add (skipped) test for twitter extraction with youtubedlp 2025-01-15 19:00:57 +01:00
erinhmclark
33686ea851 Update versions for GH Actions and Geckodriver. 2025-01-15 17:35:42 +00:00
Patrick Robertson
5626bba815 Add test on bluesky and note on why it doesn't work 2025-01-15 18:31:20 +01:00
Patrick Robertson
3ff7a9444d Update yt-dlp to latest version (2025.1.12) to add bsky support 2025-01-15 17:58:07 +01:00
Patrick Robertson
74cf1f5f23 Merge branch 'main' into youtubedlp-rewrite 2025-01-15 17:47:23 +01:00
Patrick Robertson
4f2b9baa73 refactor youtubedlp archiver to work for all valid websites
1. Extract more metadata
2. Better extract thumbnail
3. Setup framework for specific sites to provide more granular metadata processing
2025-01-15 17:46:47 +01:00
Patrick Robertson
c3dd19f309 Sniff filetype of downloaded media and add extension
Also download in chunks - fixes 2 x TODOs
2025-01-15 17:46:47 +01:00
Patrick Robertson
05e0c9de93 Merge pull request #169 from bellingcat/remove_dependencies
Tidy up and remove dependencies
2025-01-15 17:16:30 +01:00
Patrick Robertson
73b1a3902c Merge pull request #172 from bellingcat/docker_compose
Add docker-compose for easy building and running of docker image in dev
2025-01-15 17:16:22 +01:00
Patrick Robertson
100996f1e5 Add docker-compose for easy building and running of docker image in dev
Just use docker compose up
2025-01-15 14:36:02 +01:00
Patrick Robertson
74a4a24a23 Remove toml - unused
(pytest etc. use tomli, which is instlled)
2025-01-14 18:13:27 +01:00
Patrick Robertson
306df62a98 Fix all instances of utcnow() 2025-01-14 17:51:41 +01:00
Patrick Robertson
20726c1116 Remove tiktok-downloader - getting info is broken
TODO: switch to using youtube-dlp
2025-01-14 17:40:45 +01:00
Patrick Robertson
2eb2ab9ac9 Merge branch 'main' into remove_dependencies 2025-01-14 17:39:20 +01:00
Patrick Robertson
eebd040e13 Merge pull request #163 from bellingcat/feat/unittest
CI Unit tests
2025-01-14 17:26:34 +01:00
Patrick Robertson
6f10270baf Remove unittest and switch to pytest fully 2025-01-14 16:28:39 +01:00
Patrick Robertson
080f474d49 Remove minify_html package - HTML file is no longer minified
Savings were 5K (~15KB vs ~20KB) for the generated .html file, but minify_html is currently not compatible with python3.13+
2025-01-14 11:36:10 +01:00
Patrick Robertson
cef4037ad5 Add documentation on running tests to the readme 2025-01-14 11:30:06 +01:00
Patrick Robertson
4e13a09a87 Fix deprecation warning about utcnow 2025-01-14 11:01:40 +01:00
Patrick Robertson
6329b72ee8 Remove argparse from dependency list
Argparse is installed by default on python>=3.2, we only support python3.10+
Ref: https://pypi.org/project/argparse/#description
2025-01-14 10:50:47 +01:00
Patrick Robertson
1b1af2f0b1 Revert change to twitter_archiver
As per discussion at: https://github.com/bellingcat/auto-archiver/pull/165#discussion_r1905930837
2025-01-14 10:30:41 +01:00
Patrick Robertson
8f17a235f3 Switch to ubuntu-22.04 for CI tests
An issue with oscrypto means it currently does not work on 24.04. Ref: https://github.com/wbond/oscrypto/issues/78#issuecomment-2565688091
2025-01-14 10:24:14 +01:00
Patrick Robertson
ab2eb3c7f5 Add dev dependencies to poetry 2025-01-13 20:42:08 +01:00
Patrick Robertson
bdfedfcf61 Merge branch 'main' into feat/unittest 2025-01-13 19:50:47 +01:00
Erin Clark
9cdaea873b Merge pull request #164 from bellingcat/ec_add_poetry
Migrate to Poetry
2025-01-13 18:49:15 +00:00
erinhmclark
84ee1b422f Update and restrict versions of Poetry and Python. 2025-01-13 17:42:51 +00:00
Patrick Robertson
b9aea99de8 Prettify pytest output 2025-01-13 18:41:24 +01:00
Patrick Robertson
52f064908e Add unit test badges to readme 2025-01-13 18:33:22 +01:00
Patrick Robertson
9b596e59d6 Run expensive download tests once per week, on a month at 2:35pm
(time is offset from the hour to alleviate high load on Github
2025-01-13 18:33:02 +01:00
Patrick Robertson
528b78db85 Flag tombstone tweets for twitter_syndication method 2025-01-13 18:17:24 +01:00
Patrick Robertson
57eacdc24a Merge branch 'main' into feat/unittest 2025-01-13 18:06:55 +01:00
Patrick Robertson
bbef80de4c Add unit tests for html_formatter, csv_db 2025-01-13 17:58:10 +01:00
Patrick Robertson
930d78096a Merge pull request #162 from bellingcat/small_issues
Fix two small issues
2025-01-13 16:39:59 +01:00
Patrick Robertson
2353f9d6a5 Separate CI for download tests and core tests 2025-01-13 16:27:46 +01:00
Patrick Robertson
63973e2ce7 switch to pytest and pytest-recording 2025-01-13 16:23:20 +01:00
erinhmclark
e9a7f435a3 Add package dist directory to .gitignore 2025-01-13 13:33:23 +00:00
Patrick Robertson
e2bc84ccb9 Merge branch 'main' into feat/unittest 2025-01-13 13:15:13 +01:00
erinhmclark
72a8e76fbb Update README.md for usage with Poetry. 2025-01-12 20:21:23 +00:00
erinhmclark
c69a5fa1c9 Refactor Dockerfile for multi-stage builds.
Combining environment and runtime stages due to Poetry's dependency on source code.
2025-01-12 12:38:12 +00:00
erinhmclark
d80b4b7557 Remove snscrape and Python 3.12 restriction. 2025-01-12 12:15:56 +00:00
erinhmclark
cc490f9c10 Updated Dockerfile (not optimised yet) 2025-01-12 12:15:56 +00:00
erinhmclark
08e83eb94e Update pyproject.toml configuration for Poetry version 2.0.0. 2025-01-12 12:15:56 +00:00
erinhmclark
dd822b8b44 Update poetry.lock 2025-01-12 12:15:56 +00:00
erinhmclark
4a63ca7753 Update PyPi workflow to read python version from pyproject.toml. 2025-01-12 12:15:56 +00:00
erinhmclark
6d5b0090d9 Pull version from pyproject.toml file/ 2025-01-12 12:15:56 +00:00
erinhmclark
26abd6f7ae Added TODO comment for adding a version restriction. 2025-01-12 12:15:56 +00:00
erinhmclark
dba8f46016 Replaced comments for python-publish.yaml workflow. 2025-01-12 12:15:56 +00:00
erinhmclark
50e8c93477 Updated workflow for python-publish.yaml to use poetry (untested), and cleanup of pipenv files. 2025-01-12 12:15:56 +00:00
erinhmclark
6da837b374 Add note to update dynamic versioning and references to version. 2025-01-12 12:15:56 +00:00
erinhmclark
660ee82c67 Update Dockerfile for poetry.
Note: Review security with curl installation. Currently locked to known version, but additional checks could be added.
2025-01-12 12:15:56 +00:00
erinhmclark
5490947657 Add packaging to Poetry. 2025-01-12 12:15:56 +00:00
erinhmclark
fd9a6c26ed Create Poetry environment.
Required addition of transitive package (pyOpenSSL) and version restrictions on cryptography, boto3.
2025-01-12 12:15:56 +00:00
Patrick Robertson
3546d4ad79 Fix 'download_syndication' method for tweet archiving (now requires a token)
Plus add in unit tests for token generation + download syndication
2025-01-12 12:55:00 +01:00
Patrick Robertson
c932fb7416 Improved logging when an invalid/deleted tweet is attempted to be downloaded
Plus: unit tests for non-existent tweet + invalid tweet ID
2025-01-12 12:00:45 +01:00
Patrick Robertson
f29950905c Merge branch 'main' into small_issues 2025-01-12 11:47:55 +01:00
Patrick Robertson
8e99d62c97 Merge pull request #165 from bellingcat/fix/snscrape
Remove snscrape from the twitter_archiver
2025-01-09 11:06:14 +01:00
Patrick Robertson
9dc4eb35de Switch to pytest and use vcr for request storing 2025-01-08 11:25:13 +01:00
Patrick Robertson
8c044c15f0 Add base test class for archivers with boilerplate code
Plus: create test class for twitter archiver. Currently WIP
2025-01-08 10:38:56 +01:00
Patrick Robertson
ab9335bb7a Merge branch 'main' into feat/unittest 2025-01-08 10:35:45 +01:00
Patrick Robertson
add83c9650 Remove snscrape from twitter_archiver
1. snscrape twitter downloader no longer works (ref: https://github.com/JustAnotherArchivist/snscrape/issues/1045)
2. snscrape limits python to versions <3.12
2025-01-07 19:40:19 +01:00
Miguel Sozinho Ramalho
a697f0a212 adds an unauthenticated Bluesky archiver (#160)
* adds a TODO for next code iterations

* implements bsky archiver

* adds new archiver to example orchestration file

* Fix downloading media for posts with multiple images

(Images are stored in media/images)

* Setup a basic framework for unit tests

Use 'python -m unittest' from the project root to run

---------

Co-authored-by: Patrick Robertson <robertson.patrick@gmail.com>
2025-01-07 10:28:07 +00:00
Patrick Robertson
bffa3a6254 Merge pull request #159 from bellingcat/print_pdf
Add 'print_pdf' option to the screenshot enricher. Fixes #132
2025-01-06 18:13:38 +01:00
Miguel Sozinho Ramalho
ef471f41e1 adds better debug for wayback failures (#161) 2025-01-06 16:49:11 +00:00
Patrick Robertson
928518cda7 Allow setting cookies for yt-dl (#158) 2025-01-06 16:19:53 +00:00
Patrick Robertson
1bd017000e Add Github CI test workflow 2024-12-31 15:20:33 +01:00
Patrick Robertson
33e967ce4b Update pipfile for:
- pyopenssl==24.2.1
- youtube-dlp==2024.09.27
- numpy==2.1.3

Fixes building/local installs. Also fixes #155
2024-12-31 15:20:11 +01:00
Patrick Robertson
30d423c8e6 Setup a basic framework for unit tests
Use 'python -m unittest' from the project root to run
2024-12-31 14:29:52 +01:00
Patrick Robertson
0c803f15a5 Fix showing preview images in the .html file when using local storage
Local storage media urls are prefixed with '/', previously only http(s) media preview src were displayed
2024-12-31 09:29:31 +01:00
Patrick Robertson
a46f9997ea Better logging when there's a timestamp parse error 2024-12-31 09:28:08 +01:00
msramalho
83da9ae089 adds pdf preview support for html formatter 2024-12-23 18:19:26 +00:00
Patrick Robertson
663c8ad93a Add 'print_pdf' option to the screenshot enricher. Fixes #132 2024-12-20 07:14:03 +01:00
msramalho
e49550163f adds proxy_server option to wacz 2024-10-06 10:45:34 +06:00
msramalho
e6f5981afc numpy version downgrade 2024-10-06 10:10:04 +06:00
msramalho
c62bf1a34d yt-dlp version bump 2024-10-05 17:43:07 +06:00
msramalho
b166d57e61 v0.12.0 bump 2024-08-21 13:34:34 +01:00
msramalho
11c3288267 closes #146 2024-08-21 13:33:58 +01:00
msramalho
004143a58a version bump v0.11.6 2024-07-18 11:27:39 +01:00
msramalho
686f0027c4 adds new entries to example orchestration file 2024-07-18 11:27:15 +01:00
dependabot[bot]
b03cf32c73 Bump authlib from 1.3.0 to 1.3.1 (#144)
Bumps [authlib](https://github.com/lepture/authlib) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/lepture/authlib/releases)
- [Changelog](https://github.com/lepture/authlib/blob/master/docs/changelog.rst)
- [Commits](https://github.com/lepture/authlib/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: authlib
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-18 11:26:22 +01:00
msramalho
dc9e64397e bumping yt-dlp 2024-07-18 11:23:09 +01:00
msramalho
c7bc5e2988 cleanup 2024-05-15 11:04:29 +01:00
msramalho
1e375bd740 version bump 2024-05-14 16:42:15 +01:00
Miguel Sozinho Ramalho
f8824691dd refactors free twitter archiver strategies (#142) 2024-05-14 16:23:33 +01:00
msramalho
012cc36609 removes deprecated datetime method 2024-05-14 15:54:50 +01:00
Miguel Sozinho Ramalho
7cfe1e39cc #135 fix cleanup of telethon session files (#139)
* closes #135

* version bump
2024-04-16 12:45:45 +01:00
Jett Chen
cf8691bad7 Add yt-dlp based archiving for TwitterArchiver (#138)
* Add ytdlp archiving capability

* Add type annotation

* version bump

---------

Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2024-04-15 19:54:55 +01:00
R. Miles McCain
f603400d0d Add direct Atlos integration (#137)
* Add Atlos feeder

* Add Atlos db

* Add Atlos storage

* Fix Atlos storages

* Fix Atlos feeder

* Only include URLs in Atlos feeder once they're processed

* Remove print

* Add Atlos documentation to README

* Formatting fixes

* Don't archive existing material

* avoid KeyError in atlos_db

* version bump

---------

Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2024-04-15 19:25:17 +01:00
msramalho
eb37f0b45b version bump 2024-04-15 19:02:54 +01:00
msramalho
75497f5773 minor bug fix when using an archiver_enricher in enrichers only 2024-04-15 19:02:40 +01:00
msramalho
623e555713 dependencies updates 2024-04-15 19:02:20 +01:00
msramalho
9c7824de57 browsertrix docker updates 2024-04-15 19:01:55 +01:00
msramalho
f4827770e6 adds instagram no stories as success, and fix for telethon-based archivers. 2024-03-05 14:49:10 +00:00
msramalho
601572d76e strip url 2024-02-29 11:54:01 +00:00
msramalho
d21e79a272 general security updates 2024-02-29 11:40:30 +00:00
msramalho
ccf5f857ef adds configurable limits to instagram/youtube 2024-02-25 15:14:17 +00:00
msramalho
7de317d1b5 avoiding exception 2024-02-23 15:54:33 +00:00
msramalho
70075a1e5e improving insta archiver 2024-02-23 15:37:28 +00:00
msramalho
5b9bc4919a version bump 2024-02-23 14:08:23 +00:00
msramalho
f0158ffd9c adds tagged posts and better parsing 2024-02-23 14:08:17 +00:00
msramalho
bfb35a43a9 adds more details from yt-dlp 2024-02-23 14:08:05 +00:00
msramalho
ef5b39c4f1 dind exception 2024-02-22 18:05:56 +00:00
msramalho
24ceafcb64 missing forward slash 2024-02-22 17:47:13 +00:00
msramalho
9fd4bb56a8 new attempt at dind wacz 2024-02-22 17:24:27 +00:00
msramalho
5324d562ba cleanup wacz patch 2024-02-21 18:14:30 +00:00
msramalho
5bf0a0206d version update 2024-02-21 17:26:07 +00:00
msramalho
4941823565 fix growing volume size in wacz_enricher 2024-02-21 17:25:55 +00:00
msramalho
27310c2911 fixes issue with api requests 2024-02-21 12:25:05 +00:00
msramalho
eb973ba42d v0.9.1 fixes to bad parsing in ssl certificates 2024-02-20 19:31:19 +00:00
Miguel Sozinho Ramalho
7a21ae96af V0.9.0 - closes several open issues: new enrichers and bug fixes (#133)
* clean orchestrator code, add archiver cleanup logic

* improves documentation for database.py

* telethon archivers isolate sessions into copied files

* closes #127

* closes #125

* closes #84

* meta enricher applies to all media

* closes #61 adds subtitles and comments

* minor update

* minor fixes to yt-dlp subtitles and comments

* closes #17 but logic is imperfect.

* closes #85 ssl enhancer

* minimifies html, JS refactor for preview of certificates

* closes #91 adds freetsa timestamp authority

* version bump

* simplify download_url method

* skip ssl if nothing archived

* html preview improvements

* adds retrying lib

* manual download archiver improvements

* meta only runs when relevant data available

* new metadata convenience method

* html template improvements

* removes debug message

* does not close #91 yet, will need a few more certificate chaing logging

* adds verbosity config

* new instagram api archiver

* adds proxy support we

* adds proxy/end support and bug fix for yt-dlp

* proxy support for webdriver

* adds socks proxy to wacz_enricher

* refactor recursivity in inner media and display

* infinite recursive display

* foolproofing timestamping authortities

* version to 0.9.0

* minor fixes from code-review
2024-02-20 18:05:29 +00:00
msramalho
5c49124ac6 Merge branch 'main' of https://github.com/bellingcat/auto-archiver 2024-02-13 15:44:53 +00:00
Kai
b9d71d0b3f Change submit-archive from basic to bearer auth (#128) 2024-02-06 15:24:15 +00:00
msramalho
b9b831ce03 v8.0.1 2024-02-01 15:08:55 +00:00
msramalho
2a773a25e8 better handling of telethon data display 2024-02-01 15:08:23 +00:00
msramalho
719645fc2d minor improvement to html_template 2024-02-01 15:03:00 +00:00
Chu-An, Huang
71fcf5a089 fix: Correct the path of service account in google drive settings (#123)
* fix: Correct the path of service account in yaml file

* fix: Remove redefined function

* Update src/auto_archiver/storages/gd.py

* fix: remove unwanted drafting code

---------

Co-authored-by: Miguel Sozinho Ramalho <19508417+msramalho@users.noreply.github.com>
2024-02-01 15:02:04 +00:00
Tomas Apodaca
590d3fe824 Fix typo in readme (#121) 2024-01-24 21:17:31 +00:00
Miguel Sozinho Ramalho
e6b6b83007 0.8.0 new features and dependency updates (#119)
* wacz can extract_screenshot only

* new meta enricher

* twitter api can use multiple authentication tokens in sequence

* cleanup non-dup logic

* meta info on archive duration

* minor html report update

* updated dependencies

* new version
2023-12-20 14:13:22 +00:00
msramalho
499832d146 fix datetime parsing 2023-12-13 18:41:48 +00:00
msramalho
fa1163532b patching now optional value 2023-12-13 13:55:31 +00:00
msramalho
96f6ea8f09 v0.7.8 2023-12-13 13:03:39 +00:00
Miguel Sozinho Ramalho
ff17dfd0aa enables option to toggle db api writes (#118) 2023-12-13 12:54:47 +00:00
msramalho
0a3053bbc7 version update 2023-12-13 11:29:13 +00:00
Miguel Sozinho Ramalho
e69660be82 chooses most complete result from api (#117) 2023-12-13 11:28:27 +00:00
Miguel Sozinho Ramalho
a786d4bb0e chooses most complete result from api (#116) 2023-12-13 11:26:46 +00:00
Miguel Sozinho Ramalho
128d4136e3 fixes empty api search results (#115) 2023-12-13 10:51:25 +00:00
Miguel Sozinho Ramalho
98fb574d89 fixing older db entries formats (#114) 2023-12-12 22:47:54 +00:00
Miguel Sozinho Ramalho
6f36e92e02 enables api_db cache queries if configured with new option (#113) 2023-12-12 19:20:26 +00:00
Miguel Sozinho Ramalho
3e56ef137d reduce s3 duplicating while keeping random urls via hash (#112) 2023-12-12 19:12:03 +00:00
Jett Chen
9ee323a654 Set _mimetype for final media of html formatter (#111) 2023-12-11 11:47:04 +00:00
Kai
9eb39943c7 Extract text in wacz_enricher (#110) 2023-12-05 22:24:12 +00:00
msramalho
8624e9f177 version update 0.7.1 2023-11-13 11:58:43 +01:00
Galen Reich
381940f5a8 Fix Selenium headless invokation (#106)
Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2023-11-13 11:56:35 +01:00
msramalho
1382f8b795 version bump and release without commit 2023-09-22 10:18:58 +01:00
Dave Mateer
fac8364762 Updated gd.py to work with shared folders (#102)
Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
2023-09-22 10:17:54 +01:00
msramalho
0feeb0bd24 Bump version to v0.6.12 for release 2023-09-20 10:18:44 +01:00
msramalho
ddb9dc87d7 unfortunately needed twitter->x 2023-09-20 10:17:31 +01:00
msramalho
e8935b9a80 Bump version to v0.6.11 for release 2023-09-15 19:53:07 +01:00
msramalho
b157f9a6b1 renaming variable 2023-09-15 19:52:47 +01:00
msramalho
ea38a604bb fixes #96 by not assigning to self.prop 2023-09-15 19:35:35 +01:00
360 changed files with 31109 additions and 5889 deletions

40
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
groups:
python:
patterns:
- "*"
schedule:
interval: "monthly"
- package-ecosystem: "github-actions"
directory: "/"
groups:
actions:
patterns:
- "*"
schedule:
interval: "monthly"
- package-ecosystem: "npm"
directory: "/scripts/settings/"
groups:
actions:
patterns:
- "*"
schedule:
interval: "monthly"
- package-ecosystem: "docker"
# Look for a `Dockerfile` in the `root` directory
directory: "/"
# Check for updates once a week
schedule:
interval: "monthly"

View File

@@ -8,13 +8,10 @@ name: Docker
on:
release:
types: [published]
push:
# branches: [ "main" ]
tags: [ "v*.*.*" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
REGISTRY: docker.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
@@ -25,33 +22,35 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v4
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v4
- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
with:
images: bellingcat/auto-archiver
- name: Build and push Docker image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache,mode=max

View File

@@ -1,4 +1,4 @@
# This workflow will upload a Python Package using Twine when a release is created
# This workflow uploads a Python Package to PyPI using Poetry when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
@@ -11,9 +11,6 @@ name: Pypi
on:
release:
types: [published]
push:
# branches: [ "main" ]
tags: [ "v*.*.*" ]
permissions:
contents: read
@@ -24,30 +21,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Checkout Repository
uses: actions/checkout@v6
- name: Set up Python 3.10
uses: actions/setup-python@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.10"
python-version-file: pyproject.toml
- name: Install Poetry
run: |
pipx install "poetry>=2.0.0,<3.0.0"
- name: Install dependencies
run: |
python -m pip install --upgrade --upgrade-strategy=eager pip setuptools wheel twine pipenv
python -m pip install -e . --upgrade
python -m pipenv install --dev --python 3.10
env:
PIPENV_DEFAULT_PYTHON_VERSION: "3.10"
poetry install --no-interaction --no-root
- name: Build wheels
- name: Build the package
run: |
python -m pipenv run python setup.py sdist bdist_wheel
poetry build
- name: Publish a Python distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
verbose: true
skip_existing: true
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: dist/
# Step 6: Publish to PyPI
- name: Publish to PyPI
run: |
poetry publish --username __token__ --password ${{ secrets.PYPI_API_TOKEN }}

34
.github/workflows/ruff.yaml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Ruff Formatting & Linting
on:
push:
branches: [ main ]
paths-ignore:
- "README.md"
- ".github"
- "poetry.lock"
- "scripts/settings"
pull_request:
branches: [ main ]
paths-ignore:
- "README.md"
- ".github"
- "poetry.lock"
- "scripts/settings"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
- name: Run Ruff
run: ruff check --output-format=github . && ruff format --check

56
.github/workflows/tests-core.yaml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Core Tests
on:
push:
branches: [ main ]
paths:
- src/**
- poetry.lock
- pyproject.toml
pull_request:
paths:
- src/**
- poetry.lock
- pyproject.toml
jobs:
tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
os: [ubuntu-22.04, ubuntu-latest]
defaults:
run:
working-directory: ./
steps:
- uses: actions/checkout@v6
- name: Install ffmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install latest Poetry
run: pipx install poetry
- name: Cache Poetry and pip artifacts
uses: actions/cache@v5
with:
path: |
~/.cache/pypoetry
~/.cache/pip
key: poetry-${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies from source only
run: poetry install --no-interaction --with dev
- name: Run Core Tests
run: |
poetry run auto-archiver --version || true
poetry run pytest -ra -v -m "not download"

50
.github/workflows/tests-download.yaml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Download Tests
on:
schedule:
- cron: '35 14 * * 1'
pull_request:
branches: [ main ]
paths:
- src/**
jobs:
tests:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
python-version: ["3.10"] # only run expensive downloads on one (lowest) python version
defaults:
run:
working-directory: ./
steps:
- uses: actions/checkout@v6
- name: Install ffmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install latest Poetry
run: pipx install poetry
- name: Cache Poetry and pip artifacts
uses: actions/cache@v5
with:
path: |
~/.cache/pypoetry
~/.cache/pip
key: poetry-${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies from source only
run: poetry install --no-interaction --with dev
- name: Run Download Tests
run: poetry run pytest -ra -v -x -m "download"
env:
TWITTER_BEARER_TOKEN: ${{ secrets.TWITTER_BEARER_TOKEN || '' }}

16
.gitignore vendored
View File

@@ -1,9 +1,11 @@
tmp*/
temp/
.env*
!.env*.example
.DS_Store
expmt/
service_account.json
service_account-*.json
__pycache__/
._*
anu.html
@@ -27,4 +29,16 @@ instaloader.session
orchestration.yaml
auto_archiver.egg-info*
logs*
*.csv
*.csv
archived/
dist*
docs/_build/
docs/source/autoapi/
docs/source/modules/autogen/
scripts/settings_page.html
scripts/settings/src/schema.json
.vite
downloaded_files
latest_logs
# for launch.json
.vscode

7
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,7 @@
# Run Ruff formatter on commits.
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.10
hooks:
- id: ruff
- id: ruff-format

3
.pylintrc Normal file
View File

@@ -0,0 +1,3 @@
[MAIN]
ignore-patterns=(.*tests.*.py, __manifest__.py)

30
.readthedocs.yaml Normal file
View File

@@ -0,0 +1,30 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
build:
os: ubuntu-22.04
apt_packages:
- ffmpeg
tools:
python: "3.10"
nodejs: "22"
jobs:
post_install:
- pip install poetry
# https://python-poetry.org/docs/managing-dependencies/#dependency-groups
# VIRTUAL_ENV needs to be set manually for now.
# See https://github.com/readthedocs/readthedocs.org/pull/11152/
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs
# generate the config editor page. Schema then HTML
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry run python scripts/generate_settings_schema.py
# install node dependencies and build the settings
- cd scripts/settings && npm install && npm run build && yes | cp -v dist/index.html ../../docs/source/installation/settings.html && cd ../..
sphinx:
configuration: docs/source/conf.py

49
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,49 @@
# Contributing to Auto Archiver
Thank you for your interest in contributing to Auto Archiver! Your contributions help improve the project and make it more useful for everyone. Please follow the guidelines below to ensure a smooth collaboration.
### 1. Reporting a Bug
If you encounter a bug, please create an issue on GitHub with the following details:
* Describe the bug: Provide a clear and concise description of the issue.
* Steps to reproduce: Include the steps needed to reproduce the bug.
* Expected behavior: Describe what you expected to happen.
* Actual behavior: Explain what actually happened.
* Screenshots/logs: If applicable, attach screenshots or logs to help diagnose the problem.
* Environment: Mention the OS, Ruby version, and any other relevant details.
### 2. Writing a Patch/Fix and Submitting Pull Requests
If youd like to fix a bug or improve existing code:
1. Open a pull request on GitHub and link it to the relevant issue.
2. Make sure to document your pull request with a clear description of what changes were made and why.
3. Wait for review and make any requested changes.
### 3. Creating New Modules
If you want to add a new module to Auto Archiver:
1. Ensure your module follows the existing [coding style and project structure](https://auto-archiver.readthedocs.io/en/latest/development/creating_modules.html).
2. Write clear documentation explaining what your module does and how to use it.
3. Ideally, include unit tests for your module!
4. Follow the steps in Section 2 to submit a pull request.
### 4. Do You Have Questions About the Source Code?
If you have any questions about how the source code works or need help using Auto Archiver
📝 Check the [Auto Archiver](https://auto-archiver.readthedocs.io/en/latest/) documentation.
👉 Ask your questions in the [Bellingcat Discord](https://www.bellingcat.com/follow-bellingcat-on-social-media/).
### 5. Do You Want to Contribute to the Documentation?
We welcome contributions to the documentation!
📖 Please read [Contributing to the Auto Archiver Documentation](https://auto-archiver.readthedocs.io/en/latest/development/docs.html) to learn how you can help improve the project's documentation.
------------------
Thank you for contributing to Auto Archiver! 🚀

View File

@@ -1,31 +1,62 @@
FROM webrecorder/browsertrix-crawler:latest
FROM webrecorder/browsertrix-crawler:1.12.4 AS base
ENV RUNNING_IN_DOCKER=1
ENV RUNNING_IN_DOCKER=1 \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONFAULTHANDLER=1
ARG TARGETARCH
# Installing system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc ffmpeg fonts-noto exiftool python3-tk
# Poetry and runtime
FROM base AS runtime
ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_VIRTUALENVS_CREATE=1
# Create a virtual environment for poetry and install it
RUN python3 -m venv /poetry-venv && \
/poetry-venv/bin/python -m pip install --upgrade pip && \
/poetry-venv/bin/python -m pip install "poetry>=2.0.0,<3.0.0"
WORKDIR /app
RUN pip install --upgrade pip && \
pip install pipenv && \
add-apt-repository ppa:mozillateam/ppa && \
apt-get update && \
apt-get install -y gcc ffmpeg fonts-noto exiftool && \
apt-get install -y --no-install-recommends firefox-esr && \
ln -s /usr/bin/firefox-esr /usr/bin/firefox && \
wget https://github.com/mozilla/geckodriver/releases/download/v0.33.0/geckodriver-v0.33.0-linux64.tar.gz && \
tar -xvzf geckodriver* -C /usr/local/bin && \
chmod +x /usr/local/bin/geckodriver && \
rm geckodriver-v*
COPY pyproject.toml poetry.lock README.md ./
# Copy dependency files and install dependencies (excluding the package itself)
RUN /poetry-venv/bin/poetry install --only main --no-root --no-cache
COPY Pipfile* ./
# install from pipenv, with browsertrix-only requirements
RUN pipenv install && \
pipenv install pywb uwsgi
# doing this at the end helps during development, builds are quick
COPY ./src/ .
# Copy code: This is needed for poetry to install the package itself,
# but the environment should be cached from the previous step if toml and lock files haven't changed
COPY ./src/ .
RUN /poetry-venv/bin/poetry install --only main --no-cache
ENTRYPOINT ["pipenv", "run", "python3", "-m", "auto_archiver"]
# Run as non-root user to avoid permission issues with mounted volumes (see #342)
# The base image already has an 'ubuntu' user at UID/GID 1000.
# Ensure directories that need write access at runtime are writable.
RUN chown 1000:1000 /app && \
chown -R 1000:1000 /app/.venv/lib/python3.12/site-packages/seleniumbase/drivers/ && \
mkdir -p /app/local_archive /app/secrets /tmp/archive && \
chown -R 1000:1000 /app/local_archive /app/secrets /tmp/archive
# Update PATH to include virtual environment binaries
# Allowing entry point to run the application directly with Python
ENV VIRTUAL_ENV=/app/.venv \
PATH="/app/.venv/bin:$PATH"
USER 1000
ENTRYPOINT ["python3", "-m", "auto_archiver"]
# should be executed with 2 volumes (3 if local_storage is used)
# docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive aa pipenv run python3 -m auto_archiver --config secrets/orchestration.yaml

79
Makefile Normal file
View File

@@ -0,0 +1,79 @@
# Variables
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = docs/source
BUILDDIR = docs/_build
.PHONY: help
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@echo "Additional Commands:"
@echo " make test - Run all tests in 'tests/' with pytest"
@echo " make ruff-check - Run Ruff linting and formatting checks (safe)"
@echo " make ruff-clean - Auto-fix Ruff linting and formatting issues"
@echo " make docs - Generate documentation (same as 'make html')"
@echo " make clean-docs - Remove generated docs"
@echo " make docker-build - Build the Auto Archiver Docker image"
@echo " make docker-compose - Run Auto Archiver with Docker Compose"
@echo " make docker-compose-rebuild - Rebuild and run Auto Archiver with Docker Compose"
@echo " make show-docs - Build and open the documentation in a browser"
.PHONY: test
test:
@echo "Running tests..."
@pytest tests --disable-warnings
.PHONY: ruff-check
ruff-check:
@echo "Checking code style with Ruff (safe)..."
@ruff check .
.PHONY: ruff-clean
ruff-clean:
@echo "Fixing lint and formatting issues with Ruff..."
@ruff check . --fix
@ruff format .
.PHONY: docs
docs:
@echo "Building documentation..."
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)"
.PHONY: clean-docs
clean-docs:
@echo "Cleaning up generated documentation files..."
@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@rm -rf "$(SOURCEDIR)/autoapi/" "$(SOURCEDIR)/modules/autogen/"
@echo "Cleanup complete."
.PHONY: show-docs
show-docs:
@echo "Opening documentation in browser..."
@open "$(BUILDDIR)/html/index.html"
.PHONY: docker-build
docker-build:
@echo "Building local Auto Archiver Docker image..."
@docker compose build # Uses the same build context as docker-compose.yml
.PHONY: docker-compose
docker-compose:
@echo "Running Auto Archiver with Docker Compose..."
@docker compose up
.PHONY: docker-compose-rebuild
docker-compose-rebuild:
@echo "Rebuilding and running Auto Archiver with Docker Compose..."
@docker compose up --build
# Catch-all for Sphinx commands
.PHONY: Makefile
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

44
Pipfile
View File

@@ -1,44 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
gspread = "*"
boto3 = "*"
argparse = "*"
beautifulsoup4 = "*"
tiktok-downloader = "*"
bs4 = "*"
loguru = "*"
ffmpeg-python = "*"
selenium = "*"
snscrape = "*"
telethon = "*"
google-api-python-client = "*"
google-auth-httplib2 = "*"
google-auth-oauthlib = "*"
oauth2client = "*"
pdqhash = "*"
pillow = "*"
python-slugify = "*"
pyyaml = "*"
dateparser = "*"
python-twitter-v2 = "*"
instaloader = "*"
tqdm = "*"
jinja2 = "*"
cryptography = "*"
dataclasses-json = "*"
yt-dlp = "*"
vk-url-scraper = "*"
requests = {extras = ["socks"], version = "*"}
numpy = "*"
warcio = "*"
[dev-packages]
autopep8 = "*"
setuptools-pipfile = "*"
[requires]
python_version = "3.10"

1899
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

248
README.md
View File

@@ -1,244 +1,42 @@
<h1 align="center">Auto Archiver</h1>
[![Documentation Status](https://readthedocs.org/projects/auto-archiver/badge/?version=latest)](https://auto-archiver.readthedocs.io/en/latest/?badge=latest)
[![PyPI version](https://badge.fury.io/py/auto-archiver.svg)](https://badge.fury.io/py/auto-archiver)
[![Docker Image Version (latest by date)](https://img.shields.io/docker/v/bellingcat/auto-archiver?label=version&logo=docker)](https://hub.docker.com/r/bellingcat/auto-archiver)
[![Docker Image Version (latest by date)](https://img.shields.io/docker/v/bellingcat/auto-archiver?sort=semver&logo=docker&color=#69F0AE)](https://hub.docker.com/r/bellingcat/auto-archiver)
[![Core Test Status](https://github.com/bellingcat/auto-archiver/workflows/Core%20Tests/badge.svg)](https://github.com/bellingcat/auto-archiver/actions/workflows/tests-core.yaml)
<!-- [![Download Test Status](https://github.com/bellingcat/auto-archiver/workflows/Download%20Tests/badge.svg)](https://github.com/bellingcat/auto-archiver/actions/workflows/tests-download.yaml) -->
<!-- ![Docker Pulls](https://img.shields.io/docker/pulls/bellingcat/auto-archiver) -->
<!-- [![PyPI download month](https://img.shields.io/pypi/dm/auto-archiver.svg)](https://pypi.python.org/pypi/auto-archiver/) -->
<!-- [![Documentation Status](https://readthedocs.org/projects/vk-url-scraper/badge/?version=latest)](https://vk-url-scraper.readthedocs.io/en/latest/?badge=latest) -->
Auto Archiver is a Python tool to automatically archive content on the web in a secure and verifiable way. It takes URLs from different sources (e.g. a CSV file, Google Sheets, command line etc.) and archives the content of each one. It can archive social media posts, videos, images and webpages. Content can be enriched, then saved either locally or remotely (S3 bucket, Google Drive). The status of the archiving process can be appended to a CSV report, or if using Google Sheets back to the original sheet.
<div class="hidden_rtd">
**[See the Auto Archiver documentation for more information.](https://auto-archiver.readthedocs.io/en/latest/)**
</div>
Read the [article about Auto Archiver on bellingcat.com](https://www.bellingcat.com/resources/2022/09/22/preserve-vital-online-content-with-bellingcats-auto-archiver-tool/).
Python tool to automatically archive social media posts, videos, and images from a Google Sheets, the console, and more. Uses different archivers depending on the platform, and can save content to local storage, S3 bucket (Digital Ocean Spaces, AWS, ...), and Google Drive. If using Google Sheets as the source for links, it will be updated with information about the archived content. It can be run manually or on an automated basis.
## Installation
There are 3 ways to use the auto-archiver:
1. (easiest installation) via docker
2. (local python install) `pip install auto-archiver`
3. (legacy/development) clone and manually install from repo (see legacy [tutorial video](https://youtu.be/VfAhcuV2tLQ))
View the [Installation Guide](https://auto-archiver.readthedocs.io/en/latest/installation/installation.html) for full instructions
But **you always need a configuration/orchestration file**, which is where you'll configure where/what/how to archive. Make sure you read [orchestration](#orchestration).
**Advanced:**
To get started quickly using Docker:
## How to install and run the auto-archiver
`docker pull bellingcat/auto-archiver && docker run -it --rm -v secrets:/app/secrets bellingcat/auto-archiver --config secrets/orchestration.yaml`
### Option 1 - docker
Or pip:
[![dockeri.co](https://dockerico.blankenship.io/image/bellingcat/auto-archiver)](https://hub.docker.com/r/bellingcat/auto-archiver)
`pip install auto-archiver && auto-archiver --help`
Docker works like a virtual machine running inside your computer, it isolates everything and makes installation simple. Since it is an isolated environment when you need to pass it your orchestration file or get downloaded media out of docker you will need to connect folders on your machine with folders inside docker with the `-v` volume flag.
## Contributing
We welcome contributions to the Auto Archiver project! See the [Contributing Guide](https://auto-archiver.readthedocs.io/en/latest/contributing.html) for how to get involved!
1. install [docker](https://docs.docker.com/get-docker/)
2. pull the auto-archiver docker [image](https://hub.docker.com/r/bellingcat/auto-archiver) with `docker pull bellingcat/auto-archiver`
3. run the docker image locally in a container: `docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml` breaking this command down:
1. `docker run` tells docker to start a new container (an instance of the image)
2. `--rm` makes sure this container is removed after execution (less garbage locally)
3. `-v $PWD/secrets:/app/secrets` - your secrets folder
1. `-v` is a volume flag which means a folder that you have on your computer will be connected to a folder inside the docker container
2. `$PWD/secrets` points to a `secrets/` folder in your current working directory (where your console points to), we use this folder as a best practice to hold all the secrets/tokens/passwords/... you use
3. `/app/secrets` points to the path the docker container where this image can be found
4. `-v $PWD/local_archive:/app/local_archive` - (optional) if you use local_storage
1. `-v` same as above, this is a volume instruction
2. `$PWD/local_archive` is a folder `local_archive/` in case you want to archive locally and have the files accessible outside docker
3. `/app/local_archive` is a folder inside docker that you can reference in your orchestration.yml file
### Option 2 - python package
<details><summary><code>Python package instructions</code></summary>
1. make sure you have python 3.8 or higher installed
2. install the package `pip/pipenv/conda install auto-archiver`
3. test it's installed with `auto-archiver --help`
4. run it with your orchestration file and pass any flags you want in the command line `auto-archiver --config secrets/orchestration.yaml` if your orchestration file is inside a `secrets/`, which we advise
You will also need [ffmpeg](https://www.ffmpeg.org/), [firefox](https://www.mozilla.org/en-US/firefox/new/) and [geckodriver](https://github.com/mozilla/geckodriver/releases), and optionally [fonts-noto](https://fonts.google.com/noto). Similar to the local installation.
</details>
### Option 3 - local installation
This can also be used for development.
<details><summary><code>Legacy instructions, only use if docker/package is not an option</code></summary>
Install the following locally:
1. [ffmpeg](https://www.ffmpeg.org/) must also be installed locally for this tool to work.
2. [firefox](https://www.mozilla.org/en-US/firefox/new/) and [geckodriver](https://github.com/mozilla/geckodriver/releases) on a path folder like `/usr/local/bin`.
3. (optional) [fonts-noto](https://fonts.google.com/noto) to deal with multiple unicode characters during selenium/geckodriver's screenshots: `sudo apt install fonts-noto -y`.
Clone and run:
1. `git clone https://github.com/bellingcat/auto-archiver`
2. `pipenv install`
3. `pipenv run python -m src.auto_archiver --config secrets/orchestration.yaml`
</details><br/>
# Orchestration
The archiver work is orchestrated by the following workflow (we call each a **step**):
1. **Feeder** gets the links (from a spreadsheet, from the console, ...)
2. **Archiver** tries to archive the link (twitter, youtube, ...)
3. **Enricher** adds more info to the content (hashes, thumbnails, ...)
4. **Formatter** creates a report from all the archived content (HTML, PDF, ...)
5. **Database** knows what's been archived and also stores the archive result (spreadsheet, CSV, or just the console)
To setup an auto-archiver instance create an `orchestration.yaml` which contains the workflow you would like. We advise you put this file into a `secrets/` folder and do not share it with others because it will contain passwords and other secrets.
The structure of orchestration file is split into 2 parts: `steps` (what **steps** to use) and `configurations` (how those steps should behave), here's a simplification:
```yaml
# orchestration.yaml content
steps:
feeder: gsheet_feeder
archivers: # order matters
- youtubedl_archiver
enrichers:
- thumbnail_enricher
formatter: html_formatter
storages:
- local_storage
databases:
- gsheet_db
configurations:
gsheet_feeder:
sheet: "your google sheet name"
header: 2 # row with header for your sheet
# ... configurations for the other steps here ...
```
To see all available `steps` (which archivers, storages, databses, ...) exist check the [example.orchestration.yaml](example.orchestration.yaml).
All the `configurations` in the `orchestration.yaml` file (you can name it differently but need to pass it in the `--config FILENAME` argument) can be seen in the console by using the `--help` flag. They can also be overwritten, for example if you are using the `cli_feeder` to archive from the command line and want to provide the URLs you should do:
```bash
auto-archiver --config secrets/orchestration.yaml --cli_feeder.urls="url1,url2,url3"
```
Here's the complete workflow that the auto-archiver goes through:
```mermaid
graph TD
s((start)) --> F(fa:fa-table Feeder)
F -->|get and clean URL| D1{fa:fa-database Database}
D1 -->|is already archived| e((end))
D1 -->|not yet archived| a(fa:fa-download Archivers)
a -->|got media| E(fa:fa-chart-line Enrichers)
E --> S[fa:fa-box-archive Storages]
E --> Fo(fa:fa-code Formatter)
Fo --> S
Fo -->|update database| D2(fa:fa-database Database)
D2 --> e
```
## Orchestration checklist
Use this to make sure you help making sure you did all the required steps:
* [ ] you have a `/secrets` folder with all your configuration files including
* [ ] a orchestration file eg: `orchestration.yaml` pointing to the correct location of other files
* [ ] (optional if you use GoogleSheets) you have a `service_account.json` (see [how-to](https://gspread.readthedocs.io/en/latest/oauth2.html#for-bots-using-service-account))
* [ ] (optional for telegram) a `anon.session` which appears after the 1st run where you login to telegram
* if you use private channels you need to add `channel_invites` and set `join_channels=true` at least once
* [ ] (optional for VK) a `vk_config.v2.json`
* [ ] (optional for using GoogleDrive storage) `gd-token.json` (see [help script](scripts/create_update_gdrive_oauth_token.py))
* [ ] (optional for instagram) `instaloader.session` file which appears after the 1st run and login in instagram
* [ ] (optional for browsertrix) `profile.tar.gz` file
#### Example invocations
The recommended way to run the auto-archiver is through Docker. The invocations below will run the auto-archiver Docker image using a configuration file that you have specified
```bash
# all the configurations come from ./secrets/orchestration.yaml
docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml
# uses the same configurations but for another google docs sheet
# with a header on row 2 and with some different column names
# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided
docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}'
# all the configurations come from orchestration.yaml and specifies that s3 files should be private
docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1
```
The auto-archiver can also be run locally, if pre-requisites are correctly configured. Equivalent invocations are below.
```bash
# all the configurations come from ./secrets/orchestration.yaml
auto-archiver --config secrets/orchestration.yaml
# uses the same configurations but for another google docs sheet
# with a header on row 2 and with some different column names
# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided
auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}'
# all the configurations come from orchestration.yaml and specifies that s3 files should be private
auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1
```
### Extra notes on configuration
#### Google Drive
To use Google Drive storage you need the id of the shared folder in the `config.yaml` file which must be shared with the service account eg `autoarchiverservice@auto-archiver-111111.iam.gserviceaccount.com` and then you can use `--storage=gd`
#### Telethon + Instagram with telegram bot
The first time you run, you will be prompted to do a authentication with the phone number associated, alternatively you can put your `anon.session` in the root.
## Running on Google Sheets Feeder (gsheet_feeder)
The `--gseets_feeder.sheet` property is the name of the Google Sheet to check for URLs.
This sheet must have been shared with the Google Service account used by `gspread`.
This sheet must also have specific columns (case-insensitive) in the `header` as specified in [Gsheet.configs](src/auto_archiver/utils/gsheet.py). The default names of these columns and their purpose is:
Inputs:
* **Link** *(required)*: the URL of the post to archive
* **Destination folder**: custom folder for archived file (regardless of storage)
Outputs:
* **Archive status** *(required)*: Status of archive operation
* **Archive location**: URL of archived post
* **Archive date**: Date archived
* **Thumbnail**: Embeds a thumbnail for the post in the spreadsheet
* **Timestamp**: Timestamp of original post
* **Title**: Post title
* **Text**: Post text
* **Screenshot**: Link to screenshot of post
* **Hash**: Hash of archived HTML file (which contains hashes of post media) - for checksums/verification
* **Perceptual Hash**: Perceptual hashes of found images - these can be used for de-duplication of content
* **WACZ**: Link to a WACZ web archive of post
* **ReplayWebpage**: Link to a ReplayWebpage viewer of the WACZ archive
For example, this is a spreadsheet configured with all of the columns for the auto archiver and a few URLs to archive. (Note that the column names are not case sensitive.)
![A screenshot of a Google Spreadsheet with column headers defined as above, and several Youtube and Twitter URLs in the "Link" column](docs/demo-before.png)
Now the auto archiver can be invoked, with this command in this example: `docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver:dockerize --config secrets/orchestration-global.yaml --gsheet_feeder.sheet "Auto archive test 2023-2"`. Note that the sheet name has been overridden/specified in the command line invocation.
When the auto archiver starts running, it updates the "Archive status" column.
![A screenshot of a Google Spreadsheet with column headers defined as above, and several Youtube and Twitter URLs in the "Link" column. The auto archiver has added "archive in progress" to one of the status columns.](docs/demo-progress.png)
The links are downloaded and archived, and the spreadsheet is updated to the following:
![A screenshot of a Google Spreadsheet with videos archived and metadata added per the description of the columns above.](docs/demo-after.png)
Note that the first row is skipped, as it is assumed to be a header row (`--gsheet_feeder.header=1` and you can change it if you use more rows above). Rows with an empty URL column, or a non-empty archive column are also skipped. All sheets in the document will be checked.
The "archive location" link contains the path of the archived file, in local storage, S3, or in Google Drive.
![The archive result for a link in the demo sheet.](docs/demo-archive.png)
---
## Development
Use `python -m src.auto_archiver --config secrets/orchestration.yaml` to run from the local development environment.
#### Docker development
working with docker locally:
* `docker build . -t auto-archiver` to build a local image
* `docker run --rm -v $PWD/secrets:/app/secrets auto-archiver --config secrets/orchestration.yaml`
* to use local archive, also create a volume `-v` for it by adding `-v $PWD/local_archive:/app/local_archive`
release to docker hub
* `docker image tag auto-archiver bellingcat/auto-archiver:latest`
* `docker push bellingcat/auto-archiver`
#### RELEASE
* update version in [version.py](src/auto_archiver/version.py)
* run `bash ./scripts/release.sh` and confirm
* package is automatically updated in pypi
* docker image is automatically pushed to dockerhup

15
docker-compose.yaml Normal file
View File

@@ -0,0 +1,15 @@
services:
auto-archiver:
# point to the local dockerfile
build:
context: .
dockerfile: Dockerfile
container_name: auto-archiver
# Override user to match host UID/GID and avoid permission issues on volumes.
# Set USER_ID and GROUP_ID env vars, or defaults to 1000:1000.
user: "${USER_ID:-1000}:${GROUP_ID:-1000}"
volumes:
- ./secrets:/app/secrets
- ./local_archive:/app/local_archive
command: --config secrets/orchestration.yaml

4
docs/_static/custom.css vendored Normal file
View File

@@ -0,0 +1,4 @@
.hidden_rtd {
display:none;
}

46
docs/_templates/autoapi/index.rst vendored Normal file
View File

@@ -0,0 +1,46 @@
API Reference
=============
These pages are intended for developers of the `auto-archiver` package,
and include documentation on the core classes and functions used by
the auto-archiver
Core Classes
------------
.. toctree::
:titlesonly:
{% for page in pages|selectattr("is_top_level_object") %}
{% if page.name == 'core' %}
{{ page.include_path }}
{% endif %}
{% endfor %}
Util Functions
--------------
.. toctree::
:titlesonly:
{% for page in pages|selectattr("is_top_level_object") %}
{% if page.name == 'utils' %}
{{ page.include_path }}
{% endif %}
{% endfor %}
Core Modules
------------
.. toctree::
:titlesonly:
{% for page in pages|selectattr("is_top_level_object") %}
{% if page.name != 'core' and page.name != 'utils' %}
{{ page.include_path }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1 @@
{% extends "python/data.rst" %}

104
docs/_templates/autoapi/python/class.rst vendored Normal file
View File

@@ -0,0 +1,104 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id | length }}
{% endif %}
{% set visible_children = obj.children|selectattr("display")|list %}
{% set own_page_children = visible_children|selectattr("type", "in", own_page_types)|list %}
{% if is_own_page and own_page_children %}
.. toctree::
:hidden:
{% for child in own_page_children %}
{{ child.include_path }}
{% endfor %}
{% endif %}
.. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}{% if obj.args %}({{ obj.args }}){% endif %}
{% for (args, return_annotation) in obj.overloads %}
{{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %}
{% endfor %}
{% if obj.bases %}
{% if "show-inheritance" in autoapi_options %}
Bases: {% for base in obj.bases %}{{ base|link_objs }}{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}
{% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %}
.. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }}
:parts: 1
{% if "private-members" in autoapi_options %}
:private-bases:
{% endif %}
{% endif %}
{% endif %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{% for obj_item in visible_children %}
{% if obj_item.type not in own_page_types %}
{{ obj_item.render()|indent(3) }}
{% endif %}
{% endfor %}
{% if is_own_page and own_page_children %}
{% set visible_attributes = own_page_children|selectattr("type", "equalto", "attribute")|list %}
{% if visible_attributes %}
Attributes
----------
.. autoapisummary::
{% for attribute in visible_attributes %}
{{ attribute.id }}
{% endfor %}
{% endif %}
{% set visible_exceptions = own_page_children|selectattr("type", "equalto", "exception")|list %}
{% if visible_exceptions %}
Exceptions
----------
.. autoapisummary::
{% for exception in visible_exceptions %}
{{ exception.id }}
{% endfor %}
{% endif %}
{% set visible_classes = own_page_children|selectattr("type", "equalto", "class")|list %}
{% if visible_classes %}
Classes
-------
.. autoapisummary::
{% for klass in visible_classes %}
{{ klass.id }}
{% endfor %}
{% endif %}
{% set visible_methods = own_page_children|selectattr("type", "equalto", "method")|list %}
{% if visible_methods %}
Methods
-------
.. autoapisummary::
{% for method in visible_methods %}
{{ method.id }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

38
docs/_templates/autoapi/python/data.rst vendored Normal file
View File

@@ -0,0 +1,38 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id | length }}
{% endif %}
.. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.name }}{% endif %}
{% if obj.annotation is not none %}
:type: {% if obj.annotation %} {{ obj.annotation }}{% endif %}
{% endif %}
{% if obj.value is not none %}
{% if obj.value.splitlines()|count > 1 %}
:value: Multiline-String
.. raw:: html
<details><summary>Show Value</summary>
.. code-block:: python
{{ obj.value|indent(width=6,blank=true) }}
.. raw:: html
</details>
{% else %}
:value: {{ obj.value|truncate(100) }}
{% endif %}
{% endif %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{% endif %}

View File

@@ -0,0 +1 @@
{% extends "python/class.rst" %}

View File

@@ -0,0 +1,21 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id | length }}
{% endif %}
.. py:function:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
{% for (args, return_annotation) in obj.overloads %}
{%+ if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %}
{% endfor %}
{% for property in obj.properties %}
:{{ property }}:
{% endfor %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,21 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id | length }}
{% endif %}
.. py:method:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
{% for (args, return_annotation) in obj.overloads %}
{%+ if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %}
{% endfor %}
{% for property in obj.properties %}
:{{ property }}:
{% endfor %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,156 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id|length }}
.. py:module:: {{ obj.name }}
{% if obj.docstring %}
.. autoapi-nested-parse::
{{ obj.docstring|indent(3) }}
{% endif %}
{% block submodules %}
{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
{% set visible_submodules = obj.submodules|selectattr("display")|list %}
{% set visible_submodules = (visible_subpackages + visible_submodules)|sort %}
{% if visible_submodules %}
Submodules
----------
.. toctree::
:maxdepth: 1
{% for submodule in visible_submodules %}
{{ submodule.include_path }}
{% endfor %}
{% endif %}
{% endblock %}
{% block content %}
{% set visible_children = obj.children|selectattr("display")|list %}
{% if visible_children %}
{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %}
{% if visible_attributes %}
{% if "attribute" in own_page_types or "show-module-summary" in autoapi_options %}
Attributes
----------
{% if "attribute" in own_page_types %}
.. toctree::
:hidden:
{% for attribute in visible_attributes %}
{{ attribute.include_path }}
{% endfor %}
{% endif %}
.. autoapisummary::
{% for attribute in visible_attributes %}
{{ attribute.id }}
{% endfor %}
{% endif %}
{% endif %}
{% set visible_exceptions = visible_children|selectattr("type", "equalto", "exception")|list %}
{% if visible_exceptions %}
{% if "exception" in own_page_types or "show-module-summary" in autoapi_options %}
Exceptions
----------
{% if "exception" in own_page_types %}
.. toctree::
:hidden:
{% for exception in visible_exceptions %}
{{ exception.include_path }}
{% endfor %}
{% endif %}
.. autoapisummary::
{% for exception in visible_exceptions %}
{{ exception.id }}
{% endfor %}
{% endif %}
{% endif %}
{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
{% if visible_classes %}
{% if "class" in own_page_types or "show-module-summary" in autoapi_options %}
Classes
-------
{% if "class" in own_page_types %}
.. toctree::
:hidden:
{% for klass in visible_classes %}
{{ klass.include_path }}
{% endfor %}
{% endif %}
.. autoapisummary::
{% for klass in visible_classes %}
{{ klass.id }}
{% endfor %}
{% endif %}
{% endif %}
{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
{% if visible_functions %}
{% if "function" in own_page_types or "show-module-summary" in autoapi_options %}
Functions
---------
{% if "function" in own_page_types %}
.. toctree::
:hidden:
{% for function in visible_functions %}
{{ function.include_path }}
{% endfor %}
{% endif %}
.. autoapisummary::
{% for function in visible_functions %}
{{ function.id }}
{% endfor %}
{% endif %}
{% endif %}
{% set this_page_children = visible_children|rejectattr("type", "in", own_page_types)|list %}
{% if this_page_children %}
{{ obj.type|title }} Contents
{{ "-" * obj.type|length }}---------
{% for obj_item in this_page_children %}
{{ obj_item.render()|indent(0) }}
{% endfor %}
{% endif %}
{% endif %}
{% endblock %}
{% else %}
.. py:module:: {{ obj.name }}
{% if obj.docstring %}
.. autoapi-nested-parse::
{{ obj.docstring|indent(6) }}
{% endif %}
{% for obj_item in visible_children %}
{{ obj_item.render()|indent(3) }}
{% endfor %}
{% endif %}
{% endif %}

View File

@@ -0,0 +1 @@
{% extends "python/module.rst" %}

View File

@@ -0,0 +1,21 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.id }}
{{ "=" * obj.id | length }}
{% endif %}
.. py:property:: {% if is_own_page %}{{ obj.id}}{% else %}{{ obj.short_name }}{% endif %}
{% if obj.annotation %}
:type: {{ obj.annotation }}
{% endif %}
{% for property in obj.properties %}
:{{ property }}:
{% endfor %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{% endif %}

1
docs/scripts/__init__.py Normal file
View File

@@ -0,0 +1 @@
from scripts import generate_module_docs

168
docs/scripts/scripts.py Normal file
View File

@@ -0,0 +1,168 @@
# iterate through all the modules in auto_archiver.modules and turn the __manifest__.py file into a markdown table
from pathlib import Path
from auto_archiver.core.module import ModuleFactory
from auto_archiver.core.base_module import BaseModule
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
import io
MODULES_FOLDER = Path(__file__).parent.parent.parent.parent / "src" / "auto_archiver" / "modules"
SAVE_FOLDER = Path(__file__).parent.parent / "source" / "modules" / "autogen"
type_color = {
"feeder": "<span style='color: #FFA500'>[feeder](/core_modules.md#feeder-modules)</a></span>",
"extractor": "<span style='color: #00FF00'>[extractor](/core_modules.md#extractor-modules)</a></span>",
"enricher": "<span style='color: #0000FF'>[enricher](/core_modules.md#enricher-modules)</a></span>",
"database": "<span style='color: #FF00FF'>[database](/core_modules.md#database-modules)</a></span>",
"storage": "<span style='color: #FFFF00'>[storage](/core_modules.md#storage-modules)</a></span>",
"formatter": "<span style='color: #00FFFF'>[formatter](/core_modules.md#formatter-modules)</a></span>",
}
TABLE_HEADER = ("Option", "Description", "Default", "Type")
EXAMPLE_YAML = """
# steps configuration
steps:
...
{steps_str}
...
# module configuration
...
{config_string}
"""
def generate_module_docs():
yaml = YAML()
SAVE_FOLDER.mkdir(exist_ok=True)
modules_by_type = {}
header_row = "| " + " | ".join(TABLE_HEADER) + "|\n" + "| --- " * len(TABLE_HEADER) + "|\n"
global_table = "\n## Configuration Options\n" + header_row
global_yaml = yaml.load("""\n# Module configuration\nplaceholder: {}""")
for module in sorted(ModuleFactory().available_modules(), key=lambda x: (x.requires_setup, x.name)):
# generate the markdown file from the __manifest__.py file.
manifest = module.manifest
for type in manifest["type"]:
modules_by_type.setdefault(type, []).append(module)
description = "\n".join(line.lstrip() for line in manifest["description"].split("\n"))
types = ", ".join(type_color[t] for t in manifest["type"])
readme_str = f"""
# {manifest["name"]}
```{{admonition}} Module type
{types}
```
{description}
"""
steps_str = "\n".join(f" {t}s:\n - {module.name}" for t in manifest["type"])
if manifest.get("autodoc_dropins"):
loaded_module = module.load({})
dropins = loaded_module.load_dropins()
dropin_str = "\n##### Available Dropins\n"
for dropin in dropins:
if not (ddoc := dropin.documentation()):
continue
dropin_str += f"\n###### {ddoc.get('name', dropin.__name__)}\n\n"
dropin_str += f"{ddoc.get('description')}\n\n"
if ddoc.get("site"):
dropin_str += f"**Site**: {ddoc['site']}\n\n"
if dauth := ddoc.get("authentication"):
dropin_str += "**YAML configuration**:\n"
dropin_auth_yaml = "authentication:\n...\n"
for site, creds in dauth.items():
dropin_auth_yaml += f" {site}:\n"
for k, v in creds.items():
dropin_auth_yaml += f' {k}: "{v}"\n'
dropin_str += f"```{{code}} yaml\n{dropin_auth_yaml}...\n```\n"
readme_str += dropin_str
if not manifest["configs"]:
config_string = f"# No configuration options for {module.name}.*\n"
else:
config_table = header_row
config_yaml = {}
global_yaml[module.name] = CommentedMap()
global_yaml.yaml_set_comment_before_after_key(
module.name, f"\n\n{module.display_name} configuration options"
)
for key, value in manifest["configs"].items():
type = value.get("type", "string")
if type == "json_loader":
value["type"] = "json"
elif type == "str":
type = "string"
default = value.get("default", "")
config_yaml[key] = default
global_yaml[module.name][key] = default
if value.get("help", ""):
global_yaml[module.name].yaml_add_eol_comment(value.get("help", ""), key)
help = "**Required**. " if value.get("required", False) else "Optional. "
help += value.get("help", "")
config_table += f"| `{module.name}.{key}` | {help} | {value.get('default', '')} | {type} |\n"
global_table += f"| `{module.name}.{key}` | {help} | {default} | {type} |\n"
readme_str += "\n## Configuration Options\n"
readme_str += "\n### YAML\n"
config_string = io.BytesIO()
yaml.dump({module.name: config_yaml}, config_string)
config_string = config_string.getvalue().decode("utf-8")
yaml_string = EXAMPLE_YAML.format(steps_str=steps_str, config_string=config_string)
readme_str += f"```{{code}} yaml\n{yaml_string}\n```\n"
if manifest["configs"]:
readme_str += "\n### Command Line:\n"
readme_str += config_table
# add a link to the autodoc refs
readme_str += f"\n[API Reference](../../../autoapi/{module.name}/index)\n"
# create the module.type folder, use the first type just for where to store the file
for type in manifest["type"]:
type_folder = SAVE_FOLDER / type
type_folder.mkdir(exist_ok=True)
with open(type_folder / f"{module.name}.md", "w") as f:
print("writing", SAVE_FOLDER)
f.write(readme_str)
generate_index(modules_by_type)
del global_yaml["placeholder"]
global_string = io.BytesIO()
global_yaml = yaml.dump(global_yaml, global_string)
global_string = global_string.getvalue().decode("utf-8")
global_yaml = f"```yaml\n{global_string}\n```"
with open(SAVE_FOLDER / "configs_cheatsheet.md", "w") as f:
f.write("### Configuration File\n" + global_yaml + "\n### Command Line\n" + global_table)
def generate_index(modules_by_type):
readme_str = ""
for type in BaseModule.MODULE_TYPES:
modules = modules_by_type.get(type, [])
module_str = f"## {type.capitalize()} Modules\n"
for module in modules:
module_str += f"\n[{module.manifest['name']}](/modules/autogen/{module.type[0]}/{module.name}.md)\n"
with open(SAVE_FOLDER / f"{type}.md", "w") as f:
print("writing", SAVE_FOLDER / f"{type}.md")
f.write(module_str)
readme_str += module_str
with open(SAVE_FOLDER / "module_list.md", "w") as f:
print("writing", SAVE_FOLDER / "module_list.md")
f.write(readme_str)
if __name__ == "__main__":
generate_module_docs()

BIN
docs/source/bc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

94
docs/source/conf.py Normal file
View File

@@ -0,0 +1,94 @@
# Configuration file for the Sphinx documentation builder.
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import sys
import os
from importlib.metadata import metadata
from datetime import datetime
sys.path.append(os.path.abspath("../scripts"))
from scripts import generate_module_docs
from auto_archiver.version import __version__
# -- Project Hooks -----------------------------------------------------------
# convert the module __manifest__.py files into markdown files
generate_module_docs()
# -- Project information -----------------------------------------------------
package_metadata = metadata("auto-archiver")
project = package_metadata["name"]
copyright = str(datetime.now().year)
author = "Bellingcat"
release = package_metadata["version"]
language = "en"
# -- General configuration ---------------------------------------------------
extensions = [
"myst_parser", # Markdown support
"autoapi.extension", # Generate API documentation from docstrings
"sphinxcontrib.mermaid", # Mermaid diagrams
"sphinx.ext.viewcode", # Source code links
"sphinx_copybutton",
"sphinx.ext.napoleon", # Google-style and NumPy-style docstrings
"sphinx.ext.autosectionlabel",
# 'sphinx.ext.autosummary', # Summarize module/class/function docs
]
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ""]
# -- AutoAPI Configuration ---------------------------------------------------
autoapi_type = "python"
autoapi_dirs = ["../../src/auto_archiver/core/", "../../src/auto_archiver/utils/"]
# get all the modules and add them to the autoapi_dirs
autoapi_dirs.extend([f"../../src/auto_archiver/modules/{m}" for m in os.listdir("../../src/auto_archiver/modules")])
autodoc_typehints = "signature" # Include type hints in the signature
autoapi_ignore = [
"*/version.py",
] # Ignore specific modules
autoapi_keep_files = True # Option to retain intermediate JSON files for debugging
autoapi_add_toctree_entry = True # Include API docs in the TOC
autoapi_python_use_implicit_namespaces = True
autoapi_template_dir = "../_templates/autoapi"
autoapi_options = [
"members",
"undoc-members",
"show-inheritance",
"imported-members",
]
# -- Markdown Support --------------------------------------------------------
myst_enable_extensions = [
"deflist", # Definition lists
"html_admonition", # HTML-style admonitions
"html_image", # Inline HTML images
"replacements", # Substitutions like (C)
"smartquotes", # Smart quotes
"linkify", # Auto-detect links
"substitution", # Text substitutions
]
myst_heading_anchors = 2
myst_fence_as_directive = ["mermaid"]
source_suffix = {
".rst": "restructuredtext",
".md": "markdown",
}
# -- Options for HTML output -------------------------------------------------
html_theme = "sphinx_book_theme"
html_static_path = ["../_static"]
html_css_files = ["custom.css"]
html_title = f"Auto Archiver v{__version__}"
html_logo = "bc.png"
html_theme_options = {
"repository_url": "https://github.com/bellingcat/auto-archiver",
"use_repository_button": True,
}
copybutton_prompt_text = r">>> |\.\.\."
copybutton_prompt_is_regexp = True
copybutton_only_copy_prompt_lines = False

View File

@@ -0,0 +1,2 @@
```{include} ../../CONTRIBUTING.md
```

View File

@@ -0,0 +1,28 @@
# Module Documentation
These pages describe the core modules that come with Auto Archiver and provide the main functionality for archiving websites on the internet. There are five core module types:
1. Feeders - these 'feed' information (the URLs) from various sources to the Auto Archiver for processing
2. Extractors - these 'extract' the page data for a given URL that is fed in by a feeder
3. Enrichers - these 'enrich' the data extracted in the previous step with additional information
4. Storage - these 'store' the data in a persistent location (on disk, Google Drive etc.)
5. Databases - these 'store' the status of the entire archiving process in a log file or database.
```{include} modules/autogen/module_list.md
```
```{toctree}
:maxdepth: 1
:caption: Core Modules
:hidden:
modules/config_cheatsheet
modules/feeder
modules/extractor
modules/enricher
modules/storage
modules/database
modules/formatter
```

View File

@@ -0,0 +1,52 @@
# Creating Your Own Modules
Modules are what's used to extend Auto Archiver to process different websites or media, and/or transform the data in a way that suits your needs. In most cases, the [Core Modules](../core_modules.md) should be sufficient for every day use, but the most common use-cases for making your own Modules include:
1. Extracting data from a website which doesn't work with the current core extractors.
2. Enriching or altering the data before saving with additional information that the core enrichers do not offer.
3. Storing your data in a different format/location from what the core storage providers offer.
## Setting up the folder structure
1. First, decide what type of module you wish to create. Check the types of modules on the [](../core_modules.md) page to decide what type you need. (Note: a module can be more than one type, more on that below)
2. Create a new python package (a folder) with the name of your module (in this tutorial, we'll call it `awesome_extractor`).
3. Create the `__manifest__.py` and an the `awesome_extractor.py` files in this folder.
When done, you should have a module structure as follows:
```
.
├── awesome_extractor
│ ├── __manifest__.py
│ └── awesome_extractor.py
```
Check out the [core modules](https://github.com/bellingcat/auto-archiver/tree/main/src/auto_archiver/modules) in the Auto Archiver repository for examples of the folder structure for real-world modules.
## Populating the Manifest File
The manifest file is where you define the core information of your module. It is a python dict containing important information, here's an example file:
```{include} ../../../tests/data/test_modules/example_module/__manifest__.py
:name: __manifest__.py
:literal:
:parser: python
```
## Creating the Python Code
The next step is to create your module code. First, create a class which should subclass the base module types from `auto_archiver.core`, here's an example class for the `awesome_extractor` module which is an `extractor`:
```{code-block} python
:filename: awesome_extractor.py
from auto_archiver.core import Extractor, Metadata
def AwesomeExtractor(Extractor):
def download(self, item: Metadata) -> Metadata | False:
url = item.get_url()
# download the content and create the metadata object
metadata = ...
return metadata
```

View File

@@ -0,0 +1,36 @@
# Developer Guidelines
This section of the documentation provides guidelines for developers who want to modify or contribute to the tool.
## Developer Install
1. Clone the project using `git clone https://github.com/bellingcat/auto-archiver.git`
2. Install poetry using `curl -sSL https://install.python-poetry.org | python3 -` ([other installation methods](https://python-poetry.org/docs/#installation))
3. Install dependencies with `poetry install`
## Running
4. Run the code with `poetry run auto-archiver [my args]`
```{note}
Add the plugin [poetry-shell-plugin](https://github.com/python-poetry/poetry-plugin-shell) and run `poetry shell` to activate the virtual environment.
This allows you to run the auto-archiver without the `poetry run` prefix.
```
### Optional Development Packages
Install development packages (used for unit tests etc.) using:
`poetry install --with dev`
```{toctree}
:hidden:
creating_modules
docker_development
testing
docs
release
settings_page
style_guide
```

View File

@@ -0,0 +1,5 @@
## Docker development
working with docker locally:
* `docker compose up` to build the first time and run a local image with the settings in `secrets/orchestration.yaml`
* To modify/pass additional command line args, use `docker compose run auto-archiver --config secrets/orchestration.yaml [OTHER ARGUMENTS]`
* To rebuild after code changes, just pass the `--build` flag, e.g. `docker compose up --build`

View File

@@ -0,0 +1,47 @@
### Building the Docs
The documentation is built using [Sphinx](https://www.sphinx-doc.org/en/master/) and [AutoAPI](https://sphinx-autoapi.readthedocs.io/en/latest/) and hosted on ReadTheDocs.
To build the documentation locally, run the following commands:
**Install required dependencies:**
- Install the docs group of dependencies:
```shell
# only the docs dependencies
poetry install --only docs
# or for all dependencies
poetry install
```
- Either use [poetry-plugin-shell](https://github.com/python-poetry/poetry-plugin-shell) to activate the virtual environment: `poetry shell`
- Or prepend the following commands with `poetry run`
**Create the documentation:**
- Build the documentation:
```shell
# Using makefile (Linux/macOS):
make -C docs html
# or using sphinx directly (Windows/Linux/macOS):
sphinx-build -b html docs/source docs/_build/html
```
- If you make significant changes and want a fresh build run: `make -C docs clean` to remove the old build files.
**Viewing the documentation:**
```shell
# to open the documentation in your browser.
open docs/_build/html/index.html
# or run autobuild to automatically update the documentation when you make changes
sphinx-autobuild docs/source docs/_build/html
```
### Managing Readthedocs (RTD) Versions
Version management is done at [https://app.readthedocs.org/projects/auto-archiver/](https://app.readthedocs.org/projects/auto-archiver/)
(login required). Once logged in, you can create new versions, delete old versions or change visibility of versions. More info on
[RTD](https://docs.readthedocs.com/platform/stable/versions.html).
Currently, the Auto Archiver project is set up to automatically create a new docs version for each `vX.Y.Z` release. For more on this,
see the RTD [instructions on automation](https://docs.readthedocs.com/platform/stable/guides/automation-rules.html) or edit the existing automation rule in the project settings.

View File

@@ -0,0 +1,33 @@
# Release Process
```{note} This is a work in progress.
```
### Update the project version
Update the version number in the project file: [pyproject.toml](../../pyproject.toml) following SemVer:
```toml
[project]
name = "auto-archiver"
version = "0.1.1"
```
Then commit and push the changes.
* The package version is automatically updated in PyPi using the workflow [python-publish.yml](../../.github/workflows/python-publish.yml)
* A Docker image is automatically pushed with the git tag to dockerhub using the workflow [docker-publish.yml](../../.github/workflows/docker-publish.yml)
### Create the release on Git
The release needs a git tag which should match the project version number, prefixed with a 'v'. For example, if the project version is `0.1.1`, the git tag should be `v0.1.1`.
This can be done the usual way, or created within the Github UI when you create the release.
Go to GitHub releases > new release > create the release with the new tag and the release notes.
manual release to docker hub
* `docker image tag auto-archiver bellingcat/auto-archiver:latest`
* `docker push bellingcat/auto-archiver`
### Building the Settings Page
The Settings page is built as part of the python-publish workflow and packaged within the app.

View File

@@ -0,0 +1,31 @@
# Configuration Editor
The [configuration editor](../installation/config_editor.md), is an easy-to-use UI for users to edit their auto-archiver settings.
The single-file app is built using React and vite. To get started developing the package, follow these steps:
1. Make sure you have Node v22 installed.
```{note} Tip: if you don't have node installed:
Use `nvm` to manage your node installations. Use:
`curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash` to install `nvm` and then `nvm i 22` to install Node v22
```
2. Generate the `schema.json` file for the currently installed modules using `python scripts/generate_settings_schema.py`
3. Go to the settings folder `cd scripts/settings/` and build your environment with `npm i`
4. Run a development version of the page with `npm run dev` and then open localhost:5173.
5. Build a release version of the page with `npm run build`
A release version creates a single-file app called `dist/index.html`. This file should be copied to `docs/source/installation/settings_base.html` so that it can be integrated into the sphinx docs.
```{note}
The single-file app dist/index.html does not include any `<html>` or `<head>` tags as it is designed to be built into a RTD docs page. Edit `index.html` in the settings folder if you wish to modify the built page.
```
## Readthedocs Integration
The configuration editor is built as part of the RTD deployment (see `.readthedocs.yaml` file). This command is run every time RTD is built:
`cd scripts/settings && npm install && npm run build && yes | cp dist/index.html ../../docs/source/installation/settings_base.html && cd ../..`

View File

@@ -0,0 +1,70 @@
# Style Guide
The project uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting.
Our style configurations are set in the `pyproject.toml` file. If needed, you can modify them there.
### **Formatting (Auto-Run Before Commit) 🛠️**
We have a pre-commit hook to run the formatter before you commit.
This requires you to set it up once locally, then it will run automatically when you commit changes.
```shell
poetry run pre-commit install
```
Ruff can also be to run automatically.
Alternative: Ruff can also be [integrated with most editors](https://docs.astral.sh/ruff/editors/setup/) for real-time formatting.
If you wish to disable the pre-commit hook (for example, if you want to commit some WIP code) you can use the `--no-verify` flag when you commit.
For example: `git commit -m "WIP Code" --no-verify`
### **Linting (Check Before Pushing) 🔍**
We recommend you also run the linter before pushing code.
We have [Makefile](../../../Makefile) commands to run common tasks.
Tip: if you're on Windows you might need to install `make` first, or alternatively you can use ruff commands directly.
**Lint Check:** This outputs a report of any issues found, without attempting to fix them:
```shell
make ruff-check
```
Tip: To see a more detailed linting report, you can remove the following line from the `pyproject.toml` file:
```toml
[tool.ruff]
# Remove this for a more detailed lint report
output-format = "concise"
```
**Lint Fix:** This command will attempt to fix some of the issues it picked up with the lint check.
Note not all warnings can be fixed automatically.
⚠️ Warning: This can cause breaking changes. ⚠️
Most fixes are safe, but some non-standard practices such as dynamic loading are not picked up by linters. Ensure you check any modifications by this before committing them.
```shell
make ruff-clean
```
**Changing Configurations ⚙️**
Our rules are quite lenient for general usage, but if you want to run more rigorous checks you can then run checks with additional rules to see more nuanced errors which you can review manually.
Check out the [ruff documentation](https://docs.astral.sh/ruff/configuration/) for the full list of rules.
One example is to extend the selected rules for linting the `pyproject.toml` file:
```toml
[tool.ruff.lint]
# Extend the rules to check for by adding them to this option:
# See documentation for more details: https://docs.astral.sh/ruff/rules/
extend-select = ["B"]
```
Then re-run the `make ruff-check` command to see the new rules in action.

View File

@@ -0,0 +1,32 @@
# Testing
`pytest` is used for testing. There are two main types of tests:
1. 'core' tests which should be run on every change
2. 'download' tests which hit the network. These tests will do things like make API calls (e.g. Twitter, Bluesky etc.) and should be run regularly to make sure that APIs have not changed, they take longer.
## Running Tests
1. Make sure you've installed the dev dependencies with `poetry install --with dev`
2. Tests can be run as follows:
```{code} bash
#### Command prefix of 'poetry run' removed here for simplicity
# run core tests
pytest -ra -v -m "not download"
# run download tests
pytest -ra -v -m "download"
# run all tests
pytest -ra -v
# run a specific test file
pytest -ra -v tests/test_file.py
# run a specific test function
pytest -ra -v tests/test_file.py::test_function_name
```
3. Some tests require environment variables to be set. You can use the example `tests/.env.test.example` file as a template. Copy it to `tests/.env.test` and fill in the required values. This file will be loaded automatically by `pytest`.
```{code} bash
cp tests/.env.test.example tests/.env.test
```

View File

@@ -0,0 +1,79 @@
# Auto Archiver Configuration
# Steps are the modules that will be run in the order they are defined
steps:
feeders:
- cli_feeder
extractors:
- generic_extractor
- telegram_extractor
enrichers:
- thumbnail_enricher
- meta_enricher
- pdq_hash_enricher
- ssl_enricher
- hash_enricher
databases:
- console_db
- csv_db
storages:
- local_storage
formatters:
- html_formatter
# Global configuration
# Authentication
# a dictionary of authentication information that can be used by extractors to login to website.
# you can use a comma separated list for multiple domains on the same line (common usecase: x.com,twitter.com)
# Common login 'types' are username/password, cookie, api key/token.
# There are two special keys for using cookies, they are: cookies_file and cookies_from_browser.
# Some Examples:
# facebook.com:
# username: "my_username"
# password: "my_password"
# or for a site that uses an API key:
# twitter.com,x.com:
# api_key
# api_secret
# youtube.com:
# cookie: "login_cookie=value ; other_cookie=123" # multiple 'key=value' pairs should be separated by ;
authentication: {}
# Logging settings for your project. See the logging settings with --help
logging:
level: INFO
# These are the global configurations that are used by the modules
file:
rotation:
local_storage:
path_generator: flat
filename_generator: static
save_to: ./local_archive
save_absolute: false
html_formatter:
detect_thumbnails: true
thumbnail_enricher:
thumbnails_per_minute: 60
max_thumbnails: 16
generic_extractor:
subtitles: true
comments: false
livestreams: false
live_from_start: false
proxy: ''
end_means_success: true
allow_playlist: false
max_downloads: inf
csv_db:
csv_file: db.csv
ssl_enricher:
skip_when_nothing_archived: true
hash_enricher:
algorithm: SHA-256
chunksize: 16000000

View File

@@ -0,0 +1,30 @@
# Archiving Overview
The archiver archives web pages using the following workflow
1. **Feeder** gets the links (from a spreadsheet, from the console, ...)
2. **Extractor** tries to extract content from the given link (e.g. videos from youtube, images from Twitter...)
3. **Enricher** adds more info to the content (hashes, thumbnails, ...)
4. **Formatter** creates a report from all the archived content (HTML, PDF, ...)
5. **Database** knows what's been archived and also stores the archive result (spreadsheet, CSV, or just the console)
Each step in the workflow is handled by 'modules' that interact with the data in different ways. For example, the Twitter Extractor Module would extract information from the Twitter website. The AntiBot Module will download HTML and take screenshots of the given page. See the [core modules page](core_modules.md) for an overview of all the modules that are available.
Auto-archiver must have at least one module defined for each step of the workflow. This is done by setting the [configuration](installation/configurations.md) for your auto-archiver instance.
Here's the complete workflow that the auto-archiver goes through:
```mermaid
graph TD
s((start)) --> F(fa:fa-table Feeder)
F -->|get and clean URL| D1{fa:fa-database Database}
D1 -->|is already archived| e((end))
D1 -->|not yet archived| a(fa:fa-download Archivers)
a -->|got media| E(fa:fa-chart-line Enrichers)
E --> S[fa:fa-box-archive Storages]
E --> Fo(fa:fa-code Formatter)
Fo --> S
Fo -->|update database| D2(fa:fa-database Database)
D2 --> e
```

12
docs/source/how_to.md Normal file
View File

@@ -0,0 +1,12 @@
# How-To Guides
The follow pages contain helpful how-to guides for common use cases of the Auto Archiver.
---
```{toctree}
:maxdepth: 1
:glob:
how_to/*
```

View File

@@ -0,0 +1,222 @@
# Logging in to sites
This how-to guide shows you how you can use various authentication methods to allow you to login to a site you are trying to archive. This is useful for websites that require a user to be logged in to browse them, or for sites that restrict bots.
In this How-To, we will authenticate on use Twitter/X.com using cookies, and on XXXX using username/password.
## Using cookies to authenticate on Twitter/X
It can be useful to archive tweets after logging in, since some tweets are only visible to authenticated users. One case is Tweets marked as 'Sensitive'.
Take this tweet as an example: [https://x.com/SozinhoRamalho/status/1876710769913450647](https://x.com/SozinhoRamalho/status/1876710769913450647)
This tweet has been marked as sensitive, so a normal run of Auto Archiver without a logged in session will fail to extract the tweet:
```{code-block} console
:emphasize-lines: 3,4,5,6
>>> auto-archiver https://x.com/SozinhoRamalho/status/1876710769913450647 ✭ ✱
...
ERROR: [twitter] 1876710769913450647: NSFW tweet requires authentication. Use --cookies,
--cookies-from-browser, --username and --password, --netrc-cmd, or --netrc (twitter) to
provide account credentials. See https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp
for how to manually pass cookies
[twitter] 1876710769913450647: Downloading guest token
[twitter] 1876710769913450647: Downloading GraphQL JSON
2025-02-20 15:06:13.362 | ERROR | auto_archiver.modules.generic_extractor.generic_extractor:download_for_extractor:248 - Error downloading metadata for post: NSFW tweet requires authentication. Use --cookies, --cookies-from-browser, --username and --password, --netrc-cmd, or --netrc (twitter) to provide account credentials. See https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp for how to manually pass cookies
[generic] Extracting URL: https://x.com/SozinhoRamalho/status/1876710769913450647
[generic] 1876710769913450647: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] 1876710769913450647: Extracting information
ERROR: Unsupported URL: https://x.com/SozinhoRamalho/status/1876710769913450647
2025-02-20 15:06:13.744 | INFO | auto_archiver.core.orchestrator:archive:483 - Trying extractor telegram_extractor for https://x.com/SozinhoRamalho/status/1876710769913450647
2025-02-20 15:06:13.744 | SUCCESS | auto_archiver.modules.console_db.console_db:done:23 - DONE Metadata(status='nothing archived', metadata={'_processed_at': datetime.datetime(2025, 2, 20, 15, 6, 12, 473979, tzinfo=datetime.timezone.utc), 'url': 'https://x.com/SozinhoRamalho/status/1876710769913450647'}, media=[])
...
```
To get round this limitation, we can use **cookies** (information about a logged in user) to mimic being logged in to Twitter. There are two ways to pass cookies to Auto Archiver. One is from a file, and the other is from a browser profile on your computer.
In this tutorial, we will export the Twitter cookies from our browser and add them to Auto Archiver
**1. Installing a cookie exporter extension**
First, we need to install an extension in our browser to export the cookies for a certain site. The [FAQ on yt-dlp](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp) provides some suggestions: Get [cookies.txt LOCALLY](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) for Chrome or [cookies.txt](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/) for Firefox.
**2. Export the cookies**
```{note} See the note [here](../installation/authentication.md#recommendations-for-authentication) on why you shouldn't use your own personal account for archiving.
```
Once the extension is installed in your preferred browser, login to Twitter in this browser, and then activate the extension and export the cookies. You can choose to export all your cookies for your browser, or just cookies for this specific site. In the image below, we're only exporting cookies for Twitter/x.com:
![extract cookies](extract_cookies.png)
**3. Adding the cookies file to Auto Archiver**
You now will have a file called `cookies.txt` (tip: name it `twitter_cookies.txt` if you only exported cookies for Twitter), which needs to be added to Auto Archiver.
Do this by going into your Auto Archiver configuration file, and editing the `authentication` section. We will add the `cookies_file` option for the site `x.com,twitter.com`.
```{note} For websites that have multiple URLs (like x.com and twitter.com) you can 'reuse' the same login information without duplicating it using a comma separated list of domain names.
```
I've saved my `twitter_cookies.txt` file in a `secrets` folder, so here's how my authentication section looks now:
```{code} yaml
:caption: orchestration.yaml
...
authentication:
x.com,twitter.com:
cookies_file: secrets/twitter_cookies.txt
...
```
**4. Re-run your archiving with the cookies enabled**
Now, the next time we re-run Auto Archiver, the cookies from our logged-in session will be used by Auto Archiver, and restricted/sensitive tweets can be downloaded!
```{code} console
>>> auto-archiver https://x.com/SozinhoRamalho/status/1876710769913450647 ✭ ✱ ◼
...
2025-02-20 15:27:46.785 | WARNING | auto_archiver.modules.console_db.console_db:started:13 - STARTED Metadata(status='no archiver', metadata={'_processed_at': datetime.datetime(2025, 2, 20, 15, 27, 46, 785304, tzinfo=datetime.timezone.utc), 'url': 'https://x.com/SozinhoRamalho/status/1876710769913450647'}, media=[])
2025-02-20 15:27:46.785 | INFO | auto_archiver.core.orchestrator:archive:483 - Trying extractor generic_extractor for https://x.com/SozinhoRamalho/status/1876710769913450647
[twitter] Extracting URL: https://x.com/SozinhoRamalho/status/1876710769913450647
...
2025-02-20 15:27:53.134 | INFO | auto_archiver.modules.local_storage.local_storage:upload:26 - ./local_archive/https-x-com-sozinhoramalho-status-1876710769913450647/06e8bacf27ac4bb983bf6280.html
2025-02-20 15:27:53.135 | SUCCESS | auto_archiver.modules.console_db.console_db:done:23 - DONE Metadata(status='yt-dlp_Twitter: success',
metadata={'_processed_at': datetime.datetime(2025, 2, 20, 15, 27, 48, 564738, tzinfo=datetime.timezone.utc), 'url':
'https://x.com/SozinhoRamalho/status/1876710769913450647', 'title': 'ignore tweet, testing sensitivity warning nudity https://t.co/t3u0hQsSB1',
...
```
### Finishing Touches
You've now successfully exported your cookies from a logged-in session in your browser, and used them to authenticate with Twitter and download a sensitive tweet. Congratulations!
Finally,Some important things to remember:
1. It's best not to use your own personal account for archiving. [Here's why](../installation/authentication.md#recommendations-for-authentication).
2. Cookies can be short-lived, so may need updating. Sometimes, a website session may 'expire' or a website may force you to login again. In these instances, you'll need to repeat the export step (step 2) after logging in again to update your cookies.
## Authenticating on XXXX site with username/password
```{note}
This section is still under construction 🚧
```
# Proof of Origin Tokens
YouTube uses **Proof of Origin Tokens (POT)** as part of its bot detection system to verify that requests originate from valid clients. If a token is missing or invalid, some videos may return errors like "Sign in to confirm you're not a bot."
yt-dlp provides [a detailed guide to POTs](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide).
### How Auto Archiver Uses POT
This feature is enabled for the Generic Archiver via two yt-dlp plugins:
- **Client-side plugin**: [yt-dlp-get-pot](https://github.com/coletdjnz/yt-dlp-get-pot)
Detects when a token is required and requests one from a provider.
- **Provider plugin**: [bgutil-ytdlp-pot-provider](https://github.com/Brainicism/bgutil-ytdlp-pot-provider)
Includes both a Python plugin and a **Node.js server or script** to generate the token.
These are installed in our Poetry environment.
### Integration Methods
**Docker (Recommended)**:
When running the Auto Archiver using the Docker image, we use the [Node.js token generation script](https://github.com/Brainicism/bgutil-ytdlp-pot-provider/tree/master/server).
This is to avoid managing a separate server process, and is handled automatically inside the Docker container when needed.
This is already included in the Docker image, however if you need to disable this you can set the config option `bguils_po_token_method` under the `generic_extractor` section of your `orchestration.yaml` config file to "disabled".
```yaml
generic_extractor:
bguils_po_token_method: "disabled"
```
**PyPi/ Local**:
When using the Auto Archiver PyPI package, or running locally, you will need additional system requirements to run the token generation script, namely either Docker, or Node.js and Yarn.
See the [bgutil-ytdlp-pot-provider](https://github.com/Brainicism/bgutil-ytdlp-pot-provider?tab=readme-ov-file#a-http-server-option) documentation for more details.
WARNING⚠: This will add the server scripts to the home directory of wherever this is running.
- You can set the config option `bguils_po_token_method` under the `generic_extractor` section of your `orchestration.yaml` config file to "script" to enable the token generation script process locally.
- Alternatively you can run the bgutil-ytdlp-pot-provider server separately using their Docker image or Node.js server.
### Notes
- The token generation script is only triggered when needed by yt-dlp, so it should have no effect unless YouTube requests a POT.
- If you're running the Auto Archiver in Docker, this is set up automatically.
- If you're running locally, you'll need to run the setup script manually or enable the feature in your config.
- You can set up both the server and the script, and the plugin will fallback on each other if needed. This is recommended for robustness!
### Configurations:
## Configurations Summary
| Option | Behavior | Docker Default? |
|------------| ------------------------------------------------------------------------------------------------------------------------------------------ | --------------- |
| `auto` | Docker: Automatically downloads and uses the token generation script. Local: Does nothing; assumes a separate server is running externally. | ✅ Yes |
| `script` | Explicitly downloads and uses the token generation script, even locally. | ❌ No |
| `disabled` | Disables token generation completely. | ❌ No |
Example configuration:
```yaml
generic_extractor:
# ...
bguils_po_token_method: "script"
# For debugging add the verbose flag here:
ytdlp_args: "--no-abort-on-error --abort-on-error --verbose"
```
**Advanced Configuration:**
If you change the default port of the bgutil-ytdlp-pot-provider server, you can pass the updated values using our `extractor_args` option for the gereric extractor.
```yaml
generic_extractor:
ytdlp_args: "--no-abort-on-error --abort-on-error --verbose"
ytdlp_update_interval: 5
bguils_po_token_method: "script"
extractor_args:
youtube:
getpot_bgutil_baseurl: "http://127.0.0.1:8080"
player_client: web,tv
```
For more details on this for bgutils see [here](https://github.com/Brainicism/bgutil-ytdlp-pot-provider?tab=readme-ov-file#usage)
### Checking the logs
To verify that the POT process working, look for the following lines in your log after adding the config option:
```shell
[GetPOT] BgUtilScript: Generating POT via script: /Users/you/bgutil-ytdlp-pot-provider/server/build/generate_once.js
[debug] [GetPOT] BgUtilScript: Executing command to get POT via script: /Users/you/.nvm/versions/node/v20.18.0/bin/node /Users/you/bgutil-ytdlp-pot-provider/server/build/generate_once.js -v ymCMy8OflKM
[debug] [GetPOT] BgUtilScript: stdout:
{"poToken":"MlMxojNFhEJvUzGeHEkVRSK_luXtwcDnwSNIOgaUutqB7t99nmlNvtWgYayboopG6ZopZgmQ-6PJCWEMHv89MIiFGGlJRY25Fkwzxmia_8uYgf5AWf==","generatedAt":"2025-03-26T10:45:26.156Z","visitIdentifier":"ymCMy8OflKM"}
[debug] [GetPOT] Fetching gvs PO Token for tv client
```
If it can't find the script or something, you'll see something like this:
```shell
[debug] [GetPOT] Fetching player PO Token for tv client
WARNING: [GetPOT] BgUtilScript: Script path doesn't exist: /Users/you/bgutil-ytdlp-pot-provider/server/build/generate_once.js. Please make sure the script has been transpiled correctly.
WARNING: [GetPOT] BgUtilHTTP: Error reaching GET http://127.0.0.1:4416/ping (caused by TransportError). Please make sure that the server is reachable at http://127.0.0.1:4416.
[debug] [GetPOT] No player PO Token provider available for tv client
```
In this case check that the script has been transpiled correctly and is available at the path specified in the log,
or that the server is running and reachable.

View File

@@ -0,0 +1,190 @@
# Using Google Sheets
This guide explains how to set up Google Sheets to process URLs automatically and then store the archiving status back into the Google sheet. It is broadly split into 3 steps:
1. Setting up your Google Sheet
2. Setting up a service account so Auto Archiver can access the sheet
3. Setting the Auto Archiver settings
## 1. Setting up a Google Service Account
Once your Google Sheet is set up, you need to create what's called a 'service account' that will allow the Auto Archiver to access it.
To do this, you can either:
* a) follow the steps in [this guide](https://gspread.readthedocs.io/en/latest/oauth2.html) all the way up until step 8. You should have downloaded a file called `service_account.json` and should save it in the `secrets/` folder
* b) run the following script to automatically generate the file:
```{code} bash
https://raw.githubusercontent.com/bellingcat/auto-archiver/refs/heads/main/scripts/generate_google_services.sh | bash -s --
```
This uses gcloud to create a new project, a new user and downloads the service account automatically for you. The service account file will have the name `service_account-XXXXXXX.json` where XXXXXXX is a random 16 letter/digit string for the project created.
```{note}
To save the generated file to a different folder, pass an argument as follows:
```{code} bash
https://raw.githubusercontent.com/bellingcat/auto-archiver/refs/heads/main/scripts/generate_google_services.sh | bash -s -- /path/to/secrets
```
----------
Once you've downloaded the file, you can save it to `secrets/service_account.json` (the default name), or to another file and then change the location in the settings (see step 4).
Also make sure to **note down** the email address for this service account. You'll need that for step 3.
```{note}
The email address created in this step can be found either by opening the `service_account.json` file, or if you used b) the `generate_google_services.sh` script, then the script will have printed it out for you.
The email address will look something like `user@project-name.iam.gserviceaccount.com`
```
## 2. Setting up your Google Sheet
We recommend copying [this template Google Sheet](https://docs.google.com/spreadsheets/d/1NJZo_XZUBKTI1Ghlgi4nTPVvCfb0HXAs6j5tNGas72k/edit?usp=sharing) as a starting point for your project, as this matches all the columns required.
But if you like, you can also create your own custom sheet. The only columns required are 'link', 'archive status', and 'archive location'. 'link' is the column with the URLs that you want the Auto Archiver to archive, the other two record the archival status and result.
Here's an overview of all the columns, and what a complete sheet would look like.
**Inputs:**
These are processed by the Gsheet Feeder and passed to the Auto Archiver.
* **Link** *(required)*: the URL of the post that is to be archived
* **Destination folder**: custom folder for archived file (regardless of storage)
**Outputs:**
These are updated by the Gsheet DB module during the archiving process.
Note the required columns are only required if you are using the Gsheet DB module as well as the feeder.
* **Archive status** *(required)*: Status of archive operation
* **Archive location**: URL of archived post
* **Archive date**: Date archived
* **Thumbnail**: Embeds a thumbnail for the post in the spreadsheet
* **Timestamp**: Timestamp of original post
* **Title**: Post title
* **Text**: Post text
* **Screenshot**: Link to screenshot of post
* **Hash**: Hash of archived HTML file (which contains hashes of post media) - for checksums/verification
* **Perceptual Hash**: Perceptual hashes of found images - these can be used for de-duplication of content
* **WACZ**: Link to a WACZ web archive of post
* **ReplayWebpage**: Link to a ReplayWebpage viewer of the WACZ archive
For example, this is a spreadsheet configured with all of the columns for the auto archiver and a few URLs to archive.
In this example the Ghseet Feeder and Gsheet DB are being used, and the archive is in progress.
(Note that the column names are not case sensitive.)
![A screenshot of a Google Spreadsheet with column headers defined as above, and several Youtube and Twitter URLs in the "Link" column](../../demo-before.png)
We'll change the name of the 'Destination Folder' column in the Step 4a.
## 3. Share your Google Sheet with your Service Account email address
Remember that email address you copied in Step 1? Now that you've set up your Google sheet, click 'Share' in the top
right hand corner and enter the email address. Make sure to give the account **Editor** access. Here's how that looks:
![Share sheet](share_sheet.png)
## 4. Setting up the configuration file
The final step is to set your configuration. First, make sure you have `gsheet_feeder_db` set in the `steps.feeders` section of your config. If you wish to store the results of the archiving process back in your Google sheet, make sure to also put `gsheet_feeder_db` setting in the `steps.databases` section. Here's how this might look:
```{code} yaml
steps:
feeders:
- gsheet_feeder_db
...
databases:
- gsheet_feeder_db # optional, if you also want to store the results in the Google sheet and tract the status of active archivals.
...
```
Next, set up the `gsheet_feeder_db` configuration settings in the 'Configurations' part of the config `orchestration.yaml` file. Open up the file, and set the `gsheet_feeder_db.sheet` setting or the `gsheet_feeder_db.sheet_id` setting. The `sheet` should be the name of your sheet, as it shows in the top left of the sheet.
For example, the sheet [here](https://docs.google.com/spreadsheets/d/1NJZo_XZUBKTI1Ghlgi4nTPVvCfb0HXAs6j5tNGas72k/edit?gid=0#gid=0) is called 'Public Auto Archiver template'.
If you saved your `service_account.json` file to anywhere other than the default location (`secrets/service_account.json`), then also make sure to change that now:
Here's how this might look:
```{code} yaml
...
gsheet_feeder_db:
sheet: 'My Awesome Sheet'
service_account: secrets/service_account-XXXXX.json # or leave as secrets/service_account.json
...
```
You can also pass these settings directly on the command line without having to edit the file, here'a an example of how to do that (using docker):
`docker run -it --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver:dockerize --gsheet_feeder_db.sheet "My Awesome Sheet 2"`.
Here, the sheet name has been overridden/specified in the command line invocation.
### 4a. (Optional) Changing the column names
In step 1, we said we would change the name of the 'Destination Folder'. Perhaps you don't like this name, or already have a sheet with a different name. In our example here, we want to name this column 'Save Folder'. To do this, we need to edit the `ghseet_feeder_db.column` setting in the configuration file.
For more information on this setting, see the [Gsheet Feeder Database docs](../modules/autogen/feeder/gsheet_feeder_db.md#configuration-options). We will first copy the default settings from the Gsheet Feeder docs for the 'column' settings, and then edit the 'Destination Folder' section to rename it 'Save Folder'. Our final configuration section looks like:
```{code} yaml
...
gsheet_feeder_db:
sheet: 'My Awesome Sheet'
header: 1
service_account: secrets/service_account.json
columns:
url: link
status: archive status
folder: save folder # <-- note how this value has been changed
archive: archive location
date: archive date
thumbnail: thumbnail
timestamp: upload timestamp
title: upload title
text: text content
screenshot: screenshot
hash: hash
pdq_hash: perceptual hashes
wacz: wacz
replaywebpage: replaywebpage
```
## 4. Running the Auto Archiver
### Feeding the URLs to the Auto Archiver
The URLs to be archived should be added to the Google Sheet, and optionally a folder value. Leave all the other configured columns empty (but you may add additional columns for your own use, as long as they don't conflict with the column names mapped in the configuration file).
The Auto Archiver will archive any URLs which have an empty 'status' column
### Viewing the Results after archiving
With the `ghseet_feeder_db` installed, once you start running the Auto Archiver, it will update the "Archive status" column.
The status will be set to "Archive in progress" once the archival starts. If the archival is stopped during a run, either manually or because an error is raised the status value should be cleared.
![A screenshot of a Google Spreadsheet with column headers defined as above, and several Youtube and Twitter URLs in the "Link" column. The auto archiver has added "archive in progress" to one of the status columns.](../../demo-progress.png)
The links are downloaded and archived, and the spreadsheet is updated to the following:
![A screenshot of a Google Spreadsheet with videos archived and metadata added per the description of the columns above.](../../demo-after.png)
Note that the first row is skipped, as it is assumed to be a header row (`--gsheet_feeder_db.header=1` and you can change it if you use more rows above). Rows with an empty URL column, or a non-empty archive column are also skipped. All sheets in the document will be checked.
The "archive location" link contains the path of the archived file, in local storage, S3, or in Google Drive.
![The archive result for a link in the demo sheet.](../../demo-archive.png)
### Troubleshooting
**Hanging Archival in progress status**
Occasionally system crashes or other unexpected events can cause the Auto Archiver to exit without cleaning up the status value.
If you are sure that all archival processes have stopped but you still see "Archive in progress" in the status column, you can manually clear the status column to allow the Auto Archiver to retry that archival on the next run.
**Nothing archived status**
Sometimes this means the tool is genuinely unable to extract the content at this point in time, but sometimes it can be resolved with different configurations.
Try:
- Turning on additional 'extractor' types in the configuration file (this can appear as 'no archiver' in the status column).
- Changing credentials or refreshing session files for extractors which require them
- Check if the extractors can accept any additional configurations such as adding a cookie file.

View File

@@ -0,0 +1,104 @@
# Keeping Logs
Auto Archiver's logs can be helpful for debugging problematic archiving processes. This guide shows you how to use the logs configuration.
## Setting up logging
Logging settings can be set on the command line or using the orchestration config file ([learn more](../installation/configuration)). A special `logging` section defines the logging options.
#### Enabling or Disabling Logging
Logging to the console is enabled by default. If you want to globally disable Auto Archiver's logging, then you can set `enabled: false` in your `logging` config file:
```{code} yaml
:caption: orchestration.yaml
...
logging:
enabled: false
...
```
```{note}
This will disable all logs from Auto Archiver, but it does not disable logs for other tools that the Auto Archiver uses (for example: yt-dlp, firefox or ffmpeg). These logs will still appear in your console.
```
#### Logging Level
There are 7 logging levels in total, with 5 of them used in this tool. They are: `DEBUG`, `INFO`, `SUCCESS`, `WARNING` and `ERROR`. If you select a level, only that and higher (more serious) levels will be included. `DEBUG` is the most verbose, while `ERROR` is the least verbose.
Change the warning level by setting the value in your orchestration config file:
```{code} yaml
:caption: orchestration.yaml
...
logging:
level: DEBUG # or INFO / WARNING / ERROR
...
```
For normal usage, it is recommended to use the `INFO` level, or if you prefer quieter logs with less information, you can use the `WARNING` level. If you encounter issues with the archiving, then it's recommended to enable the `DEBUG` level.
```{note} To learn about all logging levels, see the [loguru documentation](https://loguru.readthedocs.io/en/stable/api/logger.html)
```
### Logging Format
By default, the console logs are formatted in a human-readable way and the file logs are formatted in JSON. This is new from version 1.1.1. If you want to change the format of the console logs to JSON too you can set the `format:` option in your logging settings.
```{code} yaml
:caption: orchestration.yaml
logging:
format: json
```
When the Auto Archiver is writing logs it will include context about specific tasks, so if you are archiving a URL from a Google Sheet, both the URL (and a unique `trace_id` for that URL's archiving attempt) and the Spreadsheet name and row will be included in the logs. This is useful for debugging and understanding what the Auto Archiver is doing.
Using JSON allows you to easily parse the logs and extract specific information, tools like [`jq`](https://jqlang.org/) can be used to filter and search through the logs.
### Logging to a file
As default, auto-archiver will log to the console. But if you wish to store your logs for future reference, or you are running the auto-archiver from within code a implementation, then you may wish to enable file logging. This can be done by setting the `file:` config value in the logging settings.
**Rotation:** For file logging, you can choose to 'rotate' your log files (creating new log files) so they do not get too large. Change this by setting the 'rotation' option in your logging settings. For a full list of rotation options, see the [loguru docs](https://loguru.readthedocs.io/en/stable/overview.html#easier-file-logging-with-rotation-retention-compression).
```{code} yaml
:caption: orchestration.yaml
logging:
...
file: /my/log/file.log
rotation: 1 day
```
### Logging each level to a different file
If you want to log each level to a different file, you can do this by setting the `each_level_in_separate_file:` option to `true` and also setting your `file:` name, a new file will be created for each of the 5 levels used, by appending the `0_level` name to the file like so `your_file.log.1_error`. In this case the `level:` option is ignored, and all levels will be logged.
```{code} yaml
:caption: orchestration.yaml
logging:
each_level_in_separate_file: true
file: /my/logs/file.log
```
This will create the following files:
- `/my/logs/file.log.1_debug`
- `/my/logs/file.log.2_info`
- `/my/logs/file.log.3_success`
- `/my/logs/file.log.4_warning`
- `/my/logs/file.log.5_error`
### Full logging example
The below example logs only `DEBUG` logs to the console and to the file `/my/file.log`, rotating that file once per week:
```{code} yaml
:caption: orchestration.yaml
logging:
level: DEBUG
format: json
file: /my/file.log
rotation: 1 week
```

View File

@@ -0,0 +1,169 @@
# InstagrAPI Server
The instagram API Extractor requires access to a running instance of the InstagrAPI server.
We have a lightweight script with the endpoints required for our Instagram API Extractor module which you can run locally, or via Docker.
⚠️ Warning: Remember that it's best not to use your own personal account for archiving. [Here's why](../installation/authentication.md#recommendations-for-authentication).
## Quick Start: Using Docker
We've provided a convenient shell script (`run_instagrapi_server.sh`) that simplifies the process of setting up and running the Instagrapi server in Docker. This script handles building the Docker image, setting up credentials, and starting the container.
### 🔧 Running the script:
Run this script either from the repository root or from within the `scripts/instagrapi_server` directory:
```bash
./scripts/instagrapi_server/run_instagrapi_server.sh
```
This script will:
- Prompt for your Instagram username and password.
- Create the necessary `.env` file.
- Build the Docker image.
- Start the Docker container and authenticate with Instagram, creating a session automatically.
### ⏱ To run the server again later:
```bash
docker start ig-instasrv
```
### 🐛 Debugging:
View logs:
```bash
docker logs ig-instasrv
```
### Overview: How the Setup Works
1. You enter your Instagram credentials in a local `.env` file
2. You run the server **once locally** to generate a session file
3. After that, you can choose to run the server again locally or inside Docker without needing to log in again
---
## Optional: Manual / Local Setup
If you'd prefer to run the server manually (without Docker), you can follow these steps:
1. **Navigate to the server folder (and stay there for the rest of this guide)**:
```bash
cd scripts/instagrapi_server
```
2. **Create a `secrets/` folder** (if it doesn't already exist in `scripts/instagrapi_server`):
```bash
mkdir -p secrets
```
3. **Create a `.env` file** inside `secrets/` with your Instagram credentials:
```dotenv
INSTAGRAM_USERNAME="your_username"
INSTAGRAM_PASSWORD="your_password"
```
4. **Install dependencies** using the pyproject.toml file:
```bash
poetry install --no-root
```
5. **Run the server locally**:
```bash
poetry run uvicorn src.instaserver:app --port 8000
```
6. **Watch for the message**:
```
Login successful, session saved.
```
✅ Your session is now saved to `secrets/instagrapi_session.json`.
### To run it again locally:
```bash
poetry run uvicorn src.instaserver:app --port 8000
```
---
## Adding the API Endpoint to Auto Archiver
The server should now be running within that session, and accessible at http://127.0.0.1:8000
You can set this in the Auto Archiver orchestration.yaml file like this:
```yaml
instagram_api_extractor:
api_endpoint: http://127.0.0.1:8000
```
---
## 2. Running the Server Again
Once the session file is created, you should be able to run the server without logging in again.
### To run it locally (from scripts/instagrapi_server):
```bash
poetry run uvicorn src.instgrapinstance.instaserver:app --port 8000
```
---
## 3. Running via Docker (After Setup is Complete, either locally or via the script)
Once the `instagrapi_session.json` and `.env` files are set up, you can pass them Docker and it should authenticate successfully.
### 🔨 Build the Docker image manually:
```bash
docker build -t instagrapi-server .
```
### ▶️ Run the container:
```bash
docker run -d \
--env-file secrets/.env \
-v "$(pwd)/secrets:/app/secrets" \
-p 8000:8000 \
--name ig-instasrv \
instagrapi-server
```
This passes the /secrets/ directory to docker as well as the environment variables from the `.env` file.
---
## 4. Optional Cleanup
- **Stop the Docker container**:
```bash
docker stop ig-instasrv
```
- **Remove the container**:
```bash
docker rm ig-instasrv
```
- **Remove the Docker image**:
```bash
docker rmi instagrapi-server
```
### ⏱ To run again later:
```bash
docker start ig-instasrv
```
---
## Notes
- Never share your `.env` or `instagrapi_session.json` — these contain sensitive login data.
- If you want to reset your session, simply delete the `secrets/instagrapi_session.json` file and re-run the local server.

View File

@@ -0,0 +1,54 @@
# Upgrading from v1.0.1
```{note} This how-to is only relevant for people who used Auto Archiver before June 2025 (versions prior to 1.1.0).
If you are new to Auto Archiver, then you are already using the latest configuration format and this how-to is not relevant for you.
```
Versions 1.1.0+ of Auto Archiver has breaking changes in the configuration format, which means earlier configuration formats will not work without slight modifications.
## Dropping `vk_extractor` module
We have dropped the `vk_extractor` because of problems in a project we relied on. You will need to remove it from your configuration file, otherwise you will see an error like:
```{code} console
Module 'vk_extractor' not found. Are you sure it's installed/exists?
```
## Dropping `screenshot_enricher` module
We have dropped the `screenshot_enricher` module because a new `antibot_extractor_enricher` (see below) module replaces its functionality more robustly and with less dependency hassle on geckodriver/firefox. You will need to remove it from your configuration file, otherwise you will see an error like:
```{code} console
Module 'screenshot_enricher' not found. Are you sure it's installed/exists?
```
## New `antibot_extractor_enricher` module and VkDropin
We have added a new [`antibot_extractor_enricher`](../modules/autogen/extractor/antibot_extractor_enricher.md) module that uses a computer-controlled browser to extract content from websites that use anti-bot measures. You can add it to your configuration file like this:
```{code} yaml
steps:
extractors:
- antibot_extractor_enricher
# or alternatively, if you want to use it as an enricher:
enrichers:
- antibot_extractor_enricher
```
It will take a full page screenshot, a PDF capture, extract HTML source code, and any other relevant media.
It comes with Dropins that we will be adding and maintaining.
> Dropin: A module with site-specific behaviours that is loaded automatically. You don't need to add them to your configuration steps for them to run. Sometimes they need `authentication` configurations though.
One such Dropin is the VkDropin which uses this automated browser to access VKontakte (VK) pages. You should add a username/password to the configuration file if you get authentication blocks from VK, to do so use the [authentication settings](authentication_how_to.md):
```{code} yaml
authentication:
vk.com:
username: your_username
password: your_password
```
See all available Dropins in [the source code](https://github.com/bellingcat/auto-archiver/tree/main/src/auto_archiver/modules/antibot_extractor_enricher/dropins). Usually each Dropin needs its own authentication settings, similarly to the VkDropin.

View File

@@ -0,0 +1,145 @@
# Upgrading from v0.12
```{note} This how-to is only relevant for people who used Auto Archiver before February 2025 (versions prior to 0.13).
If you are new to Auto Archiver, then you are already using the latest configuration format and this how-to is not relevant for you.
```
Versions 0.13+ of Auto Archiver has breaking changes in the configuration format, which means earlier configuration formats will not work without slight modifications.
## How do I know if I need to update my configuration format?
There are two simple ways to check if you need to update your format:
1. When you try and run auto-archiver using your existing configuration file, you get an error about no feeders or formatters being configured, like:
```{code} console
AssertionError: No feeders were configured. Make sure to set at least one feeder in
your configuration file or on the command line (using --feeders)
```
2. Within your configuration file, you have a `feeder:` option. This is the old format. An example old format:
```{code} yaml
steps:
feeder: cli_feeder
...
```
The next two sections outline the two methods you have for updating your file.
## 1. Manually edit the configuration file and change the values.
This is recommended if you want to keep all your old settings. Follow the steps below to change the relevant settings:
#### a) Feeder & Formatter Steps Settings
The feeder and formatter settings have been changed from a single string to a list.
- `steps.feeder (string)` → `steps.feeders (list)`
- `steps.formatter (string)` → `steps.formatters (list)`
Example:
```{code} yaml
steps:
feeder: cli_feeder
...
formatter: html_formatter
# the above should be changed to:
steps:
feeders:
- cli_feeder
...
formatters:
- html_formatter
```
```{note} Auto Archiver still only supports one feeder and formatter, but from v0.13 onwards they must be added to the configuration file as a list.
```
#### b) Extractor (formerly Archiver) Steps Settings
With v0.13 of Auto Archiver, `archivers` have been renamed to `extractors` to better reflect what they actually do - extract information from a URL. Change the configuration by renaming:
- `steps.archivers` → `steps.extractors`
The names of the actual modules have also changed, so for any extractor modules you have enabled, you will need to rename the `archiver` part to `extractor`. Some examples:
- `telethon_archiver` → `telethon_extractor`
- `wacz_archiver_enricher` → `wacz_extractor_enricher`
- `wayback_archiver_enricher` → `wayback_extractor_enricher`
#### c) Module Renaming
The `youtube_archiver` has been renamed to `generic_extractor` as it is considered the default/fallback extractor. Read more about the [generic extractor](../modules/autogen/extractor/generic_extractor.md).
The `atlos` modules have been merged into one, as have the `gsheets` feeder and database.
- `atlos_feeder` → `atlos_feeder_db_storage`
- `atlos_storage` → `atlos_feeder_db_storage`
- `atlos_db` → `atlos_feeder_db_storage`
- `gsheet_feeder` → `gsheet_feeder_db`
- `gsheet_db` → `gsheet_feeder_db`
Example:
```{code} yaml
steps:
feeders:
- gsheet_feeder_db # formerly gsheet_feeder
...
extractors: # formerly 'archivers'
- telethon_extractor # formerly telethon_archiver
- generic_extractor # formerly youtube_archiver
- vk_extractor # formerly vk_archiver
databases:
- gsheet_feeder_db # formerly gsheet_db
...
```
```{note}
Don't forget to also rename the configuration settings. For example:
```{code} yaml
gsheet_feeder_db: # formerly gsheet_feeder
service_account: secrets/service_account.json
sheet: My Google Sheet
...
```
#### d) Redundant / Obsolete Modules
With v0.13 of Auto Archiver, the following modules have been removed and their features have been built in to the generic_extractor. You should remove them from the 'steps' section of your configuration file:
* `twitter_archiver` - use the `generic_extractor` for general extraction, or the `twitter_api_extractor` for API access.
* `tiktok_archiver` - use the `generic_extractor` to extract TikTok videos.
## 2. Auto-generate a new config, then copy over your settings.
Using this method, you can have Auto Archiver auto-generate a configuration file for you, then you can copy over the desired settings from your old config file. This is probably the easiest method and quickest to setup, but it may require some trial and error as you copy over your settings.
First, move your existing `orchestration.yaml` file to a different folder or rename it.
Then, you can generate a `simple` or `full` config using:
```{code} console
>>> # generate a simple config
>>> auto-archiver
>>> # config will be written to orchestration.yaml
>>>
>>> # generate a full config
>>> auto-archiver --mode=full
>>>
```
After this, copy over any settings from your old config to the new config.

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

17
docs/source/index.md Normal file
View File

@@ -0,0 +1,17 @@
```{include} ../../README.md
```
```{toctree}
:maxdepth: 2
:hidden:
:caption: Contents:
Overview <self>
installation/setup
core_modules.md
how_to
contributing
development/developer_guidelines
autoapi/index.rst
```

View File

@@ -0,0 +1,82 @@
# Authentication
The Authentication framework for auto-archiver allows you to add login details for various websites in a flexible way, directly from the configuration file.
There are two main use cases for authentication:
* Some websites require some kind of authentication in order to view the content. Examples include Facebook, Telegram etc.
* Some websites use anti-bot systems to block bot-like tools from accessing the website. Adding real login information to auto-archiver can sometimes bypass this.
```{note}
The Authentication framework currently only works with the following modules:
* [Generic Extractor](../modules/autogen/extractor/generic_extractor.md) - the main module for extracting content from websites
* [Antibot Extractor/Enricher](../modules/autogen/extractor/antibot_extractor_enricher.md)
To authenticate for WACZ archiving, see the instructions on the [](../modules/autogen/enricher/wacz_extractor_enricher.md) page.
```
## The Authentication Config
You can save your authentication information directly inside your orchestration config file, or as a separate file (for security/multi-deploy purposes). Whether storing your settings inside the orchestration file, or as a separate file, the configuration format is the same. Currently, auto-archiver supports the following authentication types:
**Username & Password:**
- `username`: str - the username to use for login
- `password`: str - the password to use for login
**API**
- `api_key`: str - the API key to use for login
- `api_secret`: str - the API secret to use for login
**Cookies**
- `cookie`: str - a cookie string to use for login (specific to this site)
- `cookies_from_browser`: str - load cookies from this browser, for this site only.
- `cookies_file`: str - load cookies from this file, for this site only.
```{note}
Currently, the Username & Password, and API settings only work with the Generic and Antibot Extractors. Furthermore, many sites can still detect bots and block username/password logins. Twitter/X and YouTube are two prominent ones that block username/password logins.
One of the 'Cookies' options is recommended for the most robust archiving, but it still isn't guaranteed to work.
```
```{code} yaml
authentication:
# optional file to load authentication information from, for security or multi-system deploy purposes
load_from_file: path/to/authentication/file.txt
# optional setting to load cookies from the named browser on the system, for **ALL** websites
cookies_from_browser: firefox
# optional setting to load cookies from a cookies.txt/cookies.jar file, for **ALL** websites. See note below on extracting these
cookies_file: path/to/cookies.jar
mysite.com:
username: myusername
password: 123
facebook.com:
cookie: single_cookie
othersite.com:
api_key: 123
api_secret: 1234
```
### Recommendations for authentication
1. **Store authentication information separately:**
The authentication part of your configuration contains sensitive information. You should make efforts not to share this with others. For extra security, use the `load_from_file` option to keep your authentication settings out of your configuration file, ideally in a different folder.
2. **Don't use your own personal credentials**
Depending on the website you are extracting information from, there may be rules (Terms of Service) that prohibit you from scraping or extracting information using a bot. If you use your own personal account, there's a possibility it might get blocked/disabled. It's recommended to set up a separate, 'throwaway' account. In that way, if it gets blocked you can easily create another one to continue your archiving.
### How to create a cookies.jar or pass cookies directly to auto-archiver
auto-archiver uses yt-dlp's powerful cookies features under the hood. For instructions on how to extract a cookies.jar (or cookies.txt) file directly from your browser, see the FAQ in the [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp)
```{note} For developers:
For information on how to access and use authentication settings from within your module, see the `{generic_extractor}` for an example, or view the [`auth_for_site()` function in BaseModule](../autoapi/core/base_module/index.rst)
```

View File

@@ -0,0 +1,6 @@
# Configuration Cheat Sheet
Below is a list of all configurations for the core modules in Auto Archiver
```{include} ../modules/autogen/configs_cheatsheet.md
```

View File

@@ -0,0 +1,5 @@
# Configuration Editor
```{raw} html
:file: settings.html
```

View File

@@ -0,0 +1,103 @@
# Configuration
The recommended way to configure auto-archiver for first-time users is to [run the Auto Archiver](setup.md#running) and have it auto-generate a default configuration for you. Then, if needed, you can edit the configuration file using one of the following methods.
## 1. Configuration file
The configuration file is typically called `orchestration.yaml` and stored in the `secrets` folder on your desktop. The configuration file contains all the settings for your entire Auto Archiver workflow in one easy-to-find place.
If you want to have Auto Archiver run with the recommended 'basic' setup,
### Advanced Configuration
The structure of orchestration file is split into 2 parts: `steps` (what [steps](../flow_overview.md) to use) and `configurations` (settings for individual modules).
A default `orchestration.yaml` will be created for you the first time you run auto-archiver (without any arguments). Here's what it looks like:
<details>
<summary>View exampleorchestration.yaml</summary>
```{literalinclude} ../example.orchestration.yaml
:language: yaml
:caption: orchestration.yaml
```
</details>
## 2. Command Line configuration
You can run auto-archiver directly from the command line, without the need for a configuration file, command line arguments are parsed using the format `module_name.config_value`. For example, a config value of `api_key` in the `instagram_extractor` module would be passed on the command line with the flag `--instagram_extractor.api_key=API_KEY`.
The command line arguments are useful for testing or editing config values and enabling/disabling modules on the fly. When you are happy with your settings, you can store them back in your configuration file by passing the `-s/--store` flag on the command line.
```bash
auto-archiver --instagram_extractor.api_key=123 --other_module.setting --store
# will store the new settings into the configuration file (default: orchestration.yaml)
```
```{note} Arguments passed on the command line override those saved in your settings file. Save them to your config file using the -s or --store flag
```
## Seeing all Configuration Options
View the configurable settings for the core modules on the individual doc pages for each [](../core_modules.md).
You can also view all settings available for the modules you have on your system using the `--help` flag in auto-archiver.
```{code-block} console
:caption: Example output when using the --help flag with auto-archiver
$ auto-archiver --help
...
Positional Arguments:
urls URL(s) to archive, either a single URL or a list of urls, should not come from config.yaml
Options:
--help, -h show a full help message and exit
--version show program's version number and exit
--config CONFIG_FILE the filename of the YAML configuration file (defaults to 'config.yaml')
--mode {simple,full} the mode to run the archiver in
-s, --store, --no-store
Store the created config in the config file
--module_paths MODULE_PATHS [MODULE_PATHS ...]
additional paths to search for modules
--feeders STEPS.FEEDERS [STEPS.FEEDERS ...]
the feeders to use
--enrichers STEPS.ENRICHERS [STEPS.ENRICHERS ...]
the enrichers to use
--extractors STEPS.EXTRACTORS [STEPS.EXTRACTORS ...]
the extractors to use
--databases STEPS.DATABASES [STEPS.DATABASES ...]
the databases to use
--storages STEPS.STORAGES [STEPS.STORAGES ...]
the storages to use
--formatters STEPS.FORMATTERS [STEPS.FORMATTERS ...]
the formatter to use
--authentication AUTHENTICATION
A dictionary of sites and their authentication methods (token, username etc.) that extractors can use to log into a website. If passing this on the command line, use a JSON string. You may
also pass a path to a valid JSON/YAML file which will be parsed.
--logging.level {INFO,DEBUG,ERROR,WARNING}
the logging level to use
--logging.file LOGGING.FILE
the logging file to write to
--logging.rotation LOGGING.ROTATION
the logging rotation to use
Wayback Machine Enricher:
Submits the current URL to the Wayback Machine for archiving and returns either a job ID or the...
--wayback_extractor_enricher.timeout TIMEOUT
seconds to wait for successful archive confirmation from wayback, if more than this passes the result contains the job_id so the status can later be checked manually.
--wayback_extractor_enricher.if_not_archived_within IF_NOT_ARCHIVED_WITHIN
only tell wayback to archive if no archive is available before the number of seconds specified, use None to ignore this option. For more information:
https://docs.google.com/document/d/1Nsv52MvSjbLb2PCpHlat0gkzw0EvtSgpKHu4mk0MnrA
--wayback_extractor_enricher.key KEY
wayback API key. to get credentials visit https://archive.org/account/s3.php
--wayback_extractor_enricher.secret SECRET
wayback API secret. to get credentials visit https://archive.org/account/s3.php
--wayback_extractor_enricher.proxy_http PROXY_HTTP
http proxy to use for wayback requests, eg http://proxy-user:password@proxy-ip:port
--wayback_extractor_enricher.proxy_https PROXY_HTTPS
https proxy to use for wayback requests, eg https://proxy-user:password@proxy-ip:port
```

View File

@@ -0,0 +1,160 @@
### Bash script for Ubuntu 24 Server install
> NOTE: this script has not been tested by the maintainers and results from the personal experience of a user. It is meant as a guide and not an out of the box script, as you will see it's aimed at a custom branches, users, and features like the Geckodriver which are removed as of version 1.0.2.
This acts as a handy guide on all requirements. This is built and tested on the 29th of May 2025 on Ubuntu Server 24.04.2 LTS (which is the current latest LTS)
```bash
#!/bin/sh
# I usually run steps manually as logged in with the user: dave
# which the application runs under which makes debugging easier
cd ~
sudo apt update -y
sudo apt upgrade -y
# Clone only my latest branch
git clone -b v1-test --single-branch https://github.com/djhmateer/auto-archiver
mkdir ~/auto-archiver/secrets
sudo chown -R dave ~/auto-archiver
sudo apt update -y
sudo apt upgrade -y
## Python 3.12.3 comes with Ubuntu 24.04.2
# Poetry install 2.1.3 on 2nd June 25
curl -sSL https://install.python-poetry.org | python3 -
# had to restart here..
sudo reboot
# C++ compiler so pdqhash will install next
sudo apt install build-essential python3-dev -y
cd auto-archiver
poetry install
# FFMpeg
# 6.1.1-3ubuntu5 on 2nd June 25
sudo apt install ffmpeg -y
## Firefox
# 139.0+build2-0ubuntu0.24.04.1~mt1 on 2nd Jun 25
# 16th Jun - don't need anymore as using Chrome in antibot
# cd ~
# sudo add-apt-repository ppa:mozillateam/ppa -y
# echo '
# Package: *
# Pin: release o=LP-PPA-mozillateam
# Pin-Priority: 1001
# ' | sudo tee /etc/apt/preferences.d/mozilla-firefox
# echo 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' | sudo tee /etc/apt/apt.conf.d/51unattended-upgrades-firefox
# sudo apt install firefox -y
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
# Chrome
cd ~
# got problems here - fixed below
# 137.0.7151.103 on 16th Jun 2025
sudo dpkg -i google-chrome-stable_current_amd64.deb
# fix dependencies on install above
sudo apt-get install -f
# had to click a lot on UI to get going.
# to test
# google-chrome
## Gecko driver
# check version numbers for new ones
# https://github.com/mozilla/geckodriver/releases/
wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-linux64.tar.gz
tar -xvzf geckodriver*
chmod +x geckodriver
sudo mv geckodriver /usr/local/bin/
rm geckodriver*
# Fonts so selenium via firefox can render other languages eg Burmese
sudo apt install fonts-noto -y
# Docker
# Add Docker's official GPG key:
sudo apt-get update -y
sudo apt-get install ca-certificates curl -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -y
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
# add dave user to docker group
sudo usermod -aG docker $USER
# reboot otherwise can't pull images
# https://github.com/webrecorder/browsertrix-crawler
# https://hub.docker.com/r/webrecorder/browsertrix-crawler/tags
# 1.6.2 on 4th Jun 2025
docker pull webrecorder/browsertrix-crawler:latest
# exif
sudo apt install libimage-exiftool-perl -y
## CRON run every minute
# the cron job running as user dave will execute the shell script
# I have many scripts running from cron_11 upwards.
# patch in the correct number
sudo chmod +x ~/auto-archiver/scripts/cron_15.sh
# don't want service to run until a reboot otherwise problems with Gecko driver
sudo service cron stop
# runs the script every minute
# notice put in a # to disable so will have to manually start it.
cat <<EOT >> run-auto-archive
#*/1 * * * * dave /home/dave/auto-archiver/scripts/cron_15.sh
EOT
sudo mv run-auto-archive /etc/cron.d
sudo chown root /etc/cron.d/run-auto-archive
sudo chmod 600 /etc/cron.d/run-auto-archive
# Helper alias 'c' to open the above file
echo "alias c='sudo vim /etc/cron.d/run-auto-archive'" >> ~/.bashrc
# secrets folder copy
# I run dev from:
# \\wsl.localhost\Ubuntu-24.04\home\dave\code\auto-archiver\secrets\
# orchestration.yaml - for aa config
# service_account - for google spreadsheet
# anon.session - for telethon so don't have to type in phone number
# profile.tar.gz - for wacz to have a logged in profile for facebook, x.com and instagram to get data
# Youtube - POT Tokens
# https://github.com/Brainicism/bgutil-ytdlp-pot-provider
docker run --name bgutil-provider --restart unless-stopped -d -p 4416:4416 brainicism/bgutil-ytdlp-pot-provider
# test run
cd ~/auto-archiver
poetry run python src/auto_archiver --config secrets/orchestration-aa-demo-main.yaml
```

View File

@@ -0,0 +1,59 @@
# Frequently Asked Questions
### Q: What websites does the Auto Archiver support?
**A:** The Auto Archiver works for a large variety of sites. Firstly, the Auto Archiver can download
and archive any video website supported by YT-DLP, a powerful video-downloading tool ([full list of of
sites here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)). Aside from these sites,
there are various different 'Extractors' for specific websites. See the full list of extractors that
are available on the [extractors](../modules/extractor.md) page. Some sites supported include:
* Twitter
* Instagram
* Telegram
* Tiktok
* Bluesky
```{note} What websites the Auto Archiver can archie depends on what extractors you have enabled in
your configuration. See [configuration](./configurations.md) for more info.
```
### Q: Does the Auto Archiver only work for social media posts ?
**A:** No, the Auto Archiver can archive any web page on the internet, not just social media posts.
However, for social media posts Auto Archiver can extract more relevant/useful information (such as
post comments, likes, author etc.) which may not be available for a generic website. If you are looking
to more generally archive webpages, then you should make sure to enable the [](../modules/autogen/extractor/wacz_extractor_enricher.md)
and the [](../modules/autogen/extractor/wayback_extractor_enricher.md).
### Q: What kind of data is stored for each webpage that's archived?
**A:** This depends on the website archived, but more generally, for social media posts any videos and photos in
the post will be archived. For video sites, the video will be downloaded separately. For most of these sites, additional
metadata such as published date, uploader/author and ratings/comments will also be saved. Additionally, further data can be
saved depending on the enrichers that you have enabled. Some other types of data saved are timestamps if you have the
[](../modules/autogen/enricher/timestamping_enricher.md) or [](../modules/autogen/enricher/opentimestamps_enricher.md) enabled,
screenshots of the web page with the [](../modules/autogen/enricher/screenshot_enricher.md), and for videos, thumbnails of the
video with the [](../modules/autogen/enricher/thumbnail_enricher.md). You can also store things like hashes (SHA256, or pdq hashes)
with the various hash enrichers.
### Q: Where is my data stored?
**A:** With the default configuration, data is stored on your local computer in the `local_storage` folder. You can adjust these settings by
changing the [storage modules](../modules/storage.md) you have enabled. For example, you could choose to store your data in an S3 bucket or
on Google Drive.
```{note}
You can choose to store your data in multiple places, for example your local drive **and** an S3 bucket for redundancy.
```
### Q: What should I do is something doesn't work?
**A:** First, read through the log files to see if you can find a specific reason why something isn't working. Learn more about logging
and how to enable debug logging in the [Logging Howto](../how_to/logging.md).
If you cannot find an answer in the logs, then try searching this documentation or existing / closed issues on the [Github Issue Tracker](https://github.com/bellingcat/auto-archiver/issues?q=is%3Aissue%20). If you still cannot find an answer, then consider opening an issue on the Github Issue Tracker or asking in the Bellingcat Discord
'Auto Archiver' group.
#### Common reasons why an archiving might not work:
* The website may have temporarily adjusted its settings - sometimes sites like Telegram or Twitter adjust their scraping protection settings. Often,
waiting a day or two and then trying again can work.
* The site requires you to be logged in - you could try using cookies or authentication to bypass any blocks. See [](../installation/authentication.md) for more information.
* The website you're trying to archive has changed its settings/structure. Make sure you're using the latest version of Auto Archiver and try again.

View File

@@ -0,0 +1,65 @@
# Installation
```{toctree}
:maxdepth: 1
upgrading.md
```
There are 3 main ways to use the auto-archiver. We recommend the 'docker' method for most uses. This installs all the requirements in one command.
1. Easiest (recommended): [via docker](#installing-with-docker)
2. Local Install: [using pip](#installing-locally-with-pip)
3. Developer Install: [see the developer guidelines](../development/developer_guidelines)
## 1. Installing with Docker
[![dockeri.co](https://dockerico.blankenship.io/image/bellingcat/auto-archiver)](https://hub.docker.com/r/bellingcat/auto-archiver)
Docker works like a virtual machine running inside your computer, making installation simple. You'll need to first set up Docker, and then download the Auto Archiver 'image':
**a) Download and install docker**
Go to the [Docker website](https://docs.docker.com/get-docker/) and download right version for your operating system.
**b) Pull the Auto Archiver docker image**
Open your command line terminal, and copy-paste / type:
```bash
docker pull bellingcat/auto-archiver
```
This will download the docker image, which may take a while.
That's it, all done! You're now ready to set up [your configuration file](configurations.md). Or, if you want to use the recommended defaults, then you can [run Auto Archiver immediately](setup.md#running-a-docker-install).
------------
## 2. Installing Locally with Pip
1. Make sure you have python 3.10 or higher installed
2. Install the package with your preferred package manager: `pip/pipenv/conda install auto-archiver` or `poetry add auto-archiver`
3. Test it's installed with `auto-archiver --help`
4. Install other local dependency requirements (for example `ffmpeg`, `firefox`)
After this, you're ready to set up your [your configuration file](configurations.md), or if you want to use the recommended defaults, then you can [run Auto Archiver immediately](setup.md#running-a-local-install).
### Installing Local Requirements
If using the local installation method, you will also need to install the following dependencies locally:
1.[ffmpeg](https://www.ffmpeg.org/) - for handling of downloaded videos
<!-- 2. [firefox](https://www.mozilla.org/en-US/firefox/new/) and [geckodriver](https://github.com/mozilla/geckodriver/releases) on a path folder like `/usr/local/bin` - for taking webpage screenshots with the screenshot enricher -->
3. (optional) [fonts-noto](https://fonts.google.com/noto) to deal with multiple unicode characters during selenium screenshots: `sudo apt install fonts-noto -y`.
4. [Browsertrix Crawler docker image](https://hub.docker.com/r/webrecorder/browsertrix-crawler) for the WACZ enricher/archiver
### Custom installation scripts
- [Ubuntu 24 Server Install by @djhmateer](example_scripts/ubuntu_24_server_install.md) - a WYSIWYG example script from a user who set up the Auto Archiver on a fresh Ubuntu 24 server.
## Developer Install
[See the developer guidelines](../development/developer_guidelines)

View File

@@ -0,0 +1,14 @@
# Requirements
Using the Auto Archiver is very simple, but ideally you have some familiarity with using the command line to run programs. ([Command line crash course](https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line)).
### System Requirements
* Auto Archiver works on any Windows, macOS and Linux computer
* If you're using the **local install** method, then you should make sure to have python3.10+ installed
### Storage Requirements
By default, Auto Archiver uses your local computer storage for any downloaded media (videos, images etc.). If you're downloading large files, this may take up a lot of your local computer's space (more than 5GB of space).
If your storage space is limited, then you may want to set up an [alternative storage method](../modules/storage.md) for your media.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,80 @@
# Getting Started
```{toctree}
:hidden:
installation.md
configurations.md
config_editor.md
authentication.md
requirements.md
faq.md
config_cheatsheet.md
```
## Getting Started
To get started with Auto Archiver, there are 3 main steps you need to complete.
1. [Install Auto Archiver](installation.md)
2. [Setup up your configuration](configurations.md) (if you are ok with the default settings, you can skip this step)
3. Run the archiving process<a id="running"></a>
The way you run the Auto Archiver depends on how you installed it (docker install or local install)
### Running a Docker Install
If you installed Auto Archiver using docker, open up your terminal, and copy-paste / type the following command:
```bash
docker run -it --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver -- "https://example.com/1/"
```
breaking this command down:
1. `docker run` tells docker to start a new container (an instance of the image)
2. `-it` tells docker to run in 'interactive mode' so that we get nice colour logs
3. `--rm` makes sure this container is removed after execution (less garbage locally)
4. `-v $PWD/secrets:/app/secrets` - your secrets folder with settings
1. `-v` is a volume flag which means a folder that you have on your computer will be connected to a folder inside the docker container
2. `$PWD/secrets` points to a `secrets/` folder in your current working directory (where your console points to), we use this folder as a best practice to hold all the secrets/tokens/passwords/... you use
3. `/app/secrets` points to the path the docker container where this image can be found
5. `-v $PWD/local_archive:/app/local_archive` - (optional) if you use local_storage
1. `-v` same as above, this is a volume instruction
2. `$PWD/local_archive` is a folder `local_archive/` in case you want to archive locally and have the files accessible outside docker
3. `/app/local_archive` is a folder inside docker that you can reference in your orchestration.yml file
6. ` -- "https://example.com/1/"` this will pass the URL to archive to the default [command line feeder](../modules/autogen/feeder/cli_feeder.md)
### Example invocations
The invocations below will run the auto-archiver Docker image using a configuration file that you have specified
```bash
# Have auto-archiver run with the default settings, generating a settings file in ./secrets/orchestration.yaml
docker run -it --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver
# uses the same configuration, but with the `gsheet_feeder`, a header on row 2 and with some different column names
# Note this expects you to have followed the [Google Sheets setup](how_to/google_sheets.md) and added your service_account.json to the `secrets/` folder
# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided
docker run -it --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --feeders=gsheet_feeder --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}'
# Runs auto-archiver for the first time, but in 'full' mode, enabling all modules to get a full settings file
docker run -it --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --mode full
```
------------
### Running a Local Install
### Example invocations
Once all your [local requirements](#installing-local-requirements) are correctly installed, the
```bash
# all the configurations come from ./secrets/orchestration.yaml
auto-archiver --config secrets/orchestration.yaml
# uses the same configurations but for another google docs sheet
# with a header on row 2 and with some different column names
# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided
auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}'
# all the configurations come from orchestration.yaml and specifies that s3 files should be private
auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1
```

View File

@@ -0,0 +1,30 @@
# Upgrading
If an update is available, then you will see a message in the logs when you
run Auto Archiver. Here's what those logs look like:
```{code} bash
********* IMPORTANT: UPDATE AVAILABLE ********
A new version of auto-archiver is available (v0.13.6, you have 0.13.4)
Make sure to update to the latest version using: `pip install --upgrade auto-archiver`
```
Upgrading Auto Archiver depends on the way you installed it.
## Docker
To upgrade using docker, update the docker image with:
```
docker pull bellingcat/auto-archiver:latest
```
## Pip
To upgrade the pip package, use:
```
pip install --upgrade auto-archiver
```

View File

@@ -0,0 +1,15 @@
# Database Modules
Database modules are used to store the status and results of the extraction and enrichment processes somewhere. The database modules are responsible for creating and managing entires for each item that has been processed.
The default (enabled) databases are the CSV Database and the Console Database.
```{include} autogen/database.md
```
```{toctree}
:maxdepth: 1
:hidden:
:glob:
autogen/database/*
```

View File

@@ -0,0 +1,14 @@
# Enricher Modules
Enricher modules are used to add additional information to the items that have been extracted. Common enrichment tasks include adding metadata to items, such as the hash of the item, a screenshot of the webpage when the item was extracted, or general metadata like the date and time the item was extracted.
```{include} autogen/enricher.md
```
```{toctree}
:maxdepth: 1
:hidden:
:glob:
autogen/enricher/*
```

View File

@@ -0,0 +1,19 @@
# Extractor Modules
Extractor modules are used to extract the content of a given URL. Typically, one extractor will work for one website or platform (e.g. a Telegram extractor or an Instagram), however, there are several wide-ranging extractors which work for a wide range of websites.
Extractors that are able to extract content from a wide range of websites include:
1. Generic Extractor: parses videos and images on sites using the powerful yt-dlp library.
2. Antibot Extractor: uses a headless browser to bypass bot detection and extract content.
3. WACZ Extractor: runs a web browser to 'browse' the URL and save a copy of the page in WACZ format.
4. Wayback Machine Extractor: sends pages to the Wayback machine for archiving, and stores the archived link.
```{include} autogen/extractor.md
```
```{toctree}
:maxdepth: 1
:hidden:
:glob:
autogen/extractor/*
```

View File

@@ -0,0 +1,20 @@
# Feeder Modules
Feeder modules are used to feed URLs into the Auto Archiver for processing. Feeders can take these URLs from a variety of sources, such as a file, a database, or the command line.
The default feeder is the command line feeder (`cli_feeder`), which allows you to input URLs directly into `auto-archiver` from the command line.
Command line feeder usage:
```{code} bash
auto-archiver [options] -- URL1 URL2 ...
```
```{include} autogen/feeder.md
```
```{toctree}
:maxdepth: 1
:glob:
:hidden:
autogen/feeder/*
```

View File

@@ -0,0 +1,13 @@
# Formatter Modules
Formatter modules are used to format the data extracted from a URL into a specific format. Currently the most widely-used formatter is the HTML formatter, which formats the data into an easily viewable HTML page.
```{include} autogen/formatter.md
```
```{toctree}
:maxdepth: 1
:hidden:
:glob:
autogen/formatter/*
```

View File

@@ -0,0 +1,15 @@
# Storage Modules
Storage modules are used to store the data extracted from a URL in a persistent location. This can be on your local hard disk, or on a remote server (e.g. S3 or Google Drive).
The default is to store the files downloaded (e.g. images, videos) in a local directory.
```{include} autogen/storage.md
```
```{toctree}
:maxdepth: 1
:hidden:
:glob:
autogen/storage/*
```

16
docs/source/overview.md Normal file
View File

@@ -0,0 +1,16 @@
```{include} ../../README.md
```
```{toctree}
:maxdepth: 2
:hidden:
:caption: Contents:
Overview <self>
installation/installation.rst
core_modules.md
how_to
development/developer_guidelines
autoapi/index.rst
```

View File

@@ -1,125 +0,0 @@
steps:
# only 1 feeder allowed
feeder: gsheet_feeder # defaults to cli_feeder
archivers: # order matters, uncomment to activate
# - vk_archiver
# - telethon_archiver
# - telegram_archiver
# - twitter_archiver
# - twitter_api_archiver
# - instagram_tbot_archiver
# - instagram_archiver
# - tiktok_archiver
- youtubedl_archiver
# - wayback_archiver_enricher
# - wacz_archiver_enricher
enrichers:
- hash_enricher
# - metadata_enricher
# - screenshot_enricher
# - thumbnail_enricher
# - wayback_archiver_enricher
# - wacz_archiver_enricher
# - pdq_hash_enricher # if you want to calculate hashes for thumbnails, include this after thumbnail_enricher
formatter: html_formatter # defaults to mute_formatter
storages:
- local_storage
# - s3_storage
# - gdrive_storage
databases:
- console_db
# - csv_db
# - gsheet_db
# - mongo_db
configurations:
gsheet_feeder:
sheet: "your sheet name"
header: 1
service_account: "secrets/service_account.json"
# allow_worksheets: "only parse this worksheet"
# block_worksheets: "blocked sheet 1,blocked sheet 2"
use_sheet_names_in_stored_paths: false
columns:
url: link
status: archive status
folder: destination folder
archive: archive location
date: archive date
thumbnail: thumbnail
timestamp: upload timestamp
title: upload title
text: textual content
screenshot: screenshot
hash: hash
pdq_hash: perceptual hashes
wacz: wacz
replaywebpage: replaywebpage
instagram_tbot_archiver:
api_id: "TELEGRAM_BOT_API_ID"
api_hash: "TELEGRAM_BOT_API_HASH"
# session_file: "secrets/anon"
telethon_archiver:
api_id: "TELEGRAM_BOT_API_ID"
api_hash: "TELEGRAM_BOT_API_HASH"
# session_file: "secrets/anon"
join_channels: false
channel_invites: # if you want to archive from private channels
- invite: https://t.me/+123456789
id: 0000000001
- invite: https://t.me/+123456788
id: 0000000002
twitter_api_archiver:
# either bearer_token only
bearer_token: "TWITTER_BEARER_TOKEN"
# OR all of the below
# consumer_key: ""
# consumer_secret: ""
# access_token: ""
# access_secret: ""
instagram_archiver:
username: "INSTAGRAM_USERNAME"
password: "INSTAGRAM_PASSWORD"
# session_file: "secrets/instaloader.session"
vk_archiver:
username: "or phone number"
password: "vk pass"
session_file: "secrets/vk_config.v2.json"
screenshot_enricher:
width: 1280
height: 2300
wayback_archiver_enricher:
timeout: 10
key: "wayback key"
secret: "wayback secret"
hash_enricher:
algorithm: "SHA3-512" # can also be SHA-256
wacz_archiver_enricher:
profile: secrets/profile.tar.gz
local_storage:
save_to: "./local_archive"
save_absolute: true
filename_generator: static
path_generator: flat
s3_storage:
bucket: your-bucket-name
region: reg1
key: S3_KEY
secret: S3_SECRET
endpoint_url: "https://{region}.digitaloceanspaces.com"
cdn_url: "https://{bucket}.{region}.cdn.digitaloceanspaces.com/{key}"
# if private:true S3 urls will not be readable online
private: false
# with 'random' you can generate a random UUID for the URL instead of a predictable path, useful to still have public but unlisted files, alternative is 'default' or not omitted from config
key_path: random
gdrive_storage:
path_generator: url
filename_generator: random
root_folder_id: folder_id_from_url
oauth_token: secrets/gd-token.json # needs to be generated with scripts/create_update_gdrive_oauth_token.py
service_account: "secrets/service_account.json"
csv_db:
csv_file: "./local_archive/db.csv"

4408
poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,121 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools-pipfile"]
build-backend = "setuptools.build_meta"
[tool.setuptools-pipfile]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
[project]
name = "auto-archiver"
version = "1.2.7"
description = "Automatically archive links to videos, images, and social media content from Google Sheets (and more)."
requires-python = ">=3.10,<3.13"
license = "MIT"
authors = [
{ name = "Bellingcat", email = "tech@bellingcat.com" },
]
readme = "README.md"
keywords = ["archive", "oosi", "osint", "scraping"]
classifiers = [
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3"
]
dependencies = [
"gspread (>=0.0.0)",
"beautifulsoup4 (>=0.0.0)",
"bs4 (>=0.0.0)",
"loguru (>=0.0.0)",
"ffmpeg-python (>=0.0.0)",
"telethon (>=0.0.0)",
"google-api-python-client (>=0.0.0)",
"google-auth-httplib2 (>=0.0.0)",
"google-auth-oauthlib (>=0.0.0)",
"oauth2client (>=0.0.0)",
"pdqhash (>=0.0.0)",
"pillow (>=0.0.0)",
"python-slugify (>=0.0.0)",
"dateparser (>=0.0.0)",
"python-twitter-v2 (>=0.0.0)",
"instaloader (>=0.0.0)",
"tqdm (>=0.0.0)",
"jinja2 (>=0.0.0)",
"boto3 (>=1.28.0,<2.0.0)",
"dataclasses-json (>=0.0.0)",
"numpy (==2.1.3)",
"requests[socks] (>=0.0.0)",
"warcio (>=0.0.0)",
"jsonlines (>=0.0.0)",
"pysubs2 (>=0.0.0)",
"retrying (>=0.0.0)",
"rich-argparse (>=1.6.0,<2.0.0)",
"ruamel-yaml (>=0.18.10,<0.19.0)",
"rfc3161-client (>=1.0.5)",
"cryptography (>=46.0.3)",
"opentimestamps (>=0.4.5,<0.5.0)",
"bgutil-ytdlp-pot-provider (>=1.0.0)",
"yt-dlp[curl-cffi,default] (>=2025.5.22)",
"secretstorage (>=3.3.3,<4.0.0)",
"seleniumbase (>=4.36.4,<5.0.0)",
"pyautogui (>=0.9.54,<0.10.0)",
"pyperclip (>=1.9.0)",
]
[tool.poetry.group.dev.dependencies]
pytest = "^8.3.4"
autopep8 = "^2.3.1"
pytest-loguru = "^0.4.0"
pytest-mock = "^3.14.0"
ruff = "^0.15.2"
pre-commit = "^4.1.0"
[tool.poetry.group.docs.dependencies]
sphinx = "^8.1.3"
sphinx-autoapi = "^3.4.0"
sphinxcontrib-mermaid = "^1.0.0"
sphinx-autobuild = "^2024.10.3"
sphinx-copybutton = "^0.5.2"
myst-parser = "^4.0.0"
sphinx-book-theme = "^1.1.3"
linkify-it-py = "^2.0.3"
[project.scripts]
auto-archiver = "auto_archiver.__main__:main"
[project.urls]
homepage = "https://github.com/bellingcat/auto-archiver"
repository = "https://github.com/bellingcat/auto-archiver"
documentation = "https://github.com/bellingcat/auto-archiver"
[tool.pytest.ini_options]
markers = [
"download: marks tests that download content from the network",
"incremental: marks a class to run tests incrementally. If a test fails in the class, the remaining tests will be skipped",
]
[tool.ruff]
#exclude = ["docs"]
line-length = 120
# Remove this for a more detailed lint report
output-format = "concise"
# TODO: temp ignore rule for timestamping_enricher to allow for open PR
exclude = ["src/auto_archiver/modules/timestamping_enricher/*"]
[tool.ruff.lint]
# Extend the rules to check for by adding them to this option:
# See documentation for more details: https://docs.astral.sh/ruff/rules/
#extend-select = ["B"]
[tool.ruff.lint.per-file-ignores]
# Ignore import violations in __init__.py files
"__init__.py" = ["F401", "F403"]
# Ignore 'useless expression' in manifest files.
"__manifest__.py" = ["B018"]
[tool.ruff.format]
docstring-code-format = false

View File

@@ -1,5 +1,6 @@
import os.path
import click, json
import click
import json
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
@@ -12,7 +13,7 @@ from googleapiclient.errors import HttpError
# Code below from https://developers.google.com/drive/api/quickstart/python
# Example invocation: py scripts/create_update_gdrive_oauth_token.py -c secrets/credentials.json -t secrets/gd-token.json
SCOPES = ['https://www.googleapis.com/auth/drive']
SCOPES = ["https://www.googleapis.com/auth/drive.file"]
@click.command(
@@ -23,7 +24,7 @@ SCOPES = ['https://www.googleapis.com/auth/drive']
"-c",
type=click.Path(exists=True),
help="path to the credentials.json file downloaded from https://console.cloud.google.com/apis/credentials",
required=True
required=True,
)
@click.option(
"--token",
@@ -31,59 +32,58 @@ SCOPES = ['https://www.googleapis.com/auth/drive']
type=click.Path(exists=False),
default="gd-token.json",
help="file where to place the OAuth token, defaults to gd-token.json which you must then move to where your orchestration file points to, defaults to gd-token.json",
required=True
required=True,
)
def main(credentials, token):
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first time.
creds = None
if os.path.exists(token):
with open(token, 'r') as stream:
with open(token, "r") as stream:
creds_json = json.load(stream)
# creds = Credentials.from_authorized_user_file(creds_json, SCOPES)
creds_json['refresh_token'] = creds_json.get("refresh_token", "")
creds_json["refresh_token"] = creds_json.get("refresh_token", "")
creds = Credentials.from_authorized_user_info(creds_json, SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
print('Requesting new token')
print("Requesting new token")
creds.refresh(Request())
else:
print('First run through so putting up login dialog')
print("First run through so putting up login dialog")
# credentials.json downloaded from https://console.cloud.google.com/apis/credentials
flow = InstalledAppFlow.from_client_secrets_file(credentials, SCOPES)
creds = flow.run_local_server(port=55192)
# Save the credentials for the next run
with open(token, 'w') as token:
print('Saving new token')
with open(token, "w") as token:
print("Saving new token")
token.write(creds.to_json())
else:
print('Token valid')
print("Token valid")
try:
service = build('drive', 'v3', credentials=creds)
service = build("drive", "v3", credentials=creds)
# About the user
results = service.about().get(fields="*").execute()
emailAddress = results['user']['emailAddress']
emailAddress = results["user"]["emailAddress"]
print(emailAddress)
# Call the Drive v3 API and return some files
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
results = service.files().list(pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get("files", [])
if not items:
print('No files found.')
print("No files found.")
return
print('Files:')
print("Files:")
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
print("{0} ({1})".format(item["name"], item["id"]))
except HttpError as error:
print(f'An error occurred: {error}')
print(f"An error occurred: {error}")
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env bash
set -e # Exit on error
UUID=$(LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 16)
PROJECT_NAME="auto-archiver-$UUID"
ACCOUNT_NAME="autoarchiver"
KEY_FILE="service_account-$UUID.json"
DEST_DIR="$1"
echo "====================================================="
echo "🔧 Auto-Archiver Google Services Setup Script"
echo "====================================================="
echo "This script will:"
echo " 1. Install Google Cloud SDK if needed"
echo " 2. Create a Google Cloud project named $PROJECT_NAME"
echo " 3. Create a service account for Auto-Archiver"
echo " 4. Generate a key file for API access"
echo ""
echo " Tip: Pass a directory path as an argument to this script to move the key file there"
echo " e.g. ./generate_google_services.sh /path/to/secrets"
echo "====================================================="
# Check and install Google Cloud SDK based on platform
install_gcloud_sdk() {
if command -v gcloud &> /dev/null; then
echo "✅ Google Cloud SDK is already installed"
return 0
fi
echo "📦 Installing Google Cloud SDK..."
# Detect OS
case "$(uname -s)" in
Darwin*)
if command -v brew &> /dev/null; then
echo "🍺 Installing via Homebrew..."
brew install google-cloud-sdk --cask
else
echo "📥 Downloading Google Cloud SDK for macOS..."
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-latest-darwin-x86_64.tar.gz
tar -xf google-cloud-cli-latest-darwin-x86_64.tar.gz
./google-cloud-sdk/install.sh --quiet
rm google-cloud-cli-latest-darwin-x86_64.tar.gz
echo "🔄 Please restart your terminal and run this script again"
exit 0
fi
;;
Linux*)
echo "📥 Downloading Google Cloud SDK for Linux..."
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-latest-linux-x86_64.tar.gz
tar -xf google-cloud-cli-latest-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh --quiet
rm google-cloud-cli-latest-linux-x86_64.tar.gz
echo "🔄 Please restart your terminal and run this script again"
exit 0
;;
CYGWIN*|MINGW*|MSYS*)
echo "⚠️ Windows detected. Please follow manual installation instructions at:"
echo "https://cloud.google.com/sdk/docs/install-sdk"
exit 1
;;
*)
echo "⚠️ Unknown operating system. Please follow manual installation instructions at:"
echo "https://cloud.google.com/sdk/docs/install-sdk"
exit 1
;;
esac
echo "✅ Google Cloud SDK installed"
}
# Install Google Cloud SDK if needed
install_gcloud_sdk
# Login to Google Cloud
if gcloud auth list --filter=status:ACTIVE --format="value(account)" | grep -q "@"; then
echo "✅ Already authenticated with Google Cloud"
else
echo "🔑 Authenticating with Google Cloud..."
gcloud auth login
fi
# Create project
echo "🌟 Creating Google Cloud project: $PROJECT_NAME"
gcloud projects create $PROJECT_NAME
# Create service account
echo "👤 Creating service account: $ACCOUNT_NAME"
gcloud iam service-accounts create $ACCOUNT_NAME --project $PROJECT_NAME
# Enable required APIs (uncomment and add APIs as needed)
echo "⬆️ Enabling required Google APIs..."
gcloud services enable sheets.googleapis.com --project $PROJECT_NAME
gcloud services enable drive.googleapis.com --project $PROJECT_NAME
# Get the service account email
echo "📧 Retrieving service account email..."
ACCOUNT_EMAIL=$(gcloud iam service-accounts list --project $PROJECT_NAME --format="value(email)")
# Create and download key
echo "🔑 Generating service account key file: $KEY_FILE"
gcloud iam service-accounts keys create $KEY_FILE --iam-account=$ACCOUNT_EMAIL
# move the file to TARGET_DIR if provided
if [[ -n "$DEST_DIR" ]]; then
# Expand `~` if used
DEST_DIR=$(eval echo "$DEST_DIR")
# Ensure the directory exists
if [[ ! -d "$DEST_DIR" ]]; then
mkdir -p "$DEST_DIR"
fi
DEST_PATH="$DEST_DIR/$KEY_FILE"
echo "🚚 Moving key file to: $DEST_PATH"
mv "$KEY_FILE" "$DEST_PATH"
KEY_FILE="$DEST_PATH"
fi
echo "====================================================="
echo "✅ SETUP COMPLETE!"
echo "====================================================="
echo "📝 Important Information:"
echo " • Project Name: $PROJECT_NAME"
echo " • Service Account: $ACCOUNT_EMAIL"
echo " • Key File: $KEY_FILE"
echo ""
echo "📋 Next Steps:"
echo " 1. Share any Google Sheets with this email address:"
echo " $ACCOUNT_EMAIL"
echo " 2. Move $KEY_FILE to your auto-archiver secrets directory"
echo " 3. Update your auto-archiver config to use this key file (if needed)"
echo "====================================================="

View File

@@ -0,0 +1,63 @@
import json
import os
import io
from ruamel.yaml import YAML
from auto_archiver.core.module import ModuleFactory
from auto_archiver.core.consts import MODULE_TYPES
from auto_archiver.core.config import EMPTY_CONFIG
class SchemaEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return list(obj)
return json.JSONEncoder.default(self, obj)
# Get available modules
module_factory = ModuleFactory()
available_modules = module_factory.available_modules()
modules_by_type = {}
# Categorize modules by type
for module in available_modules:
for type in module.manifest.get("type", []):
modules_by_type.setdefault(type, []).append(module)
all_modules_ordered_by_type = sorted(
available_modules, key=lambda x: (MODULE_TYPES.index(x.type[0]), not x.requires_setup)
)
yaml: YAML = YAML()
config_string = io.BytesIO()
yaml.dump(EMPTY_CONFIG, config_string)
config_string = config_string.getvalue().decode("utf-8")
output_schema = {
"modules": dict(
(
module.name,
{
"name": module.name,
"display_name": module.display_name,
"manifest": module.manifest,
"configs": module.configs or None,
},
)
for module in all_modules_ordered_by_type
),
"steps": dict(
(f"{module_type}s", [module.name for module in modules_by_type[module_type]]) for module_type in MODULE_TYPES
),
"configs": [m.name for m in all_modules_ordered_by_type if m.configs],
"module_types": MODULE_TYPES,
"empty_config": config_string,
}
current_file_dir = os.path.dirname(os.path.abspath(__file__))
output_file = os.path.join(current_file_dir, "settings/src/schema.json")
with open(output_file, "w") as file:
print(f"Writing schema to {output_file}")
json.dump(output_schema, file, indent=4, cls=SchemaEncoder)

2
scripts/instagrapi_server/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
secrets*
*instagrapi_session.json

View File

@@ -0,0 +1,19 @@
FROM python:3.12-slim
WORKDIR /app
# Install Poetry
RUN pip install --upgrade pip
RUN pip install poetry
# Copy all source code
COPY . .
# Prevent Poetry from creating a virtual environment
RUN poetry config virtualenvs.create false
# Install dependencies
RUN poetry install --no-root
# Use uvicorn to run the FastAPI app
CMD ["poetry", "run", "uvicorn", "src.instaserver:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -0,0 +1,18 @@
[project]
name = "instaserver"
version = "0.1.0"
description = "A FastAPI InstagrAPI server"
package-mode = false
requires-python = ">=3.10"
dependencies = [
"fastapi (>=0.115.12,<0.116.0)",
"instagrapi (>=2.1.3,<3.0.0)",
"uvicorn (>=0.34.0,<0.35.0)",
"pillow (>=11.1.0,<12.0.0)",
"python-dotenv (>=1.1.0,<2.0.0)"
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
#
# run_instagrapi_server.sh
# Usage:
# From repo root: ./scripts/instagrapi_server/run_instagrapi_server.sh
# Or from script dir: ./run_instagrapi_server.sh
#
set -e
# Step 1: cd to the script's directory (contains Dockerfile and secrets/)
cd "$(dirname "$0")" || exit 1
# Create secrets/ if it doesn't exist
if [[ ! -d "secrets" ]]; then
echo "Creating secrets/ directory..."
mkdir secrets
fi
echo "Enter your Instagram credentials to store in secrets/.env"
read -rp "Instagram Username: " IGUSER
read -rsp "Instagram Password: " IGPASS
echo ""
cat <<EOF > secrets/.env
INSTAGRAM_USERNAME=$IGUSER
INSTAGRAM_PASSWORD=$IGPASS
EOF
echo "Created secrets/.env with your credentials."
# Build Docker image
IMAGE_NAME="instagrapi-server"
echo "Building Docker image '$IMAGE_NAME'..."
docker build -t "$IMAGE_NAME" .
# Run container
CONTAINER_NAME="ig-instasrv"
echo "Running container '$CONTAINER_NAME'..."
docker run -d \
--env-file secrets/.env \
-v "$(pwd)/secrets:/app/secrets" \
-p 8000:8000 \
--name "$CONTAINER_NAME" \
"$IMAGE_NAME"
echo "Done! Instagrapi server is running on port 8000."
echo "Use 'docker logs $CONTAINER_NAME' to view logs."
echo "Use 'docker stop $CONTAINER_NAME' and 'docker rm $CONTAINER_NAME' to stop/remove the container."

View File

@@ -0,0 +1,157 @@
"""https://subzeroid.github.io/instagrapi/
Run using the following command:
uvicorn src.instgrapinstance.instaserver:app --host 0.0.0.0 --port 8000 --reload
"""
import logging
import os
import sys
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from instagrapi import Client
from instagrapi.exceptions import LoginRequired, BadCredentials
load_dotenv(dotenv_path="secrets/.env")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
INSTAGRAM_USERNAME = os.getenv("INSTAGRAM_USERNAME")
INSTAGRAM_PASSWORD = os.getenv("INSTAGRAM_PASSWORD")
SESSION_FILE = "secrets/instagrapi_session.json"
app = FastAPI()
cl = Client()
@app.on_event("startup")
def startup_event():
"""Login automatically when server starts"""
try:
login_instagram()
except RuntimeError as e:
logging.error(f"API failed to start: {e}")
sys.exit(1)
def login_instagram():
"""Ensures Instagrapi is logged in and session is persistent"""
if not INSTAGRAM_USERNAME or not INSTAGRAM_PASSWORD:
raise RuntimeError("Instagram credentials are missing.")
if os.path.exists(SESSION_FILE):
try:
cl.load_settings(SESSION_FILE)
cl.get_timeline_feed()
logging.info("Using saved session.")
return
except LoginRequired:
logging.info("Session expired. Logging in again...")
try:
cl.login(INSTAGRAM_USERNAME, INSTAGRAM_PASSWORD)
cl.dump_settings(SESSION_FILE)
logging.info("Login successful, session saved.")
except BadCredentials as bc:
raise RuntimeError("Incorrect Instagram username or password.") from bc
except Exception as e:
raise RuntimeError(f"Login failed: {e}") from e
@app.get("/v1/media/by/id")
def get_media_by_id(id: str):
"""Fetch post details by media ID"""
logging.info(f"Fetching media by ID: {id}")
try:
media = cl.media_info(id)
return media.model_dump()
except Exception as e:
logging.warning(f"Media not found for ID {id}: {e}")
raise HTTPException(status_code=404, detail="Post not found") from e
@app.get("/v1/media/by/code")
def get_media_by_code(code: str):
"""Fetch post details by shortcode"""
logging.info(f"Fetching media by shortcode: {code}")
try:
media_id = cl.media_pk_from_code(code)
media = cl.media_info(media_id)
return media.model_dump()
except Exception as e:
logging.warning(f"Media not found for code {code}: {e}")
raise HTTPException(status_code=404, detail="Post not found") from e
@app.get("/v2/user/tag/medias")
def get_user_tagged_medias(user_id: str, page_id: str = None):
logging.info(f"Fetching tagged medias for user_id={user_id} page_id={page_id}")
try:
# Placeholder for now
items, next_page_id = [], None
return {"response": {"items": items}, "next_page_id": next_page_id}
except Exception as e:
logging.warning(f"Tagged media not found for {user_id}: {e}")
raise HTTPException(status_code=404, detail="Tagged media not found") from e
@app.get("/v1/user/highlights")
def get_user_highlights(user_id: str):
logging.info(f"Fetching highlights list for user_id={user_id}")
try:
highlights = cl.user_highlights(user_id)
return [h.model_dump() for h in highlights]
except Exception as e:
logging.warning(f"Highlights not found for {user_id}: {e}")
raise HTTPException(status_code=404, detail="No highlights found") from e
@app.get("/v2/highlight/by/id")
def get_highlight_by_id(id: str):
logging.info(f"Fetching highlight details for id={id}")
try:
highlight = cl.highlight_info(id)
return {"response": {"reels": {f"highlight:{id}": highlight.model_dump()}}}
except Exception as e:
logging.warning(f"Highlight not found for id {id}: {e}")
raise HTTPException(status_code=404, detail="Highlight not found") from e
@app.get("/v1/user/stories/by/username")
def get_stories(username: str):
logging.info(f"Fetching stories for username={username}")
try:
user_id = cl.user_id_from_username(username)
stories = cl.user_stories(user_id)
return [story.model_dump() for story in stories]
except Exception as e:
logging.warning(f"Stories not found for {username}: {e}")
raise HTTPException(status_code=404, detail="Stories not found") from e
@app.get("/v2/user/by/username")
def get_user_by_username(username: str):
logging.info(f"Fetching user profile for username={username}")
try:
user = cl.user_info_by_username(username)
return {"user": user.model_dump()}
except Exception as e:
logging.warning(f"User not found: {username}: {e}")
raise HTTPException(status_code=404, detail="User not found") from e
@app.get("/v1/user/medias/chunk")
def get_user_medias(user_id: str, end_cursor: str = None):
logging.info(f"Fetching paginated medias for user_id={user_id}, end_cursor={end_cursor}")
try:
posts, next_cursor = cl.user_medias_paginated(user_id, end_cursor=end_cursor)
return [[post.model_dump() for post in posts], next_cursor]
except Exception as e:
logging.warning(f"No posts found for user_id={user_id}: {e}")
raise HTTPException(status_code=404, detail="No posts found") from e
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -1,19 +0,0 @@
#!/bin/bash
set -e
TAG=$(python -c 'from src.auto_archiver.version import __version__; print("v" + __version__)')
read -p "Creating new release for $TAG. Do you want to continue? [Y/n] " prompt
if [[ $prompt == "y" || $prompt == "Y" || $prompt == "yes" || $prompt == "Yes" ]]; then
git add -A
git commit -m "Bump version to $TAG for release" || true && git push
echo "Creating new git tag $TAG"
git tag "$TAG" -m "$TAG"
git push --tags
else
echo "Cancelled"
exit 1
fi

24
scripts/settings/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,3 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>

3914
scripts/settings/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
{
"name": "material-ui-vite-ts",
"private": true,
"version": "5.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@emotion/react": "latest",
"@emotion/styled": "latest",
"@mui/icons-material": "^7.1.1",
"@mui/material": "^7.1.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-markdown": "^10.0.0",
"yaml": "^2.7.0"
},
"devDependencies": {
"@types/react": "latest",
"@types/react-dom": "latest",
"@vitejs/plugin-react": "latest",
"typescript": "latest",
"vite": "latest",
"vite-plugin-singlefile": "^2.1.0"
}
}

View File

@@ -0,0 +1,450 @@
import * as React from 'react';
import { useEffect, useState, useRef } from 'react';
import Container from '@mui/material/Container';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import FileUploadIcon from '@mui/icons-material/FileUpload';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragOverlay
} from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
rectSortingStrategy
} from "@dnd-kit/sortable";
import type { DragStartEvent, DragEndEvent, UniqueIdentifier } from "@dnd-kit/core";
import { Module } from './types';
import { modules, steps, module_types, empty_config } from './schema.json';
import {
Stack,
Button,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import { parseDocument, Document, YAMLSeq, YAMLMap, Scalar } from 'yaml'
import StepCard from './StepCard';
function FileDrop({ setYamlFile }: { setYamlFile: React.Dispatch<React.SetStateAction<Document>> }) {
const [showError, setShowError] = useState(false);
const [label, setLabel] = useState(<>Drag and drop your orchestration.yaml file here, or click to select a file.</>);
const wrapperRef = useRef(null);
function openYAMLFile(event: any) {
let file = event.target.files[0];
if (file.type.indexOf('yaml') === -1) {
setShowError(true);
setLabel(<>Invalid type, only YAML files are accepted.</>)
return;
}
let reader = new FileReader();
reader.onload = function (e) {
let contents = e.target ? e.target.result : '';
try {
let document = parseDocument(contents as string);
if (document.errors.length > 0) {
// not a valid yaml file
setShowError(true);
setLabel(<>Invalid file. Make sure your Orchestration is a valid YAML file with a 'steps' section in it.</>)
return;
} else {
setShowError(false);
setLabel(<>File loaded successfully.</>)
}
// do some basic validation of 'steps'
let steps = document.get('steps');
if (!steps) {
setShowError(true);
setLabel(<>Invalid file. Your orchestration file must have a 'steps' section in it.</>)
return;
}
const replacements = {
feeder: 'feeders',
formatter: 'formatters',
archivers: 'extractors',
};
let error = false;
for (let stepType of Object.keys(replacements)) {
if (steps.get(stepType) !== undefined) {
setShowError(true);
setLabel(<>Invalid file. Your orchestration file appears to be in the old (v0.12) format with a '{stepType}' section.<br/>You should manually update your orchestration file first (hint: {stepType} {replacements[stepType]})</>);
error = true;
return;
}
};
setYamlFile(document);
} catch (e) {
console.error(e);
}
}
reader.readAsText(file);
}
return (
<>
<div
style={{
position: 'relative',
width: '100%',
border: 'dashed',
borderRadius:'5px',
textAlign: 'center',
borderWidth: '1px',
padding: '20px' }}
onDragEnter={(e) => {
e.currentTarget.style.backgroundColor = 'var(--mui-palette-LinearProgress-infoBg)';
}}
onDragLeave={(e) => {
e.currentTarget.style.backgroundColor = '';
}}
onDrop={(e) => {
e.currentTarget.style.backgroundColor = '';
}}
>
<FileUploadIcon style={{ fontSize: 50 }} />
<input style={{
opacity: 0,
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
cursor: 'pointer',
}}
type="file" id="file"
accept=".yaml"
onChange={openYAMLFile} />
<Typography variant="body1" color={showError ? 'error' : ''} >
{label}
</Typography>
</div>
</>
);
}
function ModuleTypes({ stepType, setEnabledModules, enabledModules, configValues }: { stepType: string, setEnabledModules: any, enabledModules: any, configValues: any }) {
const [showError, setShowError] = useState<boolean>(false);
const [activeId, setActiveId] = useState<UniqueIdentifier>();
const [items, setItems] = useState<string[]>([]);
useEffect(() => {
setItems(enabledModules[stepType].map(([name, enabled]: [string, boolean]) => name));
}
, [enabledModules]);
const toggleModule = (event: any) => {
// make sure that 'feeder' and 'formatter' types only have one value
let name = event.target.id;
let checked = event.target.checked;
if (stepType === 'feeders' || stepType === 'formatters') {
// check how many modules of this type are enabled
const checkedModules = enabledModules[stepType].filter(([m, enabled]: [string, boolean]) => {
return (m !== name && enabled) || (checked && m === name)
});
if (checkedModules.length > 1) {
setShowError(true);
} else {
setShowError(false);
}
} else {
setShowError(false);
}
let newEnabledModules = { ...enabledModules };
newEnabledModules[stepType] = enabledModules[stepType].map(([m, enabled]: [string, boolean]) => {
return (m === name) ? [m, checked] : [m, enabled];
});
setEnabledModules(newEnabledModules);
}
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates
})
);
const handleDragStart = (event: DragStartEvent) => {
setActiveId(event.active.id);
};
const handleDragEnd = (event: DragEndEvent) => {
setActiveId(undefined);
const { active, over } = event;
if (active.id !== over?.id) {
const oldIndex = items.indexOf(active.id as string);
const newIndex = items.indexOf(over?.id as string);
let newArray = arrayMove(items, oldIndex, newIndex);
// set it also on steps
let newEnabledModules = { ...enabledModules };
newEnabledModules[stepType] = enabledModules[stepType].sort((a, b) => {
return newArray.indexOf(a[0]) - newArray.indexOf(b[0]);
})
setEnabledModules(newEnabledModules);
}
};
return (
<>
<Box sx={{ my: 4 }}>
<Typography id={stepType} variant="h6" style={{ textTransform: 'capitalize' }} >
{stepType}
</Typography>
<Typography variant="body1" >
Select the <a href={`https://auto-archiver.readthedocs.io/en/latest/modules/${stepType.slice(0,-1)}.html`} target="_blank">{stepType}</a> you wish to enable. Drag to reorder.
</Typography>
</Box>
{showError ? <Typography variant="body1" color="error" >Only one {stepType.slice(0,-1)} can be enabled at a time.</Typography> : null}
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
onDragStart={handleDragStart}
>
<Grid container spacing={1} key={stepType}>
<SortableContext items={items} strategy={rectSortingStrategy}>
{items.map((name: string) => {
let m: Module = modules[name];
return (
<StepCard key={name} type={stepType} module={m} toggleModule={toggleModule} enabledModules={enabledModules} configValues={configValues} />
);
})}
<DragOverlay>
{activeId ? (
<div
style={{
width: "100%",
height: "100%",
backgroundColor: "grey",
opacity: 0.1,
}}
></div>
) : null}
</DragOverlay>
</SortableContext>
</Grid>
</DndContext>
</>
);
}
export default function App() {
const [yamlFile, setYamlFile] = useState<Document>(new Document());
const [enabledModules, setEnabledModules] = useState<{}>(Object.fromEntries(Object.keys(steps).map(type => [type, steps[type].map((name: string) => [name, false])])));
const [configValues, setConfigValues] = useState<{
[key: string]: {
[key: string
]: any
}
}>(
Object.keys(modules).reduce((acc, module) => {
acc[module] = {};
return acc;
}, {})
);
const saveSettings = function (copy: boolean = false) {
// edit the yamlFile
// generate the steps config
let stepsConfig = enabledModules;
let finalYamlFile: Document = null;
if (!yamlFile || yamlFile.contents == null) {
// create the yaml file from
finalYamlFile = parseDocument(empty_config as string);
} else {
finalYamlFile = yamlFile;
}
// set the steps
module_types.forEach((type: string) => {
let stepType = type + 's';
let existingSteps = finalYamlFile.getIn(['steps', stepType]) as YAMLSeq;
stepsConfig[stepType].forEach(([name, enabled]: [string, boolean]) => {
let index = existingSteps.items.findIndex((item) => {
return (item.value || item) === name
});
let stepItem = finalYamlFile.getIn(['steps', stepType], true) as YAMLSeq;
if (enabled && index === -1) {
finalYamlFile.addIn(['steps', stepType], name);
stepItem.commentBefore = stepItem.commentBefore?.replace("\n - " + name, '');
stepItem.comment = stepItem.comment?.replace("\n - " + name, '');
} else if (!enabled && index !== -1) {
// set the value to empty and add a comment before with the commented value
finalYamlFile.deleteIn(['steps', stepType, index]);
stepItem.commentBefore += "\n - " + name;
finalYamlFile.setIn(['steps', stepType], stepItem);
}
});
// sort the items
existingSteps.items.sort((a: Scalar | string, b: Scalar | string) => {
return (stepsConfig[stepType].findIndex((val: [string, boolean]) => {return val[0] === (a.value || a)}) -
stepsConfig[stepType].findIndex((val: [string, boolean]) => {return val[0] === (b.value || b)}))
});
existingSteps.flow = existingSteps.items.length ? false : true;
});
// set all other settings
// loop through each item that isn't 'steps' in the finalYamlFile and check if it exists in configValues
Object.keys(configValues).forEach((module_name: string) => {
// get an existing key
let existingConfig = finalYamlFile.get(module_name, true) as YAMLMap;
if (existingConfig) {
Object.keys(configValues[module_name]).forEach((config_name: string) => {
let existingConfigYAML = existingConfig.get(config_name, true) as Scalar;
if (existingConfigYAML) {
existingConfigYAML.value = configValues[module_name][config_name];
existingConfig.set(config_name, existingConfigYAML);
} else {
existingConfig.set(config_name, configValues[module_name][config_name]);
}
});
finalYamlFile.set(module_name, existingConfig);
} else {
if (configValues[module_name] && Object.keys(configValues[module_name]).length > 0) {
finalYamlFile.set(module_name, configValues[module_name]);
}
}
});
if (copy) {
navigator.clipboard.writeText(String(finalYamlFile)).then(() => {
alert("Settings copied to clipboard.");
});
} else {
// offer the file for download
const blob = new Blob([String(finalYamlFile)], { type: 'application/x-yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'orchestration.yaml';
a.click();
}
}
useEffect(() => {
// load the configs, and set the default values if they exist
let newConfigValues = {};
Object.keys(modules).map((module: string) => {
let m = modules[module];
let configs = m.configs;
if (!configs) {
return;
}
newConfigValues[module] = {};
Object.keys(configs).map((config: string) => {
let config_args = configs[config];
if (config_args.default !== undefined) {
newConfigValues[module][config] = config_args.default;
}
});
})
setConfigValues(newConfigValues);
}, []);
useEffect(() => {
if (!yamlFile || yamlFile.contents == null) {
return;
}
let settings = yamlFile.toJS();
// make a deep copy of settings
let stepSettings = settings['steps'];
let newEnabledModules = Object.fromEntries(Object.keys(steps).map((type: string) => {
return [type, steps[type].map((name: string) => {
return [name, stepSettings[type].indexOf(name) !== -1];
}).sort((a, b) => {
let aIndex = stepSettings[type].indexOf(a[0]);
let bIndex = stepSettings[type].indexOf(b[0]);
if (aIndex === -1 && bIndex === -1) {
return a - b;
}
if (bIndex === -1) {
return -1;
}
if (aIndex === -1) {
return 1;
}
return aIndex - bIndex;
})];
}).sort((a, b) => {
return module_types.indexOf(a[0]) - module_types.indexOf(b[0]);
}));
setEnabledModules(newEnabledModules);
// set the config values
let newConfigValues = settings;
delete newConfigValues['steps'];
setConfigValues(Object.keys(modules).reduce((acc, module) => {
acc[module] = newConfigValues[module] || {};
return acc;
}, {}));
}, [yamlFile]);
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Box sx={{ my: 4 }}>
<Typography variant="h5" >
1. Select your orchestration.yaml settings file.
</Typography>
<Typography variant="body1">Or skip this step to start from scratch</Typography>
<FileDrop setYamlFile={setYamlFile} />
</Box>
<Box sx={{ my: 4 }}>
<Typography variant="h5" >
2. Choose the Modules you wish to enable/disable
</Typography>
{Object.keys(steps).map((stepType: string) => {
return (
<Box key={stepType} sx={{ my: 4 }}>
<ModuleTypes stepType={stepType} setEnabledModules={setEnabledModules} enabledModules={enabledModules} configValues={configValues} />
</Box>
);
})}
</Box>
<Box sx={{ my: 4 }}>
<Typography variant="h5" >
3. Configure your Enabled Modules
</Typography>
<Typography variant="body1" >
Next to each module you've enabled, you can click 'Configure' to set the module's settings.
</Typography>
</Box>
<Box sx={{ my: 4 }}>
<Typography variant="h5" >
4. Save your settings
</Typography>
<Stack direction="row" spacing={2} sx={{ my: 2 }}>
<Button variant="contained" color="primary" onClick={() => saveSettings(true)}>Copy Settings to Clipboard</Button>
<Button variant="contained" color="primary" onClick={() => saveSettings()}>Save Settings to File</Button>
</Stack>
</Box>
</Box>
</Container>
);
}

View File

@@ -0,0 +1,258 @@
import { useState } from "react";
import { useSortable } from "@dnd-kit/sortable";
import ReactMarkdown from 'react-markdown';
import { CSS } from "@dnd-kit/utilities";
import {
Card,
CardActions,
CardHeader,
Button,
Dialog,
DialogTitle,
DialogContent,
Box,
IconButton,
Checkbox,
Select,
MenuItem,
FormControl,
FormControlLabel,
FormHelperText,
TextField,
Stack,
Typography,
InputAdornment,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import HelpIconOutlined from '@mui/icons-material/HelpOutline';
import { Module, Config } from "./types";
// adds 'capitalize' method to String prototype
declare global {
interface String {
capitalize(): string;
}
}
String.prototype.capitalize = function (this: string) {
return this.charAt(0).toUpperCase() + this.slice(1);
};
const StepCard = ({
type,
module,
toggleModule,
enabledModules,
configValues
}: {
type: string,
module: Module,
toggleModule: any,
enabledModules: any,
configValues: any
}) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging
} = useSortable({ id: module.name });
const style = {
...Card.style,
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? "100" : "auto",
opacity: isDragging ? 0.3 : 1
};
let name = module.name;
const [helpOpen, setHelpOpen] = useState(false);
const [configOpen, setConfigOpen] = useState(false);
const enabled = enabledModules[type].find((m: any) => m[0] === name)[1];
return (
<Grid ref={setNodeRef} size={{ xs: 6, sm: 4, md: 3 }} style={style}>
<Card >
<CardHeader
title={
<FormControlLabel
style={{paddingRight: '0 !important'}}
control={<Checkbox title="Check to enable this module" sx={{paddingTop:0, paddingBottom:0}} id={name} onClick={toggleModule} checked={enabled} />}
label={module.display_name} />
}
/>
<CardActions>
<Box sx={{ justifyContent: 'space-between', display: 'flex', width: '100%' }}>
<Box>
<IconButton title="Module information" size="small" onClick={() => setHelpOpen(true)}>
<HelpIconOutlined />
</IconButton>
{enabled && module.configs && name != 'cli_feeder' ? (
<Button size="small" onClick={() => setConfigOpen(true)}>Configure</Button>
) : null}
</Box>
<IconButton size="small" title="Drag to reorder" sx={{ cursor: 'grab' }} {...listeners} {...attributes}>
<DragIndicatorIcon/>
</IconButton>
</Box>
</CardActions>
</Card>
<Dialog
open={helpOpen}
onClose={() => setHelpOpen(false)}
maxWidth="lg"
>
<DialogTitle>
{module.display_name}
</DialogTitle>
<DialogContent>
<ReactMarkdown>
{module.manifest.description.split("\n").map((line: string) => line.trim()).join("\n")}
</ReactMarkdown>
</DialogContent>
</Dialog>
{module.configs && name != 'cli_feeder' && <ConfigPanel module={module} open={configOpen} setOpen={setConfigOpen} configValues={configValues} />}
</Grid>
)
}
function ConfigField({ config_value, module, configValues }: { config_value: any, module: Module, configValues: any }) {
const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
};
const handleMouseUpPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
};
function setConfigValue(config: any, value: any) {
configValues[module.name][config] = value;
}
const config_args: Config = module.configs[config_value];
const config_name: string = config_value.replace(/_/g, " ");
const config_display_name = config_name.capitalize();
const value = configValues[module.name][config_value] || config_args.default;
const config_value_lower = config_value.toLowerCase();
const is_password = config_value_lower.includes('password') ||
config_value_lower.includes('secret') ||
config_value_lower.includes('token') ||
config_value_lower.includes('key') ||
config_value_lower.includes('api_hash') ||
config_args.type === 'password';
const text_input_type = is_password ? 'password' : (config_args.type === 'int' ? 'number' : 'text');
return (
<Box>
<Typography variant='body1' style={{ fontWeight: 'bold' }}>{config_display_name} {config_args.required && (`(required)`)} </Typography>
<FormControl size="small">
{config_args.type === 'bool' ?
<FormControlLabel control={
<Checkbox defaultChecked={value} size="small" id={`${module}.${config_value}`}
onChange={(e) => {
setConfigValue(config_value, e.target.checked);
}}
/>} label={config_args.help.capitalize()}
/>
:
(
config_args.choices !== undefined ?
<Select size="small" id={`${module}.${config_value}`}
defaultValue={config_args.default}
value={value}
onChange={(e) => {
setConfigValue(config_value, e.target.value);
}}
>
{config_args.choices.map((choice: any) => {
return (
<MenuItem key={`${module}.${config_value}.${choice}`}
value={choice}>{choice}</MenuItem>
);
})}
</Select>
:
(config_args.type === 'json_loader' ?
<TextField multiline size="small" id={`${module}.${config_value}`} defaultValue={JSON.stringify(value, null, 2)} rows={6} onChange={
(e) => {
try {
let val = JSON.parse(e.target.value);
setConfigValue(config_value, val);
} catch (e) {
console.log(e);
}
}
} />
:
<TextField size="small" id={`${module}.${config_value}`} defaultValue={value} type={showPassword ? 'text' : text_input_type}
onChange={(e) => {
setConfigValue(config_value, e.target.value);
}}
required={config_args.required}
slotProps={ is_password ? {
input: { endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
onMouseUp={handleMouseUpPassword}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
)}
} : {}}
/>
)
)
}
{config_args.type !== 'bool' && (
<FormHelperText >{config_args.help.capitalize()}</FormHelperText>
)}
</FormControl>
</Box>
)
}
function ConfigPanel({ module, open, setOpen, configValues }: { module: Module, open: boolean, setOpen: any, configValues: any }) {
return (
<>
<Dialog
open={open}
onClose={() => setOpen(false)}
maxWidth="lg"
>
<DialogTitle>
{module.display_name}
</DialogTitle>
<DialogContent>
<Stack direction="column" spacing={1}>
{Object.keys(module.configs).map((config_value: any) => {
return (
<ConfigField key={config_value} config_value={config_value} module={module} configValues={configValues} />
);
})}
</Stack>
</DialogContent>
</Dialog>
</>
);
}
export default StepCard;

View File

@@ -0,0 +1,44 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { ThemeProvider } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import App from './App';
import { createTheme } from '@mui/material/styles';
import { red } from '@mui/material/colors';
import { useState, useEffect } from 'react';
function RootApp() {
const [mode, setMode] = useState('light');
useEffect(() => {
setMode(window.localStorage.getItem('theme') || 'light');
}, []);
var observer = new MutationObserver(function(mutations) {
setMode(window.localStorage.getItem('theme') || 'light');
})
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['data-theme']});
// A custom theme for this app
const theme = createTheme({
palette: {
mode: mode == 'light' ? 'light' : 'dark',
},
cssVariables: true
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RootApp />
</React.StrictMode>,
);

21
scripts/settings/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
export interface Config {
name: string;
description: string;
type: string?;
default: any;
help: string;
choices: string[];
required: boolean;
}
interface Manifest {
description: string;
}
export interface Module {
name: string;
description: string;
configs: { [key: string]: Config };
manifest: Manifest;
display_name: string;
}

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,12 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from "vite-plugin-singlefile"
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), viteSingleFile()],
build: {
// minify: false,
// sourcemap: true,
}
});

27
scripts/telegram_setup.py Normal file
View File

@@ -0,0 +1,27 @@
"""
This script is used to create a new session file for the Telegram client.
To do this you must first create a Telegram application at https://my.telegram.org/apps
And store your id and hash in the environment variables TELEGRAM_API_ID and TELEGRAM_API_HASH.
Create a .env file, or add the following to your environment :
```
export TELEGRAM_API_ID=[YOUR_ID_HERE]
export TELEGRAM_API_HASH=[YOUR_HASH_HERE]
```
Then run this script to create a new session file.
You will need to provide your phone number and a 2FA code the first time you run this script.
"""
import os
from telethon.sync import TelegramClient
from auto_archiver.utils.custom_logger import logger
# Create a
API_ID = os.getenv("TELEGRAM_API_ID")
API_HASH = os.getenv("TELEGRAM_API_HASH")
SESSION_FILE = "secrets/anon-insta"
os.makedirs("secrets", exist_ok=True)
with TelegramClient(SESSION_FILE, API_ID, API_HASH) as client:
logger.success(f"new session file created: {SESSION_FILE}.session")

View File

@@ -1,53 +0,0 @@
[metadata]
name = auto_archiver
version = attr: auto_archiver.version.__version__
author = Bellingcat
author_email = tech@bellingcat.com
description = Easily archive online media content
long_description = file: README.md
long_description_content_type = text/markdown
keywords = archive, oosi, osint, scraping
license = MIT
classifiers =
Intended Audience :: Developers
Intended Audience :: Science/Research
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
project_urls =
Source Code = https://github.com/bellingcat/auto-archiver
Bug Tracker = https://github.com/bellingcat/auto-archiver/issues
Bellingcat = https://www.bellingcat.com
platforms = any
[options]
setup_requires =
setuptools-pipfile
zip_safe = False
package_dir=
=src
packages=find:
find_packages=true
python_requires = >=3.8
[options.package_data]
* = *.html
[options.entry_points]
console_scripts =
auto-archiver = auto_archiver.__main__:main
# [options.extras_require]
# pdf = ReportLab>=1.2; RXP
# rest = docutils>=0.3; pack ==1.1, ==1.3
[options.packages.find]
where=src
# include=auto_archiver*
# exclude =
# examples*
# .eggs*
# build*
# secrets*
# tmp*
# docs*
# src.tests*

View File

@@ -1,4 +0,0 @@
from setuptools import setup
if __name__ == "__main__":
setup()

View File

@@ -1,7 +0,0 @@
from . import archivers, databases, enrichers, feeders, formatters, storages, utils, core
# need to manually specify due to cyclical deps
from .core.orchestrator import ArchivingOrchestrator
from .core.config import Config
# making accessible directly
from .core.metadata import Metadata

View File

@@ -1,11 +1,12 @@
from . import Config
from . import ArchivingOrchestrator
"""Entry point for the auto_archiver package."""
from auto_archiver.core.orchestrator import ArchivingOrchestrator
import sys
def main():
config = Config()
config.parse()
orchestrator = ArchivingOrchestrator(config)
for r in orchestrator.feed(): pass
for _ in ArchivingOrchestrator()._command_line_run(sys.argv[1:]):
pass
if __name__ == "__main__":

Some files were not shown because too many files have changed in this diff Show More