Compare commits

..

6 commits

250 changed files with 17216 additions and 270536 deletions

View file

@ -24,41 +24,26 @@ jobs:
with: with:
cache-on-failure: "true" cache-on-failure: "true"
- name: Download rustypipe-botguard
run: |
TARGET=$(rustc --version --verbose | grep "host:" | sed -e 's/^host: //')
cd ~
curl -SsL -o rustypipe-botguard.tar.xz "https://codeberg.org/ThetaDev/rustypipe-botguard/releases/download/v0.1.1/rustypipe-botguard-v0.1.1-${TARGET}.tar.xz"
cd /usr/local/bin
sudo tar -xJf ~/rustypipe-botguard.tar.xz
rm ~/rustypipe-botguard.tar.xz
rustypipe-botguard --version
- name: 📎 Clippy - name: 📎 Clippy
run: | run: cargo clippy --all --tests --features=rss,indicatif,audiotag -- -D warnings
cargo clippy --all --tests --features=rss,userdata,indicatif,audiotag -- -D warnings
cargo clippy --package=rustypipe --tests -- -D warnings
cargo clippy --package=rustypipe-downloader -- -D warnings
cargo clippy --package=rustypipe-cli -- -D warnings
cargo clippy --package=rustypipe-cli --features=timezone -- -D warnings
- name: 🧪 Test - name: 🧪 Test
run: cargo nextest run --config-file ~/.config/nextest.toml --profile ci --retries 2 --features rss,userdata --workspace -- --skip 'user_data::' run: |
printf "$RUSTYPIPE_CACHE" > rustypipe_cache.json
ls
head -n 1 rustypipe_cache.json
RUST_LOG=debug cargo test --package rustypipe --test youtube --features rss -- user_login --exact --nocapture
cargo nextest run --config-file ~/.config/nextest.toml --profile ci --retries 2 --features rss --workspace
env: env:
ALL_PROXY: "http://warpproxy:8124" ALL_PROXY: "http://warpproxy:8124"
RUSTYPIPE_CACHE: "${{ secrets.RUSTYPIPE_CACHE }}"
- name: Move test report
if: always()
run: mv target/nextest/ci/junit.xml junit.xml || true
- name: 💌 Upload test report - name: 💌 Upload test report
if: always() if: always()
uses: https://code.forgejo.org/forgejo/upload-artifact@v4 uses: https://code.forgejo.org/forgejo/upload-artifact@v4
with: with:
name: test name: test
path: | path: target/nextest/ci/junit.xml
junit.xml
rustypipe_reports
- name: 🔗 Artifactview PR comment - name: 🔗 Artifactview PR comment
if: ${{ always() && github.event_name == 'pull_request' }} if: ${{ always() && github.event_name == 'pull_request' }}

View file

@ -1,69 +0,0 @@
name: Release CLI
on:
push:
tags:
- "rustypipe-cli/v*.*.*"
jobs:
Release:
runs-on: cimaster-latest
steps:
- name: 📦 Checkout repository
uses: actions/checkout@v4
- name: Setup cross compilation
run: |
rustup target add x86_64-pc-windows-msvc x86_64-apple-darwin aarch64-apple-darwin
cargo install cargo-xwin
# https://wapl.es/rust/2019/02/17/rust-cross-compile-linux-to-macos.html/
sudo apt-get install -y llvm clang cmake
cd ~
git clone https://github.com/tpoechtrager/osxcross
cd osxcross
wget -nc "https://github.com/joseluisq/macosx-sdks/releases/download/12.3/MacOSX12.3.sdk.tar.xz"
mv MacOSX12.3.sdk.tar.xz tarballs/
UNATTENDED=yes OSX_VERSION_MIN=12.3 ./build.sh
OSXCROSS_BIN="$(pwd)/target/bin"
echo "CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER=$(find "$OSXCROSS_BIN" -name "x86_64-apple-darwin*-clang")" >> $GITHUB_ENV
echo "CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS=-Car=$(find "$OSXCROSS_BIN" -name "x86_64-apple-darwin*-ar"),-Clink-arg=-undefined,-Clink-arg=dynamic_lookup" >> $GITHUB_ENV
echo "CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=$(find "$OSXCROSS_BIN" -name "aarch64-apple-darwin*-clang")" >> $GITHUB_ENV
echo "CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS=-Car=$(find "$OSXCROSS_BIN" -name "aarch64-apple-darwin*-ar"),-Clink-arg=-undefined,-Clink-arg=dynamic_lookup" >> $GITHUB_ENV
- name: ⚒️ Build application
run: |
export PATH="$PATH:$HOME/osxcross/target/bin"
CRATE="rustypipe-cli"
PKG_CONFIG_SYSROOT_DIR=/usr/x86_64-linux-gnu cargo build --release --package=$CRATE --target x86_64-unknown-linux-gnu
PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu cargo build --release --package=$CRATE --target aarch64-unknown-linux-gnu
CC="$CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER" CXX="$CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER++" cargo build --release --package=$CRATE --target x86_64-apple-darwin
CC="$CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER" CXX="$CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER++" cargo build --release --package=$CRATE --target aarch64-apple-darwin
cargo xwin build --release --package=$CRATE --target x86_64-pc-windows-msvc
- name: Prepare release
run: |
CRATE="rustypipe-cli"
BIN="rustypipe"
echo "CRATE=$CRATE" >> "$GITHUB_ENV"
echo "CRATE_VERSION=$(echo '${{ github.ref_name }}' | awk 'BEGIN{RS="/"} NR==2{print}')" >> "$GITHUB_ENV"
CL_PATH="cli/CHANGELOG.md"
{
echo 'CHANGELOG<<END_OF_FILE'
awk 'BEGIN{RS="(^|\n)## [^\n]+\n*"} NR==2 { print }' "$CL_PATH"
echo END_OF_FILE
} >> "$GITHUB_ENV"
mkdir dist
for arch in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin; do
tar -cJf "dist/${BIN}-${CRATE_VERSION}-${arch}.tar.xz" -C target/${arch}/release "${BIN}"
done
(cd target/x86_64-pc-windows-msvc/release && zip -9 "../../../dist/${BIN}-${CRATE_VERSION}-x86_64-pc-windows-msvc.zip" "${BIN}.exe")
- name: 🎉 Publish release
uses: https://gitea.com/actions/release-action@main
with:
title: "${{ env.CRATE }} ${{ env.CRATE_VERSION }}"
body: "${{ env.CHANGELOG }}"
files: dist/*

View file

@ -17,7 +17,7 @@ jobs:
renovate: renovate:
runs-on: docker runs-on: docker
container: container:
image: renovate/renovate:39 image: renovate/renovate:latest
steps: steps:
- name: Load renovate repo cache - name: Load renovate repo cache

3
.gitignore vendored
View file

@ -4,5 +4,4 @@
*.snap.new *.snap.new
rustypipe_reports rustypipe_reports
rustypipe_cache*.json rustypipe_cache.json
bg_snapshot.bin

View file

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v4.3.0
hooks: hooks:
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-json - id: check-json
@ -10,8 +10,4 @@ repos:
hooks: hooks:
- id: cargo-fmt - id: cargo-fmt
- id: cargo-clippy - id: cargo-clippy
name: cargo-clippy rustypipe args: ["--all", "--tests", "--features=rss,indicatif,audiotag", "--", "-D", "warnings"]
args: ["--package=rustypipe", "--tests", "--", "-D", "warnings"]
- id: cargo-clippy
name: cargo-clippy workspace
args: ["--all", "--tests", "--features=rss,userdata,indicatif,audiotag", "--", "-D", "warnings"]

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.features": ["rss", "indicatif", "audiotag"]
}

10
.woodpecker.yml Normal file
View file

@ -0,0 +1,10 @@
steps:
test:
image: rust:latest
environment:
- CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
commands:
- rustup component add rustfmt clippy
- cargo fmt --all --check
- cargo clippy --all --features=rss -- -D warnings
- cargo test --features=rss --workspace

View file

@ -3,169 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [v0.10.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.9.0..rustypipe/v0.10.0) - 2025-02-09
### 🚀 Features
- Add visitor data cache, remove random visitor data - ([b12f4c5](https://codeberg.org/ThetaDev/rustypipe/commit/b12f4c5d821a9189d7ed8410ad860824b6d052ef))
- Add support for rustypipe-botguard to get PO tokens - ([b90a252](https://codeberg.org/ThetaDev/rustypipe/commit/b90a252a5e1bf05a5294168b0ec16a73cbb88f42))
- Add session po token cache - ([b72b501](https://codeberg.org/ThetaDev/rustypipe/commit/b72b501b6dbcf4333b24cd80e7c8c61b0c21ec91))
- Check rustypipe-botguard-api version - ([8385b87](https://codeberg.org/ThetaDev/rustypipe/commit/8385b87c63677f32a240679a78702f53072e517a))
- Rewrite request attempt system, retry with different visitor data - ([dfd03ed](https://codeberg.org/ThetaDev/rustypipe/commit/dfd03edfadff2657e9cfbf04e5d313ba409520ac))
- Log failed player fetch attempts with player_from_clients - ([8e35358](https://codeberg.org/ThetaDev/rustypipe/commit/8e35358c8941301f6ebf7646a11ab22711082569))
- Add timezone query option - ([3a2370b](https://codeberg.org/ThetaDev/rustypipe/commit/3a2370b97ca3d0f40d72d66a23295557317d29fb))
- [**breaking**] Add userdata feature for all personal data queries (playback history, subscriptions) - ([65cb424](https://codeberg.org/ThetaDev/rustypipe/commit/65cb4244c6ab547f53d0cb12af802c4189188c86))
- Add RustyPipe::version_botguard fn, detect rustypipe-botguard in current dir, add botguard version to report - ([1d755b7](https://codeberg.org/ThetaDev/rustypipe/commit/1d755b76bf4569f7d0bb90a65494ac8e7aae499a))
### 🐛 Bug Fixes
- Parsing history dates - ([af7dc10](https://codeberg.org/ThetaDev/rustypipe/commit/af7dc1016322a87dd8fec0b739939c2b12b6f400))
- A/V streams incorrectly recognized as video-only - ([2b891ca](https://codeberg.org/ThetaDev/rustypipe/commit/2b891ca0788f91f16dbb9203191cb3d2092ecc74))
- Update iOS client - ([e915416](https://codeberg.org/ThetaDev/rustypipe/commit/e91541629d6c944c1001f5883e3c1264aeeb3969))
- A/B test 20: music continuation item renderer - ([9c67f8f](https://codeberg.org/ThetaDev/rustypipe/commit/9c67f8f85bef8214848dc9d17bff6cff252e015e))
- Include whole request body in report - ([15245c1](https://codeberg.org/ThetaDev/rustypipe/commit/15245c18b584e42523762b94fcc7284d483660a0))
- Extracting nsig fn when outside variable starts with $ - ([eda16e3](https://codeberg.org/ThetaDev/rustypipe/commit/eda16e378730a3b57c4982a626df1622a93c574a))
- Retry updating deobf data after a RustyPipe update - ([50ab1f7](https://codeberg.org/ThetaDev/rustypipe/commit/50ab1f7a5d8aeaa3720264b4a4b27805bb0e8121))
- Allow player data to be fetched without botguard - ([29c854b](https://codeberg.org/ThetaDev/rustypipe/commit/29c854b20d7a6677415b1744e7ba7ecd4f594ea5))
- Output full request body in reports, clean up `get_player_po_token` - ([a0d850f](https://codeberg.org/ThetaDev/rustypipe/commit/a0d850f8e01428a73bbd66397d0dbf797b45958f))
- Correct timezone offset for parsed dates, add timezone_local option - ([a5a7be5](https://codeberg.org/ThetaDev/rustypipe/commit/a5a7be5b4e0a0b73d7e1dc802ebd7bd48dafc76d))
- Use localzone crate to get local tz - ([5acbf0e](https://codeberg.org/ThetaDev/rustypipe/commit/5acbf0e456b1f10707e0a56125d993a8129eee3a))
- Only use cached potokens with min. 10min lifetime - ([0c94267](https://codeberg.org/ThetaDev/rustypipe/commit/0c94267d0371b2b26c7b5c9abfa156d5cde2153e))
### 📚 Documentation
- Add Botguard info to README - ([9957add](https://codeberg.org/ThetaDev/rustypipe/commit/9957add2b5d6391b2c1869d2019fd7dd91b8cd41))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate rquickjs to 0.9.0 (#33) - ([2c8ac41](https://codeberg.org/ThetaDev/rustypipe/commit/2c8ac410aa535d83f8bcc7181f81914b13bceb77))
## [v0.9.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.8.0..rustypipe/v0.9.0) - 2025-01-16
### 🚀 Features
- Add functions to fetch a user's history and subscriptions - ([14e3995](https://codeberg.org/ThetaDev/rustypipe/commit/14e399594f97a1228a8c2991a14dd8745af1beb7))
- Add history item dates, extend timeago parser - ([320a8c2](https://codeberg.org/ThetaDev/rustypipe/commit/320a8c2c24217ad5697f0424c4f994bbbe31f3aa))
- Add session headers when using cookie auth - ([3c95b52](https://codeberg.org/ThetaDev/rustypipe/commit/3c95b52ceaf0df2d67ee0d2f2ac658f666f29836))
- Add cookies.txt parser, add cookie auth + history cmds to CLI - ([cf498e4](https://codeberg.org/ThetaDev/rustypipe/commit/cf498e4a8f9318b0197bc3f0cbaf7043c53adb9d))
- Add method to get saved_playlists - ([27f64fc](https://codeberg.org/ThetaDev/rustypipe/commit/27f64fc412e833d5bd19ad72913aae19358e98b9))
- Extract player DRM data - ([2af4001](https://codeberg.org/ThetaDev/rustypipe/commit/2af4001c75f2ff4f7c891aa59ac22c2c6b7902a2))
- Add Dolby audio codecs (ac-3, ec-3) - ([a7f8c78](https://codeberg.org/ThetaDev/rustypipe/commit/a7f8c789b1a34710274c4630e027ef868397aea2))
- Add DRM and audio channel number filtering to StreamFilter - ([d5abee2](https://codeberg.org/ThetaDev/rustypipe/commit/d5abee275300ab1bc10fc8d6c35a4e3813fd2bd4))
- Set cache file permissions to 600 - ([dee8a99](https://codeberg.org/ThetaDev/rustypipe/commit/dee8a99e7a8d071c987709a01f02ee8fecf2d776))
### 🐛 Bug Fixes
- Dont leak authorization and cookie header in reports - ([75fce91](https://codeberg.org/ThetaDev/rustypipe/commit/75fce91353c02cd498f27d21b08261c23ea03d70))
- Require new time crate version which added Month::length - ([ec7a195](https://codeberg.org/ThetaDev/rustypipe/commit/ec7a195c98f39346c4c8db875212c3843580450e))
- Parsing numbers (it), dates (kn) - ([63f86b6](https://codeberg.org/ThetaDev/rustypipe/commit/63f86b6e186aa1d2dcaf7e9169ccebb2265e5905))
- Accept user-specific playlist ids (LL, WL) - ([97c3f30](https://codeberg.org/ThetaDev/rustypipe/commit/97c3f30d180d3e62b7e19f22d191d7fd7614daca))
- Only use auth-enabled clients for fetching player with auth option enabled - ([2b2b4af](https://codeberg.org/ThetaDev/rustypipe/commit/2b2b4af0b26cdd0d4bf2218d3f527abd88658abf))
- A/B test 19: Music artist album groups reordered - ([5daad1b](https://codeberg.org/ThetaDev/rustypipe/commit/5daad1b700e8dcf1f3e803db1685f08f27794898))
- Switch to rquickjs crate for deobfuscator - ([75c3746](https://codeberg.org/ThetaDev/rustypipe/commit/75c3746890f3428f3314b7b10c9ec816ad275836))
- Player_from_clients method not send/sync - ([9c512c3](https://codeberg.org/ThetaDev/rustypipe/commit/9c512c3c4dbec0fc3b973536733d61ba61125a92))
### 📚 Documentation
- Update README - ([0432477](https://codeberg.org/ThetaDev/rustypipe/commit/0432477451ecd5f64145d65239c721f4e44826c0))
- Fix README - ([11442df](https://codeberg.org/ThetaDev/rustypipe/commit/11442dfd369599396357f5b7a7a4268a7b537f57))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate rstest to 0.24.0 (#20) - ([ab19034](https://codeberg.org/ThetaDev/rustypipe/commit/ab19034ab19baf090e83eada056559676ffdadce))
- *(deps)* Update rust crate dirs to v6 (#24) - ([6a60425](https://codeberg.org/ThetaDev/rustypipe/commit/6a604252b1af7a9388db5dc170f737069cc31051))
- Update pre-commit hooks - ([7cd9246](https://codeberg.org/ThetaDev/rustypipe/commit/7cd9246260493d7839018cb39a2dfb4dded8b343))
## [v0.8.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.7.2..rustypipe/v0.8.0) - 2024-12-20
### 🚀 Features
- Log warning when generating report - ([258f18a](https://codeberg.org/ThetaDev/rustypipe/commit/258f18a99d848ae7e6808beddad054037a3b3799))
- Add auto-dubbed audio tracks, improved StreamFilter - ([1d1ae17](https://codeberg.org/ThetaDev/rustypipe/commit/1d1ae17ffc16724667d43142aa57abda2e6468e4))
### 🐛 Bug Fixes
- Replace deprecated call to `time::util::days_in_year_month` - ([69ef6ae](https://codeberg.org/ThetaDev/rustypipe/commit/69ef6ae51e9b09a9b9c06057e717bf6f054c9803))
- Nsig fn extra variable extraction - ([8014741](https://codeberg.org/ThetaDev/rustypipe/commit/80147413ee3190bb530f8f6b02738bcc787a6444))
- Deobf function extraction, allow $ in variable names - ([8cadbc1](https://codeberg.org/ThetaDev/rustypipe/commit/8cadbc1a4c865d085e30249dba0f353472456a32))
- Remove leading zero-width-space from comments, ensure space after links - ([162959c](https://codeberg.org/ThetaDev/rustypipe/commit/162959ca4513a03496776fae905b4bf20c79899c))
- Update client versions, enable Opus audio with iOS client - ([1b60c97](https://codeberg.org/ThetaDev/rustypipe/commit/1b60c97a183b9d74b92df14b5b113c61aba1be7f))
- Extract transcript from comment voice replies - ([30f60c3](https://codeberg.org/ThetaDev/rustypipe/commit/30f60c30f9d87d39585db93c1c9e274f48d688ba))
- Error 400 when fetching player with login - ([5ce84c4](https://codeberg.org/ThetaDev/rustypipe/commit/5ce84c44a6844f692258066c83e04df875e0aa91))
### ⚙️ Miscellaneous Tasks
- Update user agent - ([53e5846](https://codeberg.org/ThetaDev/rustypipe/commit/53e5846286e8db920622152c2a0a57ddc7c41d25))
## [v0.7.2](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.7.1..rustypipe/v0.7.2) - 2024-12-13
### 🐛 Bug Fixes
- Replace futures dependency with futures-util - ([5c39bf4](https://codeberg.org/ThetaDev/rustypipe/commit/5c39bf4842b13d37a4277ea5506e15c179892ce5))
- Lifetime-related lints - ([c4feff3](https://codeberg.org/ThetaDev/rustypipe/commit/c4feff37a5989097b575c43d89c26427d92d77b9))
- Limit retry attempts to fetch client versions and deobf data - ([44ae456](https://codeberg.org/ThetaDev/rustypipe/commit/44ae456d2c654679837da8ec44932c44b1b01195))
- Deobfuscation function extraction - ([f5437aa](https://codeberg.org/ThetaDev/rustypipe/commit/f5437aa127b2b7c5a08839643e30ea1ec989d30b))
## [v0.7.1](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.7.0..rustypipe/v0.7.1) - 2024-11-25
### 🐛 Bug Fixes
- Disable Android client - ([a846b72](https://codeberg.org/ThetaDev/rustypipe/commit/a846b729e3519e3d5e62bdf028d9b48a7f8ea2ce))
- A/B test 18: music playlist facepile avatar model - ([6c8108c](https://codeberg.org/ThetaDev/rustypipe/commit/6c8108c94acf9ca2336381bdca7c97b24a809521))
### ⚙️ Miscellaneous Tasks
- Add docs badge to README - ([706e881](https://codeberg.org/ThetaDev/rustypipe/commit/706e88134c0e94ce7d880735e9d31b3ff531a4f9))
## [v0.7.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.6.0..rustypipe/v0.7.0) - 2024-11-10
### 🚀 Features
- Allow searching for YTM users - ([50010b7](https://codeberg.org/ThetaDev/rustypipe/commit/50010b7b0856d3ce05fe7a9d5989e526089bc2ef))
- [**breaking**] Replace `TrackItem::is_video` attr with TrackType enum; serde lowercase AlbumType enum for consistency - ([044094a](https://codeberg.org/ThetaDev/rustypipe/commit/044094a4b70f05c46a459fa1597e23f4224b7b0b))
### 🐛 Bug Fixes
- Fetch unlocalized player data to interpret errors correctly; regression introduced with v0.6.0 - ([0919cbd](https://codeberg.org/ThetaDev/rustypipe/commit/0919cbd0dfe28ea00610c67a694e5f319e80635f))
- A/B test 17: channel playlists lockupViewModel - ([342119d](https://codeberg.org/ThetaDev/rustypipe/commit/342119dba6f3dc2152eef1fc9841264a9e56b9f0))
- [**breaking**] Serde: lowercase Verification enum - ([badb3ae](https://codeberg.org/ThetaDev/rustypipe/commit/badb3aef8249315909160b8ff73df3019f07cf97))
- Parsing videos using LockupViewModel (Music video recommendations) - ([870ff79](https://codeberg.org/ThetaDev/rustypipe/commit/870ff79ee07dfab1f4f2be3a401cd5320ed587da))
- Parsing lockup playlists with "MIX" instead of view count - ([ac8fbc3](https://codeberg.org/ThetaDev/rustypipe/commit/ac8fbc3e679819189e2791c323975acaf1b43035))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate thiserror to v2 (#16) - ([e1e1687](https://codeberg.org/ThetaDev/rustypipe/commit/e1e1687605603686ac5fd5deeb6aa8fecaf92494))
## [v0.6.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.5.0..rustypipe/v0.6.0) - 2024-10-28
### 🚀 Features
- [**breaking**] Remove TvHtml5Embed client as it got disabled - ([9e835c8](https://codeberg.org/ThetaDev/rustypipe/commit/9e835c8f38a3dd28c65561b2f9bb7a0f530c24f1))
- [**breaking**] Generate random visitorData, remove `RustyPipeQuery::get_context` and `YTContext<'a>` from public API - ([7c4f44d](https://codeberg.org/ThetaDev/rustypipe/commit/7c4f44d09c4d813efff9e7d1059ddacd226b9e9d))
- Add OAuth user login to access age-restricted videos - ([1cc3f9a](https://codeberg.org/ThetaDev/rustypipe/commit/1cc3f9ad74908d33e247ba6243103bfc22540164))
- Add user_auth_logout method - ([9e2fe61](https://codeberg.org/ThetaDev/rustypipe/commit/9e2fe61267846ce216e0c498d8fa9ee672e03cbf))
- Revoke OAuth token when logging out - ([62f8a92](https://codeberg.org/ThetaDev/rustypipe/commit/62f8a9210c23e1f02c711a2294af8766ca6b70e2))
### 🐛 Bug Fixes
- Skip serializing empty cache entries - ([be18d89](https://codeberg.org/ThetaDev/rustypipe/commit/be18d89ea65e35ddcf0f31bea3360e5db209fb9f))
- Fetch artist albums continuation - ([b589061](https://codeberg.org/ThetaDev/rustypipe/commit/b589061a40245637b4fe619a26892291d87d25e6))
- Update channel order tokens - ([79a6281](https://codeberg.org/ThetaDev/rustypipe/commit/79a62816ff62d94e5c706f45b1ce5971e5e58a81))
- Handle auth errors - ([512223f](https://codeberg.org/ThetaDev/rustypipe/commit/512223fd83fb1ba2ba7ad96ed050a70bb7ec294d))
- Use same visitor data for fetching artist album continuations - ([7b0499f](https://codeberg.org/ThetaDev/rustypipe/commit/7b0499f6b7cbf6ac4b83695adadfebb3f30349c7))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate fancy-regex to 0.14.0 (#14) - ([94194e0](https://codeberg.org/ThetaDev/rustypipe/commit/94194e019c46ca49c343086e80e8eb75c52f4bc6))
- *(deps)* Update rust crate quick-xml to 0.37.0 (#15) - ([0662b5c](https://codeberg.org/ThetaDev/rustypipe/commit/0662b5ccfccc922b28629f11ea52c3eb35f9efd2))
## [v0.5.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.4.0..rustypipe/v0.5.0) - 2024-10-13 ## [v0.5.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe/v0.4.0..rustypipe/v0.5.0) - 2024-10-13
### 🚀 Features ### 🚀 Features

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rustypipe" name = "rustypipe"
version = "0.10.0" version = "0.5.0"
rust-version = "1.67.1" rust-version = "1.67.1"
edition.workspace = true edition.workspace = true
authors.workspace = true authors.workspace = true
@ -24,11 +24,13 @@ keywords = ["youtube", "video", "music"]
categories = ["api-bindings", "multimedia"] categories = ["api-bindings", "multimedia"]
[workspace.dependencies] [workspace.dependencies]
rquickjs = "0.9.0" quick-js-dtp = { version = "0.4.1", default-features = false, features = [
"patch-dateparser",
] }
once_cell = "1.12.0" once_cell = "1.12.0"
regex = "1.6.0" regex = "1.6.0"
fancy-regex = "0.14.0" fancy-regex = "0.13.0"
thiserror = "2.0.0" thiserror = "1.0.0"
url = "2.2.0" url = "2.2.0"
reqwest = { version = "0.12.0", default-features = false } reqwest = { version = "0.12.0", default-features = false }
tokio = "1.20.4" tokio = "1.20.4"
@ -39,23 +41,20 @@ serde_with = { version = "3.0.0", default-features = false, features = [
"macros", "macros",
] } ] }
serde_plain = "1.0.0" serde_plain = "1.0.0"
sha1 = "0.10.0"
rand = "0.8.0" rand = "0.8.0"
time = { version = "0.3.37", features = [ time = { version = "0.3.10", features = [
"macros", "macros",
"serde-human-readable", "serde-human-readable",
"serde-well-known", "serde-well-known",
"local-offset",
] } ] }
futures-util = "0.3.31" futures = "0.3.21"
ress = "0.11.0" ress = "0.11.0"
phf = "0.11.0" phf = "0.11.0"
phf_codegen = "0.11.0" phf_codegen = "0.11.0"
data-encoding = "2.0.0" base64 = "0.22.0"
urlencoding = "2.1.0" urlencoding = "2.1.0"
quick-xml = { version = "0.37.0", features = ["serialize"] } quick-xml = { version = "0.36.0", features = ["serialize"] }
tracing = { version = "0.1.0", features = ["log"] } tracing = { version = "0.1.0", features = ["log"] }
localzone = "0.3.1"
# CLI # CLI
indicatif = "0.17.0" indicatif = "0.17.0"
@ -63,19 +62,19 @@ anyhow = "1.0"
clap = { version = "4.0.0", features = ["derive"] } clap = { version = "4.0.0", features = ["derive"] }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
serde_yaml = "0.9.0" serde_yaml = "0.9.0"
dirs = "6.0.0" dirs = "5.0.0"
filenamify = "0.1.0" filenamify = "0.1.0"
# Testing # Testing
rstest = "0.24.0" rstest = "0.23.0"
tokio-test = "0.4.2" tokio-test = "0.4.2"
insta = { version = "1.17.1", features = ["ron", "redactions"] } insta = { version = "1.17.1", features = ["ron", "redactions"] }
path_macro = "1.0.0" path_macro = "1.0.0"
tracing-test = "0.2.5" tracing-test = "0.2.5"
# Included crates # Included crates
rustypipe = { path = ".", version = "0.10.0", default-features = false } rustypipe = { path = ".", version = "0.5.0", default-features = false }
rustypipe-downloader = { path = "./downloader", version = "0.3.0", default-features = false, features = [ rustypipe-downloader = { path = "./downloader", version = "0.2.1", default-features = false, features = [
"indicatif", "indicatif",
"audiotag", "audiotag",
] } ] }
@ -83,8 +82,7 @@ rustypipe-downloader = { path = "./downloader", version = "0.3.0", default-featu
[features] [features]
default = ["default-tls"] default = ["default-tls"]
rss = ["dep:quick-xml"] rss = ["quick-xml"]
userdata = []
# Reqwest TLS options # Reqwest TLS options
default-tls = ["reqwest/default-tls"] default-tls = ["reqwest/default-tls"]
@ -95,27 +93,25 @@ rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"]
rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"]
[dependencies] [dependencies]
rquickjs.workspace = true quick-js-dtp.workspace = true
once_cell.workspace = true once_cell.workspace = true
regex.workspace = true regex.workspace = true
fancy-regex.workspace = true fancy-regex.workspace = true
thiserror.workspace = true thiserror.workspace = true
url.workspace = true url.workspace = true
reqwest = { workspace = true, features = ["json", "gzip", "brotli"] } reqwest = { workspace = true, features = ["json", "gzip", "brotli"] }
tokio = { workspace = true, features = ["macros", "time", "process"] } tokio = { workspace = true, features = ["macros", "time"] }
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
serde_with.workspace = true serde_with.workspace = true
serde_plain.workspace = true serde_plain.workspace = true
sha1.workspace = true
rand.workspace = true rand.workspace = true
time.workspace = true time.workspace = true
ress.workspace = true ress.workspace = true
phf.workspace = true phf.workspace = true
data-encoding.workspace = true base64.workspace = true
urlencoding.workspace = true urlencoding.workspace = true
tracing.workspace = true tracing.workspace = true
localzone.workspace = true
quick-xml = { workspace = true, optional = true } quick-xml = { workspace = true, optional = true }
[dev-dependencies] [dev-dependencies]
@ -127,6 +123,6 @@ tracing-test.workspace = true
[package.metadata.docs.rs] [package.metadata.docs.rs]
# To build locally: # To build locally:
# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features rss,userdata --no-deps --open # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features rss --no-deps --open
features = ["rss", "userdata"] features = ["rss"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]

View file

@ -1,26 +0,0 @@
## Development
**Requirements:**
- Current version of stable Rust
- [`just`](https://github.com/casey/just) task runner
- [`nextest`](https://nexte.st) test runner
- [`pre-commit`](https://pre-commit.com/)
- yq (YAML processor)
### Tasks
**Testing**
- `just test` Run unit+integration tests
- `just unittest` Run unit tests
- `just testyt` Run YouTube integration tests
- `just testintl` Run YouTube integration tests for all supported languages (this takes
a long time and is therefore not run in CI)
- `YT_LANG=de just testyt` Run YouTube integration tests for a specific language
**Tools**
- `just testfiles` Download missing testfiles for unit tests
- `just report2yaml` Convert RustyPipe reports into a more readable yaml format
(requires `yq`)

View file

@ -1,19 +1,15 @@
test: test:
# cargo test --features=rss,userdata # cargo test --features=rss
cargo nextest run --workspace --features=rss,userdata --no-fail-fast --retries 1 -- --skip 'user_data::' cargo nextest run --workspace --features=rss --no-fail-fast --failure-output final --retries 1
unittest: unittest:
cargo nextest run --features=rss,userdata --no-fail-fast --lib cargo nextest run --features=rss --no-fail-fast --failure-output final --lib
testyt: testyt:
cargo nextest run --features=rss,userdata --no-fail-fast --retries 1 --test youtube -- --skip 'user_data::' cargo nextest run --features=rss --no-fail-fast --failure-output final --retries 1 --test youtube
testyt-cookie:
cargo nextest run --features=rss,userdata --no-fail-fast --retries 1 --test youtube
testyt-localized: testyt-localized:
YT_LANG=th cargo nextest run --features=rss,userdata --no-fail-fast --retries 1 --test youtube -- \ YT_LANG=th cargo nextest run --features=rss --no-fail-fast --failure-output final --retries 1 --test youtube
--skip 'user_data::' --skip 'search_suggestion' --skip 'isrc_search_languages'
testintl: testintl:
#!/usr/bin/env bash #!/usr/bin/env bash
@ -32,8 +28,7 @@ testintl:
for YT_LANG in "${LANGUAGES[@]}"; do for YT_LANG in "${LANGUAGES[@]}"; do
echo "---TESTS FOR $YT_LANG ---" echo "---TESTS FOR $YT_LANG ---"
if YT_LANG="$YT_LANG" cargo nextest run --no-fail-fast --retries 1 --test-threads 4 --test youtube -- \ if YT_LANG="$YT_LANG" cargo nextest run --no-fail-fast --failure-output final --retries 1 --test-threads 4 --test youtube -E 'not test(/^resolve/)'; then
--skip 'user_data::' --skip 'search_suggestion' --skip 'isrc_search_languages' --skip 'resolve_'; then
echo "--- $YT_LANG COMPLETED ---" echo "--- $YT_LANG COMPLETED ---"
else else
echo "--- $YT_LANG FAILED ---" echo "--- $YT_LANG FAILED ---"

140
README.md
View file

@ -1,8 +1,7 @@
# ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) # ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg)
[![Current crates.io version](https://img.shields.io/crates/v/rustypipe.svg)](https://crates.io/crates/rustypipe) [![Current crates.io version](https://img.shields.io/crates/v/rustypipe.svg)](https://crates.io/crates/rustypipe)
[![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](https://opensource.org/licenses/GPL-3.0) [![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](http://opensource.org/licenses/GPL-3.0)
[![Docs](https://img.shields.io/docsrs/rustypipe/latest?style=flat)](https://docs.rs/rustypipe)
[![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml) [![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml)
RustyPipe is a fully featured Rust client for the public YouTube / YouTube Music API RustyPipe is a fully featured Rust client for the public YouTube / YouTube Music API
@ -21,8 +20,6 @@ RustyPipe is a fully featured Rust client for the public YouTube / YouTube Music
- **Search suggestions** - **Search suggestions**
- **Trending** - **Trending**
- **URL resolver** - **URL resolver**
- **Subscriptions**
- **Playback history**
### YouTube Music ### YouTube Music
@ -36,30 +33,9 @@ RustyPipe is a fully featured Rust client for the public YouTube / YouTube Music
- **Moods/Genres** - **Moods/Genres**
- **Charts** - **Charts**
- **New** (albums, music videos) - **New** (albums, music videos)
- **Saved items**
- **Playback history**
## Getting started ## Getting started
The RustyPipe library works as follows: at first you have to instantiate a RustyPipe
client. You can either create it with default options or use the `RustyPipe::builder()`
to customize it.
For fetching data you have to start with a new RustyPipe query object (`rp.query()`).
The query object holds options for an individual query (e.g. content language or
country). You can adjust these options with setter methods. Finally call your query
method to fetch the data you need.
All query methods are async, you need the tokio runtime to execute them.
```rust ignore
let rp = RustyPipe::new();
let rp = RustyPipe::builder().storage_dir("/app/data").build().unwrap();
let channel = rp.query().lang(Language::De).channel_videos("UCl2mFZoRqjw_ELax4Yisf6w").await.unwrap();
```
Here are a few examples to get you started:
### Cargo.toml ### Cargo.toml
```toml ```toml
@ -181,105 +157,29 @@ Subscribers: 1780000
... ...
``` ```
## Crate features ## Development
Some features of RustyPipe are gated behind features to avoid compiling unneeded **Requirements:**
dependencies.
- `rss` Fetch a channel's RSS feed, which is faster than fetching the channel page - Current version of stable Rust
- `userdata` Add functions to fetch YouTube user data (watch history, subscriptions, - [`just`](https://github.com/casey/just) task runner
music library) - [`nextest`](https://nexte.st) test runner
- [`pre-commit`](https://pre-commit.com/)
- yq (YAML processor)
You can also choose the TLS library used for making web requests using the same features ### Tasks
as the reqwest crate (`default-tls`, `native-tls`, `native-tls-alpn`,
`native-tls-vendored`, `rustls-tls-webpki-roots`, `rustls-tls-native-roots`).
## Cache storage **Testing**
The RustyPipe cache holds the current version numbers for all clients, the JavaScript - `just test` Run unit+integration tests
code used to deobfuscate video URLs and the authentication token/cookies. Never share - `just unittest` Run unit tests
the contents of the cache if you are using authentication. - `just testyt` Run YouTube integration tests
- `just testintl` Run YouTube integration tests for all supported languages (this takes
a long time and is therefore not run in CI)
- `YT_LANG=de just testyt` Run YouTube integration tests for a specific language
By default the cache is written to a JSON file named `rustypipe_cache.json` in the **Tools**
current working directory. This path can be changed with the `storage_dir` option of the
RustyPipeBuilder. The RustyPipe CLI stores its cache in the userdata folder. The full
path on Linux is `~/.local/share/rustypipe/rustypipe_cache.json`.
You can integrate your own cache storage backend (e.g. database storage) by implementing - `just testfiles` Download missing testfiles for unit tests
the `CacheStorage` trait. - `just report2yaml` Convert RustyPipe reports into a more readable yaml format
(requires `yq`)
## Reports
RustyPipe has a builtin error reporting system. If a YouTube response cannot be
deserialized or parsed, the original response data along with some request metadata is
written to a JSON file in the folder `rustypipe_reports`, located in RustyPipe's storage
directory (current folder by default, `~/.local/share/rustypipe` for the CLI).
When submitting a bug report to the RustyPipe project, you can share this report to help
resolve the issue.
RustyPipe reports come in 3 severity levels:
- DBG (no error occurred, report creation was enabled by the `RustyPipeQuery::report`
query option)
- WRN (parts of the response could not be deserialized/parsed, response data may be
incomplete)
- ERR (entire response could not be deserialized/parsed, RustyPipe returned an error)
## PO tokens
Since August 2024 YouTube requires PO tokens to access streams from web-based clients
(Desktop, Mobile). Otherwise streams will return a 403 error.
Generating PO tokens requires a simulated browser environment, which would be too large
to include in RustyPipe directly.
Therefore, the PO token generation is handled by a seperate CLI application
([rustypipe-botguard](https://codeberg.org/ThetaDev/rustypipe-botguard)) which is called
by the RustyPipe crate. RustyPipe automatically detects the rustypipe-botguard binary if
it is located in PATH or the current working directory. If your rustypipe-botguard
binary is located at a different path, you can specify it with the `.botguard_bin(path)`
option.
## Authentication
RustyPipe supports authenticating with your YouTube account to access
age-restricted/private videos and user information. There are 2 supported authentication
methods: OAuth and cookies.
To execute a query with authentication, use the `.authenticated()` query option. This
option is enabled by default for queries that always require authentication like
fetching user data. RustyPipe may automatically use authentication in case a video is
age-restricted or your IP address is banned by YouTube. If you never want to use
authentication, set the `.unauthenticated()` query option.
### OAuth
OAuth is the authentication method used by the YouTube TV client. It is more
user-friendly than extracting cookies, however it only works with the TV client. This
means that you can only fetch videos and not access any user data.
To login using OAuth, you first have to get a new device code using the
`rp.user_auth_get_code()` function. You can then enter the code on
<https://google.com/device> and log in with your Google account. After generating the
code, you can call the `rp.user_auth_wait_for_login()` function which waits until the
user has logged in and stores the authentication token in the cache.
### Cookies
Authenticating with cookies allows you to use the functionality of the YouTube/YouTube
Music Desktop client. You can fetch your subscribed channels, playlists and your music
collection. You can also fetch videos using the Desktop client, including private
videos, as long as you have access to them.
To authenticate with cookies you have to log into YouTube in a fresh browser session
(open Incognito/Private mode). Then extract the cookies from the developer tools or by
using browser plugins like "Get cookies.txt LOCALLY"
([Firefox](https://addons.mozilla.org/de/firefox/addon/get-cookies-txt-locally/))
([Chromium](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)).
Close the browser window after extracting the cookies to prevent YouTube from rotating
the cookies.
You can then add the cookies to your RustyPipe client using the `user_auth_set_cookie`
or `user_auth_set_cookie_txt` function. The cookies are stored in the cache file. To log
out, use the function `user_auth_remove_cookie`.

View file

@ -3,95 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [v0.7.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.6.0..rustypipe-cli/v0.7.0) - 2025-02-09
### 🚀 Features
- Add support for rustypipe-botguard to get PO tokens - ([b90a252](https://codeberg.org/ThetaDev/rustypipe/commit/b90a252a5e1bf05a5294168b0ec16a73cbb88f42))
- [**breaking**] Remove manual PO token options from downloader/cli, add new rustypipe-botguard options - ([cddb32f](https://codeberg.org/ThetaDev/rustypipe/commit/cddb32f190276265258c6ab45b3d43a8891c4b39))
- Add session po token cache - ([b72b501](https://codeberg.org/ThetaDev/rustypipe/commit/b72b501b6dbcf4333b24cd80e7c8c61b0c21ec91))
- Add timezone query option - ([3a2370b](https://codeberg.org/ThetaDev/rustypipe/commit/3a2370b97ca3d0f40d72d66a23295557317d29fb))
- Add --timezone-local CLI option - ([4f2bb47](https://codeberg.org/ThetaDev/rustypipe/commit/4f2bb47ab42ae0c68a64f3b3c2831fa7850b6f56))
- Add verbose flag - ([629b590](https://codeberg.org/ThetaDev/rustypipe/commit/629b5905da653c6fe0f3c6b5814dd2f49030e7ed))
### 🐛 Bug Fixes
- Parsing mixed-case language codes like zh-CN - ([9c73ed4](https://codeberg.org/ThetaDev/rustypipe/commit/9c73ed4b3008cb093c0fa7fd94fd9f1ba8cd3627))
### 🚜 Refactor
- [**breaking**] Add client_type field to DownloadError, rename cli option po-token-cache to pot-cache - ([594e675](https://codeberg.org/ThetaDev/rustypipe/commit/594e675b39efc5fbcdbd5e920a4d2cdee64f718e))
- Rename rustypipe-cli binary to rustypipe - ([c1a872e](https://codeberg.org/ThetaDev/rustypipe/commit/c1a872e1c14ea0956053bd7c65f6875b1cb3bc55))
### 📚 Documentation
- Add Botguard info to README - ([9957add](https://codeberg.org/ThetaDev/rustypipe/commit/9957add2b5d6391b2c1869d2019fd7dd91b8cd41))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rustypipe to 0.10.0
- *(deps)* Update rust crate rquickjs to 0.9.0 (#33) - ([2c8ac41](https://codeberg.org/ThetaDev/rustypipe/commit/2c8ac410aa535d83f8bcc7181f81914b13bceb77))
## [v0.6.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.5.0..rustypipe-cli/v0.6.0) - 2025-01-16
### 🚀 Features
- Add functions to fetch a user's history and subscriptions - ([14e3995](https://codeberg.org/ThetaDev/rustypipe/commit/14e399594f97a1228a8c2991a14dd8745af1beb7))
- Add history item dates, extend timeago parser - ([320a8c2](https://codeberg.org/ThetaDev/rustypipe/commit/320a8c2c24217ad5697f0424c4f994bbbe31f3aa))
- Add cookies.txt parser, add cookie auth + history cmds to CLI - ([cf498e4](https://codeberg.org/ThetaDev/rustypipe/commit/cf498e4a8f9318b0197bc3f0cbaf7043c53adb9d))
- Add CLI commands to fetch user library and YTM releases/charts - ([a1b43ad](https://codeberg.org/ThetaDev/rustypipe/commit/a1b43ad70a66cfcbaba8ef302ac8699f243e56e7))
- Export subscriptions as OPML / NewPipe JSON - ([c90d966](https://codeberg.org/ThetaDev/rustypipe/commit/c90d966b17eab24e957d980695888a459707055c))
### 📚 Documentation
- Update README - ([0432477](https://codeberg.org/ThetaDev/rustypipe/commit/0432477451ecd5f64145d65239c721f4e44826c0))
- Fix README - ([11442df](https://codeberg.org/ThetaDev/rustypipe/commit/11442dfd369599396357f5b7a7a4268a7b537f57))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate rstest to 0.24.0 (#20) - ([ab19034](https://codeberg.org/ThetaDev/rustypipe/commit/ab19034ab19baf090e83eada056559676ffdadce))
- *(deps)* Update rust crate dirs to v6 (#24) - ([6a60425](https://codeberg.org/ThetaDev/rustypipe/commit/6a604252b1af7a9388db5dc170f737069cc31051))
## [v0.5.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.4.0..rustypipe-cli/v0.5.0) - 2024-12-20
### 🚀 Features
- Get comment replies, rich text formatting - ([dceba44](https://codeberg.org/ThetaDev/rustypipe/commit/dceba442fe1a1d5d8d2a6d9422ff699593131f6d))
### 🐛 Bug Fixes
- Replace futures dependency with futures-util - ([5c39bf4](https://codeberg.org/ThetaDev/rustypipe/commit/5c39bf4842b13d37a4277ea5506e15c179892ce5))
- Error 400 when fetching player with login - ([5ce84c4](https://codeberg.org/ThetaDev/rustypipe/commit/5ce84c44a6844f692258066c83e04df875e0aa91))
### ⚙️ Miscellaneous Tasks
- Add docs badge to README - ([706e881](https://codeberg.org/ThetaDev/rustypipe/commit/706e88134c0e94ce7d880735e9d31b3ff531a4f9))
- *(deps)* Update rustypipe to 0.8.0
## [v0.4.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.3.0..rustypipe-cli/v0.4.0) - 2024-11-10
### 🚀 Features
- Allow searching for YTM users - ([50010b7](https://codeberg.org/ThetaDev/rustypipe/commit/50010b7b0856d3ce05fe7a9d5989e526089bc2ef))
- [**breaking**] Replace `TrackItem::is_video` attr with TrackType enum; serde lowercase AlbumType enum for consistency - ([044094a](https://codeberg.org/ThetaDev/rustypipe/commit/044094a4b70f05c46a459fa1597e23f4224b7b0b))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate thiserror to v2 (#16) - ([e1e1687](https://codeberg.org/ThetaDev/rustypipe/commit/e1e1687605603686ac5fd5deeb6aa8fecaf92494))
## [v0.3.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.2.2..rustypipe-cli/v0.3.0) - 2024-10-28
### 🚀 Features
- [**breaking**] Remove TvHtml5Embed client as it got disabled - ([9e835c8](https://codeberg.org/ThetaDev/rustypipe/commit/9e835c8f38a3dd28c65561b2f9bb7a0f530c24f1))
- Add OAuth user login to access age-restricted videos - ([1cc3f9a](https://codeberg.org/ThetaDev/rustypipe/commit/1cc3f9ad74908d33e247ba6243103bfc22540164))
- Revoke OAuth token when logging out - ([62f8a92](https://codeberg.org/ThetaDev/rustypipe/commit/62f8a9210c23e1f02c711a2294af8766ca6b70e2))
## [v0.2.2](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.2.1..rustypipe-cli/v0.2.2) - 2024-10-13 ## [v0.2.2](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-cli/v0.2.1..rustypipe-cli/v0.2.2) - 2024-10-13
### 🚀 Features ### 🚀 Features

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rustypipe-cli" name = "rustypipe-cli"
version = "0.7.0" version = "0.2.2"
rust-version = "1.70.0" rust-version = "1.70.0"
edition.workspace = true edition.workspace = true
authors.workspace = true authors.workspace = true
@ -12,7 +12,6 @@ description = "CLI for RustyPipe - download videos and extract data from YouTube
[features] [features]
default = ["native-tls"] default = ["native-tls"]
timezone = ["dep:time", "dep:time-tz"]
# Reqwest TLS options # Reqwest TLS options
native-tls = [ native-tls = [
@ -42,16 +41,13 @@ rustls-tls-native-roots = [
] ]
[dependencies] [dependencies]
rustypipe = { workspace = true, features = ["rss", "userdata"] } rustypipe = { workspace = true, features = ["rss"] }
rustypipe-downloader.workspace = true rustypipe-downloader.workspace = true
reqwest.workspace = true reqwest.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
futures-util.workspace = true futures.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
quick-xml.workspace = true
time = { workspace = true, optional = true }
time-tz = { version = "2.0.0", optional = true }
indicatif.workspace = true indicatif.workspace = true
anyhow.workspace = true anyhow.workspace = true
@ -64,7 +60,3 @@ dirs.workspace = true
anstream = "0.6.15" anstream = "0.6.15"
owo-colors = "4.0.0" owo-colors = "4.0.0"
const_format = "0.2.33" const_format = "0.2.33"
[[bin]]
name = "rustypipe"
path = "src/main.rs"

View file

@ -1,26 +1,14 @@
# ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) CLI # ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) CLI
[![Current crates.io version](https://img.shields.io/crates/v/rustypipe-cli.svg)](https://crates.io/crates/rustypipe-cli) [![Current crates.io version](https://img.shields.io/crates/v/rustypipe-cli.svg)](https://crates.io/crates/rustypipe-cli)
[![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](https://opensource.org/licenses/GPL-3.0) [![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](http://opensource.org/licenses/GPL-3.0)
[![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml) [![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml)
The RustyPipe CLI is a powerful YouTube client for the command line. It allows you to The RustyPipe CLI is a powerful YouTube client for the command line. It allows you to
access most of the features of the RustyPipe crate: getting data from YouTube and access most of the features of the RustyPipe crate: getting data from YouTube and
downloading videos. downloading videos.
## Installation The following subcommands are included:
You can download a compiled version of RustyPipe here:
<https://codeberg.org/ThetaDev/rustypipe/releases>
Alternatively, you can compile it yourself by installing [Rust](https://rustup.rs/) and
running `cargo install rustypipe-cli`.
To be able to access streams from web-based clients (Desktop, Mobile) you need to
download [rustypipe-botguard](https://codeberg.org/ThetaDev/rustypipe-botguard/releases)
and place the binary either in the PATH or the current working directory.
For downloading videos you also need to have ffmpeg installed.
## `get`: Fetch information ## `get`: Fetch information
@ -30,13 +18,13 @@ the associated metadata. It can fetch channels, playlists, albums and videos.
**Usage:** `rustypipe get UC2TXq_t06Hjdr2g_KdKpHQg` **Usage:** `rustypipe get UC2TXq_t06Hjdr2g_KdKpHQg`
- `-l`, `--limit` Limit the number of list items to fetch - `-l`, `--limit` Limit the number of list items to fetch
- `-t`, `--tab` Channel tab (options: **videos**, shorts, live, playlists, info) - ``-t, --tab` Channel tab (options: **videos**, shorts, live, playlists, info)
- `-m, --music` Use the YouTube Music API - `-m, --music` Use the YouTube Music API
- `--rss`Fetch the RSS feed of a channel - `--rss`Fetch the RSS feed of a channel
- `--comments` Get comments (options: top, latest) - `--comments` Get comments (options: top, latest)
- `--lyrics` Get the lyrics for YTM tracks - `--lyrics` Get the lyrics for YTM tracks
- `--player` Get the player data instead of the video details when fetching videos - `--player` Get the player data instead of the video details when fetching videos
- `-c`, `--client-type` YT clients used to fetch player data (options: desktop, tv, - `-c, --client-type` YT clients used to fetch player data (options: desktop, tv,
tv-embed, android, ios; if multiple clients are specified, they are attempted in tv-embed, android, ios; if multiple clients are specified, they are attempted in
order) order)
@ -59,7 +47,7 @@ when searching YTM or individual channels.
- `--date` Filter results by upload date (options: hour, day, week, month, year) - `--date` Filter results by upload date (options: hour, day, week, month, year)
- `--order` Sort search results (options: rating, date, views) - `--order` Sort search results (options: rating, date, views)
- `--channel` Channel ID for searching channel videos - `--channel` Channel ID for searching channel videos
- `-m`, `--music` Search YouTube Music in the given category (options: all, tracks, - `-m, --music` Search YouTube Music in the given category (options: all, tracks,
videos, artists, albums, playlists-ytm, playlists-community) videos, artists, albums, playlists-ytm, playlists-community)
## `dl`: Download videos ## `dl`: Download videos
@ -78,94 +66,26 @@ videos can be downloaded in parallel for improved performance.
- `-r`, `--resolution` Video resolution (e.g. 720, 1080). Set to 0 for audio-only - `-r`, `--resolution` Video resolution (e.g. 720, 1080). Set to 0 for audio-only
- `-a`, `--audio` Download only the audio track and write track metadata + album cover - `-a`, `--audio` Download only the audio track and write track metadata + album cover
- `-p`, `--parallel` Number of videos downloaded in parallel (default: 8) - `-p`, `--parallel` Number of videos downloaded in parallel (default: 8)
- `-m`, `--music` Use YouTube Music for downloading playlists - `-m, --music` Use YouTube Music for downloading playlists
- `-l`, `--limit` Limit the number of videos to download (default: 1000) - `-l`, `--limit` Limit the number of videos to download (default: 1000)
- `-c`, `--client-type` YT clients used to fetch player data (options: desktop, tv, - `-c`, `--client-type` YT clients used to fetch player data (options: desktop, tv,
tv-embed, android, ios; if multiple clients are specified, they are attempted in tv-embed, android, ios; if multiple clients are specified, they are attempted in
order) order)
- `--pot` token to circumvent bot detection
## `vdata`: Get visitor data ## `vdata`: Get visitor data
You can use the vdata command to get a new visitor data ID. This feature may come in You can use the vdata command to get a new visitor data cookie. This feature may come in
handy for testing and reproducing A/B tests. handy for testing and reproducing A/B tests.
## `releases` Get YouTube Music new releases
Get a list of new albums or music videos on YouTube Music
**Usage:** `rustypipe releases` or `rustypipe releases --videos`
## `charts`: Get YouTube Music charts
Get a list of the most popular tracks and artists for a given country
**Usage:** `rustypipe charts DE`
## `history`: Get YouTube playback history
Get a list of recently played videos or tracks
### Options
- `-l`, `--limit` Limit the number of list items to fetch
- `--search` Search the playback history (unavailable on YouTube Music)
- `-m`, `--music` Get the YouTube Music playback history
## `subscriptions`: Get subscribed channels
You can use the RustyPipe CLI to get a list of the channels you subscribed to. With the
`--format` flag you can export then in different formats, including OPML and NewPipe
JSON.
With the `--feed` option you can output a list of the latest videos from your
subscription feed instead.
### Options
- `-l`, `--limit` Limit the number of list items to fetch
- `-m`, `--music` Get a list of subscribed YouTube Music artists
- `--feed` Output YouTube Music subscription feed
## `playlists`, `albums`, `tracks`: Get your YouTube library
Fetch a list of all the items saved in your YouTube/YouTube Music profile.
### Options
- `-l`, `--limit` Limit the number of list items to fetch
- `-m`, `--music` (only for playlists): Get your YouTube Music playlists
## Global options ## Global options
- **Proxy:** RustyPipe respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY` - **Proxy:** RustyPipe respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY`
and `ALL_PROXY` and `ALL_PROXY`
- **Logging:** Enable debug logging with the `-v` (verbose) flag. If you want more - **Logging:** You can change the log level with the `RUST_LOG` environment variable, it
fine-grained control, use the `RUST_LOG` environment variable. is set to `info` by default
- **Visitor data:** A custom visitor data ID can be used with the `--vdata` flag - **Visitor data:** A custom visitor data cookie can be used with the `--vdata` flag
- **Authentication:** Use the commands `rustypipe login` and `rustypipe login --cookie` - `--report`
to log into your Google account using either OAuth or YouTube cookies. With the
`--auth` flag you can use authentication for any request.
- `--lang` Change the YouTube content language
- `--country` Change the YouTube content country
- `--tz` Use a specific
[timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (e.g.
Europe/Berlin, Australia/Sydney)
**Note:** this requires building rustypipe-cli with the `timezone` feature
- `--local-tz` Use the local timezone instead of UTC
- `--report` Generate a report on every request and store it in a `rustypipe_reports`
folder in the current directory
- `--cache-file` Change the RustyPipe cache file location (Default:
`~/.local/share/rustypipe/rustypipe_cache.json`)
- `--report-dir` Change the RustyPipe report directory location (Default:
`~/.local/share/rustypipe/rustypipe_reports`)
- `--botguard-bin` Use a
[rustypipe-botguard](https://codeberg.org/ThetaDev/rustypipe-botguard) binary from the
given path for generating PO tokens
- `--no-botguard` Disable Botguard, only download videos using clients that dont require
it
- `--pot-cache` Enable caching for session-bound PO tokens
### Output format ### Output format

File diff suppressed because it is too large Load diff

View file

@ -9,13 +9,12 @@ repository.workspace = true
publish = false publish = false
[dependencies] [dependencies]
rustypipe = { path = "../", features = ["userdata"] } rustypipe = { path = "../" }
reqwest.workspace = true reqwest.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread"] } tokio = { workspace = true, features = ["rt-multi-thread"] }
futures-util.workspace = true futures.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
serde_plain.workspace = true
serde_with.workspace = true serde_with.workspace = true
once_cell.workspace = true once_cell.workspace = true
regex.workspace = true regex.workspace = true

View file

@ -1,7 +1,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use futures_util::{stream, StreamExt}; use futures::{stream, StreamExt};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -36,16 +36,13 @@ pub enum ABTest {
CommentsFrameworkUpdate = 14, CommentsFrameworkUpdate = 14,
ChannelShortsLockup = 15, ChannelShortsLockup = 15,
PlaylistPageHeader = 16, PlaylistPageHeader = 16,
ChannelPlaylistsLockup = 17,
MusicPlaylistFacepile = 18,
MusicAlbumGroupsReordered = 19,
MusicContinuationItemRenderer = 20,
} }
/// List of active A/B tests that are run when none is manually specified /// List of active A/B tests that are run when none is manually specified
const TESTS_TO_RUN: &[ABTest] = &[ const TESTS_TO_RUN: [ABTest; 3] = [
ABTest::MusicAlbumGroupsReordered, ABTest::ChannelPageHeader,
ABTest::MusicContinuationItemRenderer, ABTest::MusicPlaylistTwoColumn,
ABTest::CommentsFrameworkUpdate,
]; ];
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -94,7 +91,7 @@ pub async fn run_test(
let rp = rp.clone(); let rp = rp.clone();
let pb = pb.clone(); let pb = pb.clone();
async move { async move {
let visitor_data = rp.query().get_visitor_data(true).await.unwrap(); let visitor_data = rp.query().get_visitor_data().await.unwrap();
let query = rp.query().visitor_data(&visitor_data); let query = rp.query().visitor_data(&visitor_data);
let is_present = match ab { let is_present = match ab {
ABTest::AttributedTextDescription => attributed_text_description(&query).await, ABTest::AttributedTextDescription => attributed_text_description(&query).await,
@ -115,12 +112,6 @@ pub async fn run_test(
ABTest::CommentsFrameworkUpdate => comments_framework_update(&query).await, ABTest::CommentsFrameworkUpdate => comments_framework_update(&query).await,
ABTest::ChannelShortsLockup => channel_shorts_lockup(&query).await, ABTest::ChannelShortsLockup => channel_shorts_lockup(&query).await,
ABTest::PlaylistPageHeader => playlist_page_header_renderer(&query).await, ABTest::PlaylistPageHeader => playlist_page_header_renderer(&query).await,
ABTest::ChannelPlaylistsLockup => channel_playlists_lockup(&query).await,
ABTest::MusicPlaylistFacepile => music_playlist_facepile(&query).await,
ABTest::MusicAlbumGroupsReordered => music_album_groups_reordered(&query).await,
ABTest::MusicContinuationItemRenderer => {
music_continuation_item_renderer(&query).await
}
} }
.unwrap(); .unwrap();
pb.inc(1); pb.inc(1);
@ -146,10 +137,10 @@ pub async fn run_all_tests(n: usize, concurrency: usize) -> Vec<ABTestRes> {
let mut results = Vec::new(); let mut results = Vec::new();
for ab in TESTS_TO_RUN { for ab in TESTS_TO_RUN {
let (occurrences, vd_present, vd_absent) = run_test(*ab, n, concurrency).await; let (occurrences, vd_present, vd_absent) = run_test(ab, n, concurrency).await;
results.push(ABTestRes { results.push(ABTestRes {
id: *ab as u16, id: ab as u16,
name: *ab, name: ab,
tests: n, tests: n,
occurrences, occurrences,
vd_present, vd_present,
@ -165,7 +156,7 @@ pub async fn attributed_text_description(rp: &RustyPipeQuery) -> Result<bool> {
content_check_ok: false, content_check_ok: false,
racy_check_ok: false, racy_check_ok: false,
}; };
let response_txt = rp.raw(ClientType::Desktop, "next", &q).await?; let response_txt = rp.raw(ClientType::Desktop, "next", &q).await.unwrap();
if !response_txt.contains("\"Black Mamba\"") { if !response_txt.contains("\"Black Mamba\"") {
bail!("invalid response data"); bail!("invalid response data");
@ -175,7 +166,7 @@ pub async fn attributed_text_description(rp: &RustyPipeQuery) -> Result<bool> {
} }
pub async fn three_tab_channel_layout(rp: &RustyPipeQuery) -> Result<bool> { pub async fn three_tab_channel_layout(rp: &RustyPipeQuery) -> Result<bool> {
let channel = rp.channel_videos("UCR-DXc1voovS8nhAvccRZhg").await?; let channel = rp.channel_videos("UCR-DXc1voovS8nhAvccRZhg").await.unwrap();
Ok(channel.has_live || channel.has_shorts) Ok(channel.has_live || channel.has_shorts)
} }
@ -242,7 +233,8 @@ pub async fn discography_page(rp: &RustyPipeQuery) -> Result<bool> {
params: None, params: None,
}, },
) )
.await?; .await
.unwrap();
Ok(res.contains(&format!("\"MPAD{id}\""))) Ok(res.contains(&format!("\"MPAD{id}\"")))
} }
@ -304,7 +296,8 @@ pub async fn channel_about_modal(rp: &RustyPipeQuery) -> Result<bool> {
params: None, params: None,
}, },
) )
.await?; .await
.unwrap();
Ok(!res.contains("\"EgVhYm91dPIGBAoCEgA%3D\"")) Ok(!res.contains("\"EgVhYm91dPIGBAoCEgA%3D\""))
} }
@ -341,7 +334,8 @@ pub async fn music_playlist_two_column(rp: &RustyPipeQuery) -> Result<bool> {
params: None, params: None,
}, },
) )
.await?; .await
.unwrap();
Ok(res.contains("\"musicResponsiveHeaderRenderer\"")) Ok(res.contains("\"musicResponsiveHeaderRenderer\""))
} }
@ -350,7 +344,8 @@ pub async fn comments_framework_update(rp: &RustyPipeQuery) -> Result<bool> {
"Eg0SC3dMZHBSN2d1S3k4GAYyJSIRIgt3TGRwUjdndUt5ODAAeAJCEGNvbW1lbnRzLXNlY3Rpb24%3D"; "Eg0SC3dMZHBSN2d1S3k4GAYyJSIRIgt3TGRwUjdndUt5ODAAeAJCEGNvbW1lbnRzLXNlY3Rpb24%3D";
let res = rp let res = rp
.raw(ClientType::Desktop, "next", &QCont { continuation }) .raw(ClientType::Desktop, "next", &QCont { continuation })
.await?; .await
.unwrap();
Ok(res.contains("\"frameworkUpdates\"")) Ok(res.contains("\"frameworkUpdates\""))
} }
@ -365,7 +360,8 @@ pub async fn channel_shorts_lockup(rp: &RustyPipeQuery) -> Result<bool> {
params: Some("EgZzaG9ydHPyBgUKA5oBAA%3D%3D"), params: Some("EgZzaG9ydHPyBgUKA5oBAA%3D%3D"),
}, },
) )
.await?; .await
.unwrap();
Ok(res.contains("\"shortsLockupViewModel\"")) Ok(res.contains("\"shortsLockupViewModel\""))
} }
@ -380,66 +376,7 @@ pub async fn playlist_page_header_renderer(rp: &RustyPipeQuery) -> Result<bool>
params: None, params: None,
}, },
) )
.await?; .await
.unwrap();
Ok(res.contains("\"pageHeaderRenderer\"")) Ok(res.contains("\"pageHeaderRenderer\""))
} }
pub async fn channel_playlists_lockup(rp: &RustyPipeQuery) -> Result<bool> {
let id = "UC2DjFE7Xf11URZqWBigcVOQ";
let res = rp
.raw(
ClientType::Desktop,
"browse",
&QBrowse {
browse_id: id,
params: Some("EglwbGF5bGlzdHMgAQ%3D%3D"),
},
)
.await?;
Ok(res.contains("\"lockupViewModel\""))
}
pub async fn music_playlist_facepile(rp: &RustyPipeQuery) -> Result<bool> {
let id = "VLPL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe";
let res = rp
.raw(
ClientType::DesktopMusic,
"browse",
&QBrowse {
browse_id: id,
params: None,
},
)
.await?;
Ok(res.contains("\"facepile\""))
}
pub async fn music_album_groups_reordered(rp: &RustyPipeQuery) -> Result<bool> {
let id = "UCOR4_bSVIXPsGa4BbCSt60Q";
let res = rp
.raw(
ClientType::DesktopMusic,
"browse",
&QBrowse {
browse_id: id,
params: None,
},
)
.await?;
Ok(res.contains("\"Singles & EPs\""))
}
pub async fn music_continuation_item_renderer(rp: &RustyPipeQuery) -> Result<bool> {
let id = "VLPLbZIPy20-1pN7mqjckepWF78ndb6ci_qi";
let res = rp
.raw(
ClientType::DesktopMusic,
"browse",
&QBrowse {
browse_id: id,
params: None,
},
)
.await?;
Ok(res.contains("\"continuationItemRenderer\""))
}

View file

@ -1,41 +1,28 @@
use std::{collections::BTreeMap, fs::File, io::BufReader}; use std::{collections::BTreeMap, fs::File, io::BufReader};
use futures_util::stream::{self, StreamExt}; use futures::stream::{self, StreamExt};
use path_macro::path; use path_macro::path;
use rustypipe::{ use rustypipe::{
client::{ClientType, RustyPipe, RustyPipeQuery}, client::{ClientType, RustyPipe, RustyPipeQuery},
model::AlbumType, model::AlbumType,
param::{Language, LANGUAGES}, param::{Language, LANGUAGES},
}; };
use serde::{Deserialize, Serialize}; use serde::Deserialize;
use serde_with::rust::deserialize_ignore_any;
use crate::{ use crate::{
model::{ContentsRenderer, QBrowse, SectionList, Tab, TextRuns}, model::{QBrowse, TextRuns},
util::{self, DICT_DIR}, util::{self, DICT_DIR},
}; };
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
enum AlbumTypeX {
Album,
Ep,
Single,
Audiobook,
Show,
AlbumRow,
SingleRow,
}
pub async fn collect_album_types(concurrency: usize) { pub async fn collect_album_types(concurrency: usize) {
let json_path = path!(*DICT_DIR / "album_type_samples.json"); let json_path = path!(*DICT_DIR / "album_type_samples.json");
let album_types = [ let album_types = [
(AlbumTypeX::Album, "MPREb_nlBWQROfvjo"), (AlbumType::Album, "MPREb_nlBWQROfvjo"),
(AlbumTypeX::Single, "MPREb_bHfHGoy7vuv"), (AlbumType::Single, "MPREb_bHfHGoy7vuv"),
(AlbumTypeX::Ep, "MPREb_u1I69lSAe5v"), (AlbumType::Ep, "MPREb_u1I69lSAe5v"),
(AlbumTypeX::Audiobook, "MPREb_gaoNzsQHedo"), (AlbumType::Audiobook, "MPREb_gaoNzsQHedo"),
(AlbumTypeX::Show, "MPREb_cwzk8EUwypZ"), (AlbumType::Show, "MPREb_cwzk8EUwypZ"),
]; ];
let rp = RustyPipe::new(); let rp = RustyPipe::new();
@ -45,7 +32,7 @@ pub async fn collect_album_types(concurrency: usize) {
let rp = rp.clone(); let rp = rp.clone();
async move { async move {
let query = rp.query().lang(lang); let query = rp.query().lang(lang);
let mut data: BTreeMap<AlbumTypeX, String> = BTreeMap::new(); let mut data: BTreeMap<AlbumType, String> = BTreeMap::new();
for (album_type, id) in album_types { for (album_type, id) in album_types {
let atype_txt = get_album_type(&query, id).await; let atype_txt = get_album_type(&query, id).await;
@ -53,22 +40,6 @@ pub async fn collect_album_types(concurrency: usize) {
data.insert(album_type, atype_txt); data.insert(album_type, atype_txt);
} }
let (albums_txt, singles_txt) = get_album_groups(&query).await;
println!(
"collected {}-{:?} ({})",
lang,
AlbumTypeX::AlbumRow,
&albums_txt
);
println!(
"collected {}-{:?} ({})",
lang,
AlbumTypeX::SingleRow,
&singles_txt
);
data.insert(AlbumTypeX::AlbumRow, albums_txt);
data.insert(AlbumTypeX::SingleRow, singles_txt);
(lang, data) (lang, data)
} }
}) })
@ -84,7 +55,7 @@ pub fn write_samples_to_dict() {
let json_path = path!(*DICT_DIR / "album_type_samples.json"); let json_path = path!(*DICT_DIR / "album_type_samples.json");
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();
let collected: BTreeMap<Language, BTreeMap<String, String>> = let collected: BTreeMap<Language, BTreeMap<AlbumType, String>> =
serde_json::from_reader(BufReader::new(json_file)).unwrap(); serde_json::from_reader(BufReader::new(json_file)).unwrap();
let mut dict = util::read_dict(); let mut dict = util::read_dict();
let langs = dict.keys().copied().collect::<Vec<_>>(); let langs = dict.keys().copied().collect::<Vec<_>>();
@ -96,12 +67,10 @@ pub fn write_samples_to_dict() {
e_langs.push(lang); e_langs.push(lang);
for lang in &e_langs { for lang in &e_langs {
collected.get(lang).unwrap().iter().for_each(|(t_str, v)| { collected.get(lang).unwrap().iter().for_each(|(t, v)| {
let t =
serde_plain::from_str::<AlbumType>(t_str.split('_').next().unwrap()).unwrap();
dict_entry dict_entry
.album_types .album_types
.insert(v.to_lowercase().trim().to_owned(), t); .insert(v.to_lowercase().trim().to_owned(), *t);
}); });
} }
} }
@ -111,19 +80,13 @@ pub fn write_samples_to_dict() {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct AlbumData { struct AlbumData {
contents: AlbumContents, header: Header,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct AlbumContents { struct Header {
two_column_browse_results_renderer: ContentsRenderer<Tab<SectionList<AlbumHeader>>>, music_detail_header_renderer: HeaderRenderer,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AlbumHeader {
music_responsive_header_renderer: HeaderRenderer,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -143,20 +106,8 @@ async fn get_album_type(query: &RustyPipeQuery, id: &str) -> String {
let album = serde_json::from_str::<AlbumData>(&response_txt).unwrap(); let album = serde_json::from_str::<AlbumData>(&response_txt).unwrap();
album album
.contents .header
.two_column_browse_results_renderer .music_detail_header_renderer
.contents
.into_iter()
.next()
.unwrap()
.tab_renderer
.content
.section_list_renderer
.contents
.into_iter()
.next()
.unwrap()
.music_responsive_header_renderer
.subtitle .subtitle
.runs .runs
.into_iter() .into_iter()
@ -164,84 +115,3 @@ async fn get_album_type(query: &RustyPipeQuery, id: &str) -> String {
.unwrap() .unwrap()
.text .text
} }
async fn get_album_groups(query: &RustyPipeQuery) -> (String, String) {
let body = QBrowse {
browse_id: "UCOR4_bSVIXPsGa4BbCSt60Q",
params: None,
};
let response_txt = query
.clone()
.visitor_data("CgtwbzJZcS1XZWc1QSjM2JG8BjIKCgJERRIEEgAgCw%3D%3D")
.raw(ClientType::DesktopMusic, "browse", &body)
.await
.unwrap();
let artist = serde_json::from_str::<ArtistData>(&response_txt).unwrap();
let sections = artist
.contents
.single_column_browse_results_renderer
.contents
.into_iter()
.next()
.map(|c| c.tab_renderer.content.section_list_renderer.contents)
.unwrap();
let titles = sections
.into_iter()
.filter_map(|s| {
if let ItemSection::MusicCarouselShelfRenderer(r) = s {
r.header
} else {
None
}
})
.map(|h| {
h.music_carousel_shelf_basic_header_renderer
.title
.runs
.into_iter()
.next()
.unwrap()
.text
})
.collect::<Vec<_>>();
assert!(titles.len() >= 2, "too few sections");
let mut titles_it = titles.into_iter();
(titles_it.next().unwrap(), titles_it.next().unwrap())
}
#[derive(Debug, Deserialize)]
struct ArtistData {
contents: ArtistDataContents,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ArtistDataContents {
single_column_browse_results_renderer: ContentsRenderer<Tab<SectionList<ItemSection>>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
enum ItemSection {
MusicCarouselShelfRenderer(MusicCarouselShelf),
#[serde(other, deserialize_with = "deserialize_ignore_any")]
None,
}
#[derive(Debug, Deserialize)]
struct MusicCarouselShelf {
header: Option<MusicCarouselShelfHeader>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct MusicCarouselShelfHeader {
music_carousel_shelf_basic_header_renderer: MusicCarouselShelfHeaderRenderer,
}
#[derive(Debug, Deserialize)]
struct MusicCarouselShelfHeaderRenderer {
title: TextRuns,
}

View file

@ -1,110 +0,0 @@
use std::{collections::BTreeMap, fs::File, io::BufReader};
use path_macro::path;
use rustypipe::{
client::RustyPipe,
param::{Language, LANGUAGES},
};
use crate::util::{self, DICT_DIR};
type CollectedDates = BTreeMap<Language, BTreeMap<String, String>>;
const THIS_WEEK: &str = "this_week";
const LAST_WEEK: &str = "last_week";
pub async fn collect_dates_music() {
let json_path = path!(*DICT_DIR / "history_date_samples.json");
let rp = RustyPipe::builder()
.storage_dir(path!(env!("CARGO_MANIFEST_DIR") / ".."))
.build()
.unwrap();
let mut res: CollectedDates = {
let json_file = File::open(&json_path).unwrap();
serde_json::from_reader(BufReader::new(json_file)).unwrap()
};
for lang in LANGUAGES {
println!("{lang}");
let history = rp.query().lang(lang).music_history().await.unwrap();
if history.items.len() < 3 {
panic!("{lang} empty history")
}
// The indexes have to be adapted before running
let entry = res.entry(lang).or_default();
entry.insert(
THIS_WEEK.to_owned(),
history.items[0].playback_date_txt.clone().unwrap(),
);
entry.insert(
LAST_WEEK.to_owned(),
history.items[18].playback_date_txt.clone().unwrap(),
);
}
let file = File::create(&json_path).unwrap();
serde_json::to_writer_pretty(file, &res).unwrap();
}
pub async fn collect_dates() {
let json_path = path!(*DICT_DIR / "history_date_samples.json");
let rp = RustyPipe::builder()
.storage_dir(path!(env!("CARGO_MANIFEST_DIR") / ".."))
.build()
.unwrap();
let mut res: CollectedDates = {
let json_file = File::open(&json_path).unwrap();
serde_json::from_reader(BufReader::new(json_file)).unwrap()
};
for lang in LANGUAGES {
println!("{lang}");
let history = rp.query().lang(lang).history().await.unwrap();
if history.items.len() < 3 {
panic!("{lang} empty history")
}
let entry = res.entry(lang).or_default();
entry.insert(
"tuesday".to_owned(),
history.items[0].playback_date_txt.clone().unwrap(),
);
entry.insert(
"0000-01-06".to_owned(),
history.items[1].playback_date_txt.clone().unwrap(),
);
entry.insert(
"2024-12-28".to_owned(),
history.items[15].playback_date_txt.clone().unwrap(),
);
}
let file = File::create(&json_path).unwrap();
serde_json::to_writer_pretty(file, &res).unwrap();
}
pub fn write_samples_to_dict() {
let json_path = path!(*DICT_DIR / "history_date_samples.json");
let json_file = File::open(json_path).unwrap();
let collected_dates: CollectedDates =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let mut dict = util::read_dict();
let langs = dict.keys().copied().collect::<Vec<_>>();
for lang in langs {
let dict_entry = dict.entry(lang).or_default();
let cd = &collected_dates[&lang];
dict_entry
.timeago_nd_tokens
.insert(util::filter_datestr(&cd[THIS_WEEK]), "0Wl".to_owned());
dict_entry
.timeago_nd_tokens
.insert(util::filter_datestr(&cd[LAST_WEEK]), "1Wl".to_owned());
}
util::write_dict(dict);
}

View file

@ -6,7 +6,7 @@ use std::{
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use futures_util::{stream, StreamExt}; use futures::{stream, StreamExt};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use path_macro::path; use path_macro::path;
use regex::Regex; use regex::Regex;

View file

@ -5,7 +5,7 @@ use std::{
io::BufReader, io::BufReader,
}; };
use futures_util::{stream, StreamExt}; use futures::{stream, StreamExt};
use ordered_hash_map::OrderedHashMap; use ordered_hash_map::OrderedHashMap;
use path_macro::path; use path_macro::path;
use rustypipe::{ use rustypipe::{

View file

@ -3,7 +3,7 @@ use std::{
fs::File, fs::File,
}; };
use futures_util::{stream, StreamExt}; use futures::{stream, StreamExt};
use path_macro::path; use path_macro::path;
use rustypipe::{ use rustypipe::{
client::{RustyPipe, RustyPipeQuery}, client::{RustyPipe, RustyPipeQuery},

View file

@ -5,7 +5,7 @@ use std::{
}; };
use anyhow::Result; use anyhow::Result;
use futures_util::{stream, StreamExt}; use futures::{stream, StreamExt};
use path_macro::path; use path_macro::path;
use rustypipe::{ use rustypipe::{
client::{ClientType, RustyPipe, RustyPipeQuery}, client::{ClientType, RustyPipe, RustyPipeQuery},

View file

@ -62,17 +62,6 @@ pub async fn download_testfiles() {
music_charts().await; music_charts().await;
music_genres().await; music_genres().await;
music_genre().await; music_genre().await;
// User data
history().await;
subscriptions().await;
subscription_feed().await;
music_history().await;
music_saved_artists().await;
music_saved_albums().await;
music_saved_tracks().await;
music_saved_playlists().await;
} }
const CLIENT_TYPES: [ClientType; 5] = [ const CLIENT_TYPES: [ClientType; 5] = [
@ -466,36 +455,6 @@ async fn trending() {
rp.query().trending().await.unwrap(); rp.query().trending().await.unwrap();
} }
async fn history() {
let json_path = path!(*TESTFILES_DIR / "userdata" / "history.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().history().await.unwrap();
}
async fn subscriptions() {
let json_path = path!(*TESTFILES_DIR / "userdata" / "subscriptions.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().subscriptions().await.unwrap();
}
async fn subscription_feed() {
let json_path = path!(*TESTFILES_DIR / "userdata" / "subscription_feed.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().subscription_feed().await.unwrap();
}
async fn music_playlist() { async fn music_playlist() {
for (name, id) in [ for (name, id) in [
("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"), ("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"),
@ -817,53 +776,3 @@ async fn music_genre() {
rp.query().music_genre(id).await.unwrap(); rp.query().music_genre(id).await.unwrap();
} }
} }
async fn music_history() {
let json_path = path!(*TESTFILES_DIR / "music_userdata" / "music_history.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().music_history().await.unwrap();
}
async fn music_saved_artists() {
let json_path = path!(*TESTFILES_DIR / "music_userdata" / "saved_artists.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().music_saved_artists().await.unwrap();
}
async fn music_saved_albums() {
let json_path = path!(*TESTFILES_DIR / "music_userdata" / "saved_albums.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().music_saved_albums().await.unwrap();
}
async fn music_saved_tracks() {
let json_path = path!(*TESTFILES_DIR / "music_userdata" / "saved_tracks.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().music_saved_tracks().await.unwrap();
}
async fn music_saved_playlists() {
let json_path = path!(*TESTFILES_DIR / "music_userdata" / "saved_playlists.json");
if json_path.exists() {
return;
}
let rp = rp_testfile(&json_path);
rp.query().music_saved_playlists().await.unwrap();
}

View file

@ -10,7 +10,7 @@ use crate::{
}; };
fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) { fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
static TU_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d*)(\w*)$").unwrap()); static TU_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d*)(\w?)$").unwrap());
match TU_PATTERN.captures(tu) { match TU_PATTERN.captures(tu) {
Some(cap) => ( Some(cap) => (
cap.get(1).unwrap().as_str().parse().unwrap_or(1), cap.get(1).unwrap().as_str().parse().unwrap_or(1),
@ -22,8 +22,6 @@ fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
"W" => Some(TimeUnit::Week), "W" => Some(TimeUnit::Week),
"M" => Some(TimeUnit::Month), "M" => Some(TimeUnit::Month),
"Y" => Some(TimeUnit::Year), "Y" => Some(TimeUnit::Year),
"Wl" => Some(TimeUnit::LastWeek),
"Wd" => Some(TimeUnit::LastWeekday),
"" => None, "" => None,
_ => panic!("invalid time unit: {tu}"), _ => panic!("invalid time unit: {tu}"),
}, },
@ -45,7 +43,7 @@ pub fn generate_dictionary() {
use crate::{ use crate::{
model::AlbumType, model::AlbumType,
param::Language, param::Language,
util::timeago::{TaToken, TimeUnit}, util::timeago::{DateCmp, TaToken, TimeUnit},
}; };
/// Dictionary entry containing language-specific parsing information /// Dictionary entry containing language-specific parsing information
@ -57,13 +55,14 @@ pub(crate) struct Entry {
/// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay), /// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay),
/// `h`(our), `m`(inute), `s`(econd) /// `h`(our), `m`(inute), `s`(econd)
pub timeago_tokens: phf::Map<&'static str, TaToken>, pub timeago_tokens: phf::Map<&'static str, TaToken>,
/// True if the month has to be parsed before the day /// Order in which to parse numeric date components. Formatted as
/// a string of date identifiers (Y, M, D).
/// ///
/// Examples: /// Examples:
/// ///
/// - 03.01.2020 => DMY => false /// - 03.01.2020 => `"DMY"`
/// - 01/03/2020 => MDY => true /// - Jan 3, 2020 => `"DY"`
pub month_before_day: bool, pub date_order: &'static [DateCmp],
/// Tokens for parsing month names. /// Tokens for parsing month names.
/// ///
/// Format: Parsed token -> Month number (starting from 1) /// Format: Parsed token -> Month number (starting from 1)
@ -138,6 +137,13 @@ pub(crate) fn entry(lang: Language) -> Entry {
}; };
}); });
// Date order
let mut date_order = "&[".to_owned();
entry.date_order.chars().for_each(|c| {
write!(date_order, "DateCmp::{c}, ").unwrap();
});
date_order = date_order.trim_end_matches([' ', ',']).to_owned() + "]";
// Number tokens // Number tokens
let mut number_tokens = phf_codegen::Map::<&str>::new(); let mut number_tokens = phf_codegen::Map::<&str>::new();
entry.number_tokens.iter().for_each(|(txt, mag)| { entry.number_tokens.iter().for_each(|(txt, mag)| {
@ -178,8 +184,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
.to_string() .to_string()
.replace('\n', "\n "); .replace('\n', "\n ");
write!(code_timeago_tokens, "{} => Entry {{\n timeago_tokens: {},\n month_before_day: {:?},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n number_nd_tokens: {},\n album_types: {},\n chan_prefix: {:?},\n chan_suffix: {:?},\n }},\n ", write!(code_timeago_tokens, "{} => Entry {{\n timeago_tokens: {},\n date_order: {},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n number_nd_tokens: {},\n album_types: {},\n chan_prefix: {:?},\n chan_suffix: {:?},\n }},\n ",
selector, code_ta_tokens, entry.month_before_day, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_number_nd_tokens, code_album_types, entry.chan_prefix, entry.chan_suffix).unwrap(); selector, code_ta_tokens, date_order, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_number_nd_tokens, code_album_types, entry.chan_prefix, entry.chan_suffix).unwrap();
} }
code_timeago_tokens = code_timeago_tokens.trim_end().to_owned() + "\n }\n}\n"; code_timeago_tokens = code_timeago_tokens.trim_end().to_owned() + "\n }\n}\n";

View file

@ -3,7 +3,6 @@
mod abtest; mod abtest;
mod collect_album_types; mod collect_album_types;
mod collect_chan_prefixes; mod collect_chan_prefixes;
mod collect_history_dates;
mod collect_large_numbers; mod collect_large_numbers;
mod collect_playlist_dates; mod collect_playlist_dates;
mod collect_video_dates; mod collect_video_dates;
@ -31,11 +30,8 @@ enum Commands {
CollectAlbumTypes, CollectAlbumTypes,
CollectVideoDurations, CollectVideoDurations,
CollectVideoDates, CollectVideoDates,
CollectHistoryDates,
CollectMusicHistoryDates,
CollectChanPrefixes, CollectChanPrefixes,
ParsePlaylistDates, ParsePlaylistDates,
ParseHistoryDates,
ParseLargeNumbers, ParseLargeNumbers,
ParseAlbumTypes, ParseAlbumTypes,
ParseVideoDurations, ParseVideoDurations,
@ -72,17 +68,10 @@ async fn main() {
Commands::CollectVideoDates => { Commands::CollectVideoDates => {
collect_video_dates::collect_video_dates(cli.concurrency).await; collect_video_dates::collect_video_dates(cli.concurrency).await;
} }
Commands::CollectHistoryDates => {
collect_history_dates::collect_dates().await;
}
Commands::CollectMusicHistoryDates => {
collect_history_dates::collect_dates_music().await;
}
Commands::CollectChanPrefixes => { Commands::CollectChanPrefixes => {
collect_chan_prefixes::collect_chan_prefixes().await; collect_chan_prefixes::collect_chan_prefixes().await;
} }
Commands::ParsePlaylistDates => collect_playlist_dates::write_samples_to_dict(), Commands::ParsePlaylistDates => collect_playlist_dates::write_samples_to_dict(),
Commands::ParseHistoryDates => collect_history_dates::write_samples_to_dict(),
Commands::ParseLargeNumbers => collect_large_numbers::write_samples_to_dict(), Commands::ParseLargeNumbers => collect_large_numbers::write_samples_to_dict(),
Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(), Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(),
Commands::ParseVideoDurations => collect_video_durations::parse_video_durations(), Commands::ParseVideoDurations => collect_video_durations::parse_video_durations(),

View file

@ -13,13 +13,6 @@ pub struct DictEntry {
/// Should the language be parsed by character instead of by word? /// Should the language be parsed by character instead of by word?
/// (e.g. Chinese/Japanese) /// (e.g. Chinese/Japanese)
pub by_char: bool, pub by_char: bool,
/// True if the month has to be parsed before the day
///
/// Examples:
///
/// - 03.01.2020 => DMY => false
/// - 01/03/2020 => MDY => true
pub month_before_day: bool,
/// Tokens for parsing timeago strings. /// Tokens for parsing timeago strings.
/// ///
/// Format: Parsed token -> \[Quantity\] Identifier /// Format: Parsed token -> \[Quantity\] Identifier
@ -95,8 +88,6 @@ pub enum TimeUnit {
Week, Week,
Month, Month,
Year, Year,
LastWeek,
LastWeekday,
} }
impl TimeUnit { impl TimeUnit {
@ -109,8 +100,6 @@ impl TimeUnit {
TimeUnit::Week => "W", TimeUnit::Week => "W",
TimeUnit::Month => "M", TimeUnit::Month => "M",
TimeUnit::Year => "Y", TimeUnit::Year => "Y",
TimeUnit::LastWeek => "Wl",
TimeUnit::LastWeekday => "Wd",
} }
} }
} }
@ -152,7 +141,7 @@ pub struct Text {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Channel { pub struct Channel {
pub contents: TwoColumnBrowseResults, pub contents: Contents,
pub header: ChannelHeader, pub header: ChannelHeader,
} }
@ -170,7 +159,7 @@ pub struct HeaderRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TwoColumnBrowseResults { pub struct Contents {
pub two_column_browse_results_renderer: TabsRenderer, pub two_column_browse_results_renderer: TabsRenderer,
} }
@ -179,37 +168,24 @@ pub struct TwoColumnBrowseResults {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TabsRenderer { pub struct TabsRenderer {
#[serde_as(as = "VecSkipError<_>")] #[serde_as(as = "VecSkipError<_>")]
pub tabs: Vec<Tab<RichGrid>>, pub tabs: Vec<TabRendererWrap>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ContentsRenderer<T> { pub struct TabRendererWrap {
#[serde(alias = "tabs")] pub tab_renderer: TabRenderer,
pub contents: Vec<T>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Tab<T> { pub struct TabRenderer {
pub tab_renderer: TabRenderer<T>, pub content: RichGridRendererWrap,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TabRenderer<T> { pub struct RichGridRendererWrap {
pub content: T,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SectionList<T> {
pub section_list_renderer: ContentsRenderer<T>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RichGrid {
pub rich_grid_renderer: RichGridRenderer, pub rich_grid_renderer: RichGridRenderer,
} }

View file

@ -77,7 +77,7 @@ pub fn filter_datestr(string: &str) -> String {
.to_lowercase() .to_lowercase()
.chars() .chars()
.filter_map(|c| { .filter_map(|c| {
if matches!(c, '\u{200b}' | '.' | ',') || c.is_ascii_digit() { if c == '\u{200b}' || c.is_ascii_digit() {
None None
} else if c == '-' { } else if c == '-' {
Some(' ') Some(' ')

View file

@ -3,93 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [v0.3.0](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.7..rustypipe-downloader/v0.3.0) - 2025-02-09
### 🚀 Features
- [**breaking**] Remove manual PO token options from downloader in favor of rustypipe-botguard - ([cddb32f](https://codeberg.org/ThetaDev/rustypipe/commit/cddb32f190276265258c6ab45b3d43a8891c4b39))
### 🐛 Bug Fixes
- Ensure downloader futures are send - ([812ff4c](https://codeberg.org/ThetaDev/rustypipe/commit/812ff4c5bafffc5708a6d5066f1ebadb6d9fc958))
- Download audio with dolby codec - ([9234005](https://codeberg.org/ThetaDev/rustypipe/commit/92340056f868007beccb64e9e26eb39abc40f7aa))
### 🚜 Refactor
- [**breaking**] Add client_type field to DownloadError, rename cli option po-token-cache to pot-cache - ([594e675](https://codeberg.org/ThetaDev/rustypipe/commit/594e675b39efc5fbcdbd5e920a4d2cdee64f718e))
### 📚 Documentation
- Add Botguard info to README - ([9957add](https://codeberg.org/ThetaDev/rustypipe/commit/9957add2b5d6391b2c1869d2019fd7dd91b8cd41))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rustypipe to 0.10.0
## [v0.2.7](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.6..rustypipe-downloader/v0.2.7) - 2025-01-16
### 🚀 Features
- Extract player DRM data - ([2af4001](https://codeberg.org/ThetaDev/rustypipe/commit/2af4001c75f2ff4f7c891aa59ac22c2c6b7902a2))
- Prefer maxresdefault.jpg thumbnail if available - ([a8e97f4](https://codeberg.org/ThetaDev/rustypipe/commit/a8e97f411a1e769e52d8cbde11f0a4ca1535f7ef))
- Add DRM and audio channel number filtering to StreamFilter - ([d5abee2](https://codeberg.org/ThetaDev/rustypipe/commit/d5abee275300ab1bc10fc8d6c35a4e3813fd2bd4))
### 🐛 Bug Fixes
- Remove Unix file metadata usage (Windows compatibility) - ([5c6d992](https://codeberg.org/ThetaDev/rustypipe/commit/5c6d992939f55a203ac1784f1e9175ac1d498ce8))
### 📚 Documentation
- Update README - ([0432477](https://codeberg.org/ThetaDev/rustypipe/commit/0432477451ecd5f64145d65239c721f4e44826c0))
- Fix README - ([11442df](https://codeberg.org/ThetaDev/rustypipe/commit/11442dfd369599396357f5b7a7a4268a7b537f57))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rustypipe to 0.9.0
- *(deps)* Update rust crate rstest to 0.24.0 (#20) - ([ab19034](https://codeberg.org/ThetaDev/rustypipe/commit/ab19034ab19baf090e83eada056559676ffdadce))
- *(deps)* Update rust crate lofty to 0.22.0 - ([addeb82](https://codeberg.org/ThetaDev/rustypipe/commit/addeb821101aa968b95455604bc13bd24f50328f))
- *(deps)* Update rust crate dirs to v6 (#24) - ([6a60425](https://codeberg.org/ThetaDev/rustypipe/commit/6a604252b1af7a9388db5dc170f737069cc31051))
## [v0.2.6](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.5..rustypipe-downloader/v0.2.6) - 2024-12-20
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rustypipe to 0.8.0
## [v0.2.5](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.4..rustypipe-downloader/v0.2.5) - 2024-12-13
### 🐛 Bug Fixes
- Replace futures dependency with futures-util - ([5c39bf4](https://codeberg.org/ThetaDev/rustypipe/commit/5c39bf4842b13d37a4277ea5506e15c179892ce5))
- Remove empty tempfile after unsuccessful download - ([5262bec](https://codeberg.org/ThetaDev/rustypipe/commit/5262becca1e9e3e8262833764ef18c23bc401172))
### ⚙️ Miscellaneous Tasks
- Add docs badge to README - ([706e881](https://codeberg.org/ThetaDev/rustypipe/commit/706e88134c0e94ce7d880735e9d31b3ff531a4f9))
## [v0.2.4](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.3..rustypipe-downloader/v0.2.4) - 2024-11-10
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rust crate thiserror to v2 (#16) - ([e1e1687](https://codeberg.org/ThetaDev/rustypipe/commit/e1e1687605603686ac5fd5deeb6aa8fecaf92494))
- *(deps)* Update rustypipe to 0.7.0
## [v0.2.3](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.2..rustypipe-downloader/v0.2.3) - 2024-10-28
### 🐛 Bug Fixes
- Remove unnecessary image.rs dependencies - ([1b08166](https://codeberg.org/ThetaDev/rustypipe/commit/1b08166399cccb8394d2fdd82d54162c1a9e01be))
### ⚙️ Miscellaneous Tasks
- *(deps)* Update rustypipe to 0.6.0
## [v0.2.2](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.1..rustypipe-downloader/v0.2.2) - 2024-10-13 ## [v0.2.2](https://codeberg.org/ThetaDev/rustypipe/compare/rustypipe-downloader/v0.2.1..rustypipe-downloader/v0.2.2) - 2024-10-13
### ⚙️ Miscellaneous Tasks ### ⚙️ Miscellaneous Tasks

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rustypipe-downloader" name = "rustypipe-downloader"
version = "0.3.0" version = "0.2.2"
rust-version = "1.67.1" rust-version = "1.67.1"
edition.workspace = true edition.workspace = true
authors.workspace = true authors.workspace = true
@ -37,7 +37,7 @@ rustypipe.workspace = true
once_cell.workspace = true once_cell.workspace = true
regex.workspace = true regex.workspace = true
thiserror.workspace = true thiserror.workspace = true
futures-util.workspace = true futures.workspace = true
reqwest = { workspace = true, features = ["stream"] } reqwest = { workspace = true, features = ["stream"] }
rand.workspace = true rand.workspace = true
tokio = { workspace = true, features = ["macros", "fs", "process"] } tokio = { workspace = true, features = ["macros", "fs", "process"] }
@ -45,13 +45,9 @@ indicatif = { workspace = true, optional = true }
filenamify.workspace = true filenamify.workspace = true
tracing.workspace = true tracing.workspace = true
time.workspace = true time.workspace = true
lofty = { version = "0.22.0", optional = true } lofty = { version = "0.21.0", optional = true }
image = { version = "0.25.0", optional = true, default-features = false, features = [ image = { version = "0.25.0", optional = true }
"rayon", smartcrop2 = { version = "0.3.0", optional = true }
"jpeg",
"webp",
] }
smartcrop2 = { version = "0.3.1", optional = true }
[dev-dependencies] [dev-dependencies]
path_macro.workspace = true path_macro.workspace = true

View file

@ -1,8 +1,7 @@
# ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) Downloader # ![RustyPipe](https://codeberg.org/ThetaDev/rustypipe/raw/branch/main/notes/logo.svg) Downloader
[![Current crates.io version](https://img.shields.io/crates/v/rustypipe-downloader.svg)](https://crates.io/crates/rustypipe-downloader) [![Current crates.io version](https://img.shields.io/crates/v/rustypipe-downloader.svg)](https://crates.io/crates/rustypipe-downloader)
[![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](https://opensource.org/licenses/GPL-3.0) [![License](https://img.shields.io/badge/License-GPL--3-blue.svg?style=flat)](http://opensource.org/licenses/GPL-3.0)
[![Docs](https://img.shields.io/docsrs/rustypipe-downloader/latest?style=flat)](https://docs.rs/rustypipe-downloader)
[![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml) [![CI status](https://codeberg.org/ThetaDev/rustypipe/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/rustypipe/actions/?workflow=ci.yaml)
The downloader is a companion crate for RustyPipe that allows for easy and fast The downloader is a companion crate for RustyPipe that allows for easy and fast

View file

@ -13,13 +13,8 @@ pub enum DownloadError {
#[error("http error: {0}")] #[error("http error: {0}")]
Http(#[from] reqwest::Error), Http(#[from] reqwest::Error),
/// 403 error trying to download video /// 403 error trying to download video
#[error("YouTube returned 403 error; visitor_data={}", .visitor_data.as_deref().unwrap_or_default())] #[error("YouTube returned 403 error")]
Forbidden { Forbidden(ClientType),
/// Client type used to fetch the failed stream
client_type: ClientType,
/// Visitor data used to fetch the failed stream
visitor_data: Option<String>,
},
/// File IO error /// File IO error
#[error(transparent)] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
@ -30,8 +25,8 @@ pub enum DownloadError {
#[error("Progressive download error: {0}")] #[error("Progressive download error: {0}")]
Progressive(Cow<'static, str>), Progressive(Cow<'static, str>),
/// Video could not be downloaded because of invalid player data /// Video could not be downloaded because of invalid player data
#[error("source error: {0}")] #[error("input error: {0}")]
Source(Cow<'static, str>), Input(Cow<'static, str>),
/// Download target already exists /// Download target already exists
#[error("file {0} already exists")] #[error("file {0} already exists")]
Exists(PathBuf), Exists(PathBuf),

View file

@ -15,13 +15,13 @@ use std::{
time::Duration, time::Duration,
}; };
use futures_util::stream::{self, StreamExt, TryStreamExt}; use futures::stream::{self, StreamExt};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::Rng; use rand::Rng;
use regex::Regex; use regex::Regex;
use reqwest::{header, Client, StatusCode, Url}; use reqwest::{header, Client, StatusCode, Url};
use rustypipe::{ use rustypipe::{
client::{ClientType, RustyPipe}, client::{ClientType, RustyPipe, DEFAULT_PLAYER_CLIENT_ORDER},
model::{ model::{
traits::{FileFormat, YtEntity}, traits::{FileFormat, YtEntity},
AudioCodec, TrackItem, VideoCodec, VideoPlayer, AudioCodec, TrackItem, VideoCodec, VideoPlayer,
@ -77,6 +77,7 @@ pub struct DownloaderBuilder {
#[cfg(feature = "audiotag")] #[cfg(feature = "audiotag")]
crop_cover: bool, crop_cover: bool,
client_types: Option<Vec<ClientType>>, client_types: Option<Vec<ClientType>>,
pot: Option<String>,
} }
struct DownloaderInner { struct DownloaderInner {
@ -108,6 +109,8 @@ struct DownloaderInner {
crop_cover: bool, crop_cover: bool,
/// Client types for fetching videos /// Client types for fetching videos
client_types: Option<Vec<ClientType>>, client_types: Option<Vec<ClientType>>,
/// Pot token to circumvent bot detection
pot: Option<String>,
} }
/// Download query /// Download query
@ -127,6 +130,8 @@ pub struct DownloadQuery {
video_format: Option<DownloadVideoFormat>, video_format: Option<DownloadVideoFormat>,
/// Client types for fetching videos /// Client types for fetching videos
client_types: Option<Vec<ClientType>>, client_types: Option<Vec<ClientType>>,
/// Pot token to circumvent bot detection
pot: Option<String>,
} }
/// Video to be downloaded /// Video to be downloaded
@ -293,6 +298,7 @@ impl Default for DownloaderBuilder {
#[cfg(feature = "audiotag")] #[cfg(feature = "audiotag")]
crop_cover: false, crop_cover: false,
client_types: None, client_types: None,
pot: None,
} }
} }
} }
@ -411,6 +417,21 @@ impl DownloaderBuilder {
self self
} }
/// Set the `pot` token to circumvent bot detection
///
/// YouTube has implemented the token to prevent other clients from downloading YouTube videos.
/// The token is generated using YouTube's botguard. Therefore you need a full browser environment
/// to obtain one.
///
/// The Invidious project has created a script to extract this token: <https://github.com/iv-org/youtube-trusted-session-generator>
///
/// The `pot` token is only used for the [`ClientType::Desktop`] and [`ClientType::DesktopMusic`] clients.
#[must_use]
pub fn pot<S: Into<String>>(mut self, pot: S) -> Self {
self.pot = Some(pot.into());
self
}
/// Create a new, configured [`Downloader`] instance /// Create a new, configured [`Downloader`] instance
pub fn build(self) -> Downloader { pub fn build(self) -> Downloader {
self.build_with_client( self.build_with_client(
@ -445,6 +466,7 @@ impl DownloaderBuilder {
#[cfg(feature = "audiotag")] #[cfg(feature = "audiotag")]
crop_cover: self.crop_cover, crop_cover: self.crop_cover,
client_types: self.client_types, client_types: self.client_types,
pot: self.pot,
}), }),
} }
} }
@ -479,6 +501,7 @@ impl Downloader {
filter: None, filter: None,
video_format: None, video_format: None,
client_types: None, client_types: None,
pot: None,
} }
} }
@ -506,7 +529,7 @@ impl Downloader {
self.query(DownloadVideo::from_entity(video)) self.query(DownloadVideo::from_entity(video))
} }
/// Download a video from a [`TrackItem`] (YouTube Music album/playlist item) /// Download a video from a [`TrackItem`] (YouTube music album/playlist item)
/// ///
/// Providing an entity has the advantage that the download path can be determined before the video /// Providing an entity has the advantage that the download path can be determined before the video
/// is fetched, so already downloaded videos get skipped right away. /// is fetched, so already downloaded videos get skipped right away.
@ -629,6 +652,21 @@ impl DownloadQuery {
self self
} }
/// Set the `pot` token to circumvent bot detection
///
/// YouTube has implemented the token to prevent other clients from downloading YouTube videos.
/// The token is generated using YouTube's botguard. Therefore you need a full browser environment
/// to obtain one.
///
/// The Invidious project has created a script to extract this token: <https://github.com/iv-org/youtube-trusted-session-generator>
///
/// The `pot` token is only used for the [`ClientType::Desktop`] and [`ClientType::DesktopMusic`] clients.
#[must_use]
pub fn pot<S: Into<String>>(mut self, pot: S) -> Self {
self.pot = Some(pot.into());
self
}
/// Download the video /// Download the video
/// ///
/// If no download path is set, the video is downloaded to the current directory /// If no download path is set, the video is downloaded to the current directory
@ -660,15 +698,9 @@ impl DownloadQuery {
.await .await
{ {
Ok(res) => return Ok(res), Ok(res) => return Ok(res),
Err(DownloadError::Forbidden { Err(DownloadError::Forbidden(c)) => {
client_type, failed_client = Some(c);
visitor_data, DownloadError::Forbidden(c)
}) => {
failed_client = Some(client_type);
DownloadError::Forbidden {
client_type,
visitor_data,
}
} }
Err(DownloadError::Http(e)) => { Err(DownloadError::Http(e)) => {
if !e.is_timeout() { if !e.is_timeout() {
@ -738,7 +770,7 @@ impl DownloadQuery {
.as_ref() .as_ref()
.or(self.dl.i.client_types.as_ref()) .or(self.dl.i.client_types.as_ref())
.map(Vec::as_slice) .map(Vec::as_slice)
.unwrap_or(q.player_client_order()), .unwrap_or(DEFAULT_PLAYER_CLIENT_ORDER),
); );
// If the last download failed, try another client if possible // If the last download failed, try another client if possible
@ -755,15 +787,20 @@ impl DownloadQuery {
let player_data = q.player_from_clients(&self.video.id, &client_types).await?; let player_data = q.player_from_clients(&self.video.id, &client_types).await?;
let user_agent = q.user_agent(player_data.client_type); let user_agent = q.user_agent(player_data.client_type);
let pot = if matches!(
player_data.client_type,
ClientType::Desktop | ClientType::DesktopMusic
) {
self.pot.as_deref().or(self.dl.i.pot.as_deref())
} else {
None
};
// Select streams to download // Select streams to download
let (video, audio) = player_data.select_video_audio_stream(filter); let (video, audio) = player_data.select_video_audio_stream(filter);
if video.is_none() && audio.is_none() { if video.is_none() && audio.is_none() {
if player_data.drm.is_some() { return Err(DownloadError::Input("no stream found".into()));
return Err(DownloadError::Source("video is DRM-protected".into()));
}
return Err(DownloadError::Source("no stream found".into()));
} }
let extension = match video { let extension = match video {
@ -772,9 +809,7 @@ impl DownloadQuery {
Some(audio) => match audio.codec { Some(audio) => match audio.codec {
AudioCodec::Mp4a => "m4a", AudioCodec::Mp4a => "m4a",
AudioCodec::Opus => "opus", AudioCodec::Opus => "opus",
AudioCodec::Ac3 => "ac3", _ => return Err(DownloadError::Input("unknown audio codec".into())),
AudioCodec::Ec3 => "eac3",
_ => return Err(DownloadError::Source("unknown audio codec".into())),
}, },
None => unreachable!(), None => unreachable!(),
}, },
@ -833,10 +868,11 @@ impl DownloadQuery {
if let Some(pb) = pb { if let Some(pb) = pb {
pb.set_message(format!("Downloading {name}{attempt_suffix}")) pb.set_message(format!("Downloading {name}{attempt_suffix}"))
} }
let downloads = download_streams( download_streams(
downloads, &downloads,
&self.dl.i.http, &self.dl.i.http,
&user_agent, &user_agent,
pot,
#[cfg(feature = "indicatif")] #[cfg(feature = "indicatif")]
pb.clone(), pb.clone(),
) )
@ -844,14 +880,7 @@ impl DownloadQuery {
.map_err(|e| { .map_err(|e| {
if let DownloadError::Http(e) = &e { if let DownloadError::Http(e) = &e {
if e.status() == Some(StatusCode::FORBIDDEN) { if e.status() == Some(StatusCode::FORBIDDEN) {
// 403 errors may occur due to bad visitor data IDs return DownloadError::Forbidden(player_data.client_type);
if let Some(vd) = &player_data.visitor_data {
q.remove_visitor_data(vd);
}
return DownloadError::Forbidden {
client_type: player_data.client_type,
visitor_data: player_data.visitor_data.clone(),
};
} }
} }
e e
@ -871,7 +900,7 @@ impl DownloadQuery {
// Tag audio file // Tag audio file
#[cfg(feature = "audiotag")] #[cfg(feature = "audiotag")]
if self.dl.i.audio_tag && video.is_none() && matches!(extension, "m4a" | "opus") { if self.dl.i.audio_tag && video.is_none() {
let (details, track) = match details { let (details, track) = match details {
Some(d) => (d, self.dl.i.rp.query().music_details(&self.video.id).await?), Some(d) => (d, self.dl.i.rp.query().music_details(&self.video.id).await?),
None => { None => {
@ -898,9 +927,13 @@ impl DownloadQuery {
} }
// Delete original files // Delete original files
for d in &downloads { stream::iter(&downloads)
fs::remove_file(&d.file).await?; .map(|d| fs::remove_file(d.file.clone()))
} .buffer_unordered(downloads.len())
.collect::<Vec<_>>()
.await
.into_iter()
.collect::<core::result::Result<(), _>>()?;
#[cfg(feature = "indicatif")] #[cfg(feature = "indicatif")]
if let Some(pb) = pb { if let Some(pb) = pb {
@ -986,37 +1019,14 @@ impl DownloadQuery {
}; };
if let Some(thumbnail) = thumbnail { if let Some(thumbnail) = thumbnail {
// Attempt to get the higher resolution, uncropped maxresdefault.jpg thumbnail if available let resp = self
let mut resp = None; .dl
if thumbnail.height != thumbnail.width { .i
if let Ok(x) = self .http
.dl .get(thumbnail.url)
.i .send()
.http .await?
.get(format!( .error_for_status()?;
"https://i.ytimg.com/vi/{}/maxresdefault.jpg",
track.id
))
.send()
.await?
.error_for_status()
{
resp = Some(x);
}
}
let resp = match resp {
Some(resp) => resp,
None => self
.dl
.i
.http
.get(thumbnail.url)
.send()
.await?
.error_for_status()?,
};
let img_type = resp let img_type = resp
.headers() .headers()
.get(header::CONTENT_TYPE) .get(header::CONTENT_TYPE)
@ -1115,6 +1125,7 @@ async fn download_single_file(
output: &Path, output: &Path,
http: &Client, http: &Client,
user_agent: &str, user_agent: &str,
pot: Option<&str>,
#[cfg(feature = "indicatif")] pb: Option<ProgressBar>, #[cfg(feature = "indicatif")] pb: Option<ProgressBar>,
) -> Result<()> { ) -> Result<()> {
// Check if file is already downloaded // Check if file is already downloaded
@ -1203,7 +1214,7 @@ async fn download_single_file(
.open(&output_path_tmp) .open(&output_path_tmp)
.await?; .await?;
let res = if is_gvideo && size.is_some() { if is_gvideo && size.is_some() {
download_chunks_by_param( download_chunks_by_param(
http, http,
&mut file, &mut file,
@ -1211,10 +1222,11 @@ async fn download_single_file(
size.unwrap(), size.unwrap(),
offset, offset,
user_agent, user_agent,
pot,
#[cfg(feature = "indicatif")] #[cfg(feature = "indicatif")]
pb, pb,
) )
.await .await?;
} else { } else {
download_chunks_by_header( download_chunks_by_header(
http, http,
@ -1226,19 +1238,7 @@ async fn download_single_file(
#[cfg(feature = "indicatif")] #[cfg(feature = "indicatif")]
pb, pb,
) )
.await .await?;
};
drop(file);
if let Err(e) = res {
// Remove temporary file if nothing was downloaded (e.g. 403 error)
if std::fs::metadata(&output_path_tmp)
.map(|md| md.len() == 0)
.unwrap_or_default()
{
_ = std::fs::remove_file(&output_path_tmp);
}
return Err(e);
} }
fs::rename(&output_path_tmp, &output_path).await?; fs::rename(&output_path_tmp, &output_path).await?;
@ -1338,6 +1338,7 @@ async fn download_chunks_by_param(
size: u64, size: u64,
offset: u64, offset: u64,
user_agent: &str, user_agent: &str,
pot: Option<&str>,
#[cfg(feature = "indicatif")] pb: Option<ProgressBar>, #[cfg(feature = "indicatif")] pb: Option<ProgressBar>,
) -> Result<()> { ) -> Result<()> {
let mut offset = offset; let mut offset = offset;
@ -1350,9 +1351,12 @@ async fn download_chunks_by_param(
let range = get_download_range(offset, Some(size)); let range = get_download_range(offset, Some(size));
tracing::debug!("Fetching range {}-{}", range.start, range.end); tracing::debug!("Fetching range {}-{}", range.start, range.end);
let urlp = let mut urlp =
Url::parse_with_params(url, [("range", &format!("{}-{}", range.start, range.end))]) Url::parse_with_params(url, [("range", &format!("{}-{}", range.start, range.end))])
.map_err(|e| DownloadError::Progressive(format!("url parsing: {e}").into()))?; .map_err(|e| DownloadError::Progressive(format!("url parsing: {e}").into()))?;
if let Some(pot) = pot {
urlp.query_pairs_mut().append_pair("pot", pot);
}
let res = http let res = http
.get(urlp) .get(urlp)
@ -1370,6 +1374,7 @@ async fn download_chunks_by_param(
)); ));
} }
tracing::debug!("Retrieving chunks...");
let mut stream = res.bytes_stream(); let mut stream = res.bytes_stream();
while let Some(item) = stream.next().await { while let Some(item) = stream.next().await {
// Retrieve chunk. // Retrieve chunk.
@ -1399,30 +1404,33 @@ struct StreamDownload {
} }
async fn download_streams( async fn download_streams(
downloads: Vec<StreamDownload>, downloads: &Vec<StreamDownload>,
http: &Client, http: &Client,
user_agent: &str, user_agent: &str,
pot: Option<&str>,
#[cfg(feature = "indicatif")] pb: Option<ProgressBar>, #[cfg(feature = "indicatif")] pb: Option<ProgressBar>,
) -> Result<Vec<StreamDownload>> { ) -> Result<()> {
stream::iter(downloads.iter().map(Ok)) let n = downloads.len();
.try_for_each_concurrent(2, |d| {
#[cfg(feature = "indicatif")]
let pb = pb.clone();
async move {
download_single_file(
&d.url,
&d.file,
http,
user_agent,
#[cfg(feature = "indicatif")]
pb,
)
.await
}
})
.await?;
Ok(downloads) stream::iter(downloads)
.map(|d| {
download_single_file(
&d.url,
&d.file,
http,
user_agent,
pot,
#[cfg(feature = "indicatif")]
pb.clone(),
)
})
.buffer_unordered(n)
.collect::<Vec<_>>()
.await
.into_iter()
.collect::<Result<Vec<_>>>()?;
Ok(())
} }
async fn convert_streams( async fn convert_streams(

View file

@ -47,13 +47,11 @@ async fn download_music(rp: RustyPipe) {
let td = TempDir::default(); let td = TempDir::default();
let td_path = td.to_path_buf(); let td_path = td.to_path_buf();
#[allow(unused_mut)] let dl = Downloader::builder()
let mut dl = Downloader::builder().rustypipe(&rp); .audio_tag()
#[cfg(feature = "audiotag")] .crop_cover()
{ .rustypipe(&rp)
dl = dl.audio_tag().crop_cover(); .build();
}
let dl = dl.build();
let res = dl let res = dl
.id("bVtv3st8bgc") .id("bVtv3st8bgc")
@ -77,7 +75,7 @@ async fn download_music(rp: RustyPipe) {
assert_audio_meta( assert_audio_meta(
&res.dest, &res.dest,
"Lord of the Riffs", "Lord of the Riffs",
"Alexander Nakarada", "Alexander Nakarada - CreatorChords",
"Lord of the Riffs", "Lord of the Riffs",
"2022-02-05", "2022-02-05",
); );
@ -113,15 +111,3 @@ fn assert_audio_meta(p: &Path, title: &str, artist: &str, album: &str, date: &st
assert_eq!(tags["ALBUM"].as_str(), Some(album)); assert_eq!(tags["ALBUM"].as_str(), Some(album));
assert_eq!(tags["DATE"].as_str(), Some(date)); assert_eq!(tags["DATE"].as_str(), Some(date));
} }
/// This is just a static check to make sure all RustyPipe futures can be sent
/// between threads safely.
/// Otherwise this may cause issues when integrating RustyPipe into async projects.
#[allow(unused)]
async fn all_send_and_sync() {
fn send_and_sync<T: Send + Sync>(t: T) {}
let dl = Downloader::default();
let dlq = dl.id("");
send_and_sync(dlq.download());
}

View file

@ -3,12 +3,12 @@
When YouTube introduces a new feature, it does so gradually. When a user creates a new When YouTube introduces a new feature, it does so gradually. When a user creates a new
session, YouTube decided randomly which new features should be enabled. session, YouTube decided randomly which new features should be enabled.
YouTube sessions are identified by the visitor data ID. This cookie is sent with YouTube sessions are identified by the visitor data cookie. This cookie is sent with
every API request using the `context.client.visitor_data` JSON parameter. It is also every API request using the `context.client.visitor_data` JSON parameter. It is also
returned in the `responseContext.visitorData` response parameter and stored as the returned in the `responseContext.visitorData` response parameter and stored as the
`__SECURE-YEC` cookie. `__SECURE-YEC` cookie.
By sending the same visitor data ID, A/B tests can be reproduced, which is important By sending the same visitor data cookie, A/B tests can be reproduced, which is important
for testing alternative YouTube clients. for testing alternative YouTube clients.
This page lists all A/B tests that were encountered while maintaining the RustyPipe This page lists all A/B tests that were encountered while maintaining the RustyPipe
@ -381,7 +381,7 @@ YouTube also changed the way the full discography page is fetched, surprisingly
it easier for alternative clients. The discography page now has its own content ID in it easier for alternative clients. The discography page now has its own content ID in
the format of `MPAD<channel id>` (Music Page Artist Discography). This page can be the format of `MPAD<channel id>` (Music Page Artist Discography). This page can be
fetched with a regular browse request without requiring parameters to be parsed or a fetched with a regular browse request without requiring parameters to be parsed or a
visitor data ID to be set, as it was the case with the old system. visitor data cookie to be set, as it was the case with the old system.
**OLD** **OLD**
@ -793,7 +793,7 @@ YouTube changed the data model for the channel shorts tab
- **Encountered on:** 11.10.2024 - **Encountered on:** 11.10.2024
- **Impact:** 🟢 Low - **Impact:** 🟢 Low
- **Endpoint:** browse - **Endpoint:** browse
- **Status:** Stabilized - **Status:** Common (99%)
```json ```json
{ {
@ -899,156 +899,3 @@ YouTube changed the data model for the channel shorts tab
} }
} }
``` ```
## [17] Channel playlists: lockupViewModel
- **Encountered on:** 09.11.2024
- **Impact:** 🟢 Low
- **Endpoint:** browse
- **Status:** Stabilized
YouTube changed the data model for the channel playlists / podcasts / albums tab
```json
{
"lockupViewModel": {
"contentImage": {
"collectionThumbnailViewModel": {
"primaryThumbnail": {
"thumbnailViewModel": {
"image": {
"sources": [
{
"url": "https://i.ytimg.com/vi/XYdmX8w8xwI/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCqmf6TGfDinNXhgU29ZxOkv2u9sQ",
"width": 480,
"height": 270
}
]
},
"overlays": [
{
"thumbnailOverlayBadgeViewModel": {
"thumbnailBadges": [
{
"thumbnailBadgeViewModel": {
"icon": {
"sources": [
{
"clientResource": {
"imageName": "PLAYLISTS"
}
}
]
},
"text": "5 videos",
"badgeStyle": "THUMBNAIL_OVERLAY_BADGE_STYLE_DEFAULT",
"backgroundColor": {
"lightTheme": 2370867,
"darkTheme": 2370867
}
}
}
],
"position": "THUMBNAIL_OVERLAY_BADGE_POSITION_BOTTOM_END"
}
}
]
}
}
}
},
"metadata": {
"lockupMetadataViewModel": {
"title": {
"content": "Jellybean Components Series"
}
}
},
"contentId": "PLvOlSehNtuHv268f0mW5m1t_hq_RVGRSA",
"contentType": "LOCKUP_CONTENT_TYPE_PLAYLIST"
}
}
```
## [18] Music playlists facepile avatar
- **Encountered on:** 25.11.2024
- **Impact:** 🟢 Low
- **Endpoint:** browse (YTM)
- **Status:** Stabilized
YouTube changed the data model for the channel playlist owner avatar into a `facepile`
object. It now also contains the channel avatar.
The model is also used for playlists owned by YouTube Music (with the avatar and
commandContext missing).
```json
{
"facepile": {
"avatarStackViewModel": {
"avatars": [
{
"avatarViewModel": {
"image": {
"sources": [
{
"url": "https://yt3.ggpht.com/ytc/AIdro_n9ALaLETwQH6_2WlXitIaIKV-IqBDWWquvyI2jucNAZaQ=s48-c-k-c0x00000000-no-cc-rj-rp"
}
]
},
"avatarImageSize": "AVATAR_SIZE_XS"
}
}
],
"text": {
"content": "Chaosflo44"
},
"rendererContext": {
"commandContext": {
"onTap": {
"innertubeCommand": {
"browseEndpoint": {
"browseId": "UCQM0bS4_04-Y4JuYrgmnpZQ",
"browseEndpointContextSupportedConfigs": {
"browseEndpointContextMusicConfig": {
"pageType": "MUSIC_PAGE_TYPE_USER_CHANNEL"
}
}
}
}
}
}
}
}
}
}
```
## [19] Music artist album groups reordered
- **Encountered on:** 13.01.2025
- **Impact:** 🟢 Low
- **Endpoint:** browse (YTM)
- **Status:** Common (10%)
YouTube Music used to group artist albums into 2 rows: "Albums" and "Singles".
These groups were changed into "Albums" and "Singles & EPs". Now the "Album" label is
omitted for albums in their group, while singles and EPs have a label with their type.
## [20] Music continuation item renderer
- **Encountered on:** 25.01.2025
- **Impact:** 🟢 Low
- **Endpoint:** browse (YTM)
- **Status:** Common (4%)
YouTube Music now uses a `continuationItemRenderer` for music playlists instead of
putting the continuations in a separate attribute of the MusicShelf.
The continuation response now uses a `onResponseReceivedActions` field for its music
items.
YouTube Music now also sends a random 16-character string as a `clientScreenNonce` in
the request context. This is not mandatory though.

View file

@ -16,7 +16,7 @@ The pot token is base64-formatted and usually starts with a M
`MnToZ2brHmyo0ehfKtK_EWUq60dPYDXksNX_UsaniM_Uj6zbtiIZujCHY02hr7opxB_n3XHetJQCBV9cnNHovuhvDqrjfxsKR-sjn-eIxqv3qOZKphvyDpQzlYBnT2AXK41R-ti6iPonrvlvKIASNmYX2lhsEg==` `MnToZ2brHmyo0ehfKtK_EWUq60dPYDXksNX_UsaniM_Uj6zbtiIZujCHY02hr7opxB_n3XHetJQCBV9cnNHovuhvDqrjfxsKR-sjn-eIxqv3qOZKphvyDpQzlYBnT2AXK41R-ti6iPonrvlvKIASNmYX2lhsEg==`
The token is generated from YouTubes Botguard script. The token is bound to the visitor data ID The token is generated from YouTubes Botguard script. The token is bound to the visitor data cookie
used to fetch the player data. used to fetch the player data.
This feature has been A/B-tested for a few weeks. During that time, refetching the player in case This feature has been A/B-tested for a few weeks. During that time, refetching the player in case

View file

@ -1,6 +1,9 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:best-practices", ":preserveSemverRanges"], "extends": [
"config:best-practices",
":semanticCommitTypeAll(chore)"
],
"semanticCommits": "enabled", "semanticCommits": "enabled",
"automerge": true, "automerge": true,
"automergeStrategy": "squash", "automergeStrategy": "squash",

View file

@ -16,8 +16,7 @@
//! the cache as a JSON file. //! the cache as a JSON file.
use std::{ use std::{
fs::File, fs,
io::Write,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -69,21 +68,7 @@ impl Default for FileStorage {
impl CacheStorage for FileStorage { impl CacheStorage for FileStorage {
fn write(&self, data: &str) { fn write(&self, data: &str) {
fn _write(path: &Path, data: &str) -> Result<(), std::io::Error> { fs::write(&self.path, data).unwrap_or_else(|e| {
let mut f = File::create(path)?;
// Set cache file permissions to 0600 on Unix-based systems
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::PermissionsExt;
let metadata = f.metadata()?;
let mut permissions = metadata.permissions();
permissions.set_mode(0o600);
std::fs::set_permissions(path, permissions)?;
}
f.write_all(data.as_bytes())
}
_write(&self.path, data).unwrap_or_else(|e| {
error!( error!(
"Could not write cache to file `{}`. Error: {}", "Could not write cache to file `{}`. Error: {}",
self.path.to_string_lossy(), self.path.to_string_lossy(),
@ -97,7 +82,7 @@ impl CacheStorage for FileStorage {
return None; return None;
} }
match std::fs::read_to_string(&self.path) { match fs::read_to_string(&self.path) {
Ok(data) => Some(data), Ok(data) => Some(data),
Err(e) => { Err(e) => {
error!( error!(

View file

@ -9,16 +9,14 @@ use crate::{
error::{Error, ExtractionError}, error::{Error, ExtractionError},
model::{ model::{
paginator::{ContinuationEndpoint, Paginator}, paginator::{ContinuationEndpoint, Paginator},
Channel, ChannelInfo, PlaylistItem, Verification, VideoItem, Channel, ChannelInfo, PlaylistItem, VideoItem,
}, },
param::{ChannelOrder, ChannelVideoTab, Language}, param::{ChannelOrder, ChannelVideoTab, Language},
serializer::{text::TextComponent, MapResult}, serializer::{text::TextComponent, MapResult},
util::{self, timeago, ProtoBuilder}, util::{self, timeago, ProtoBuilder},
}; };
use super::{ use super::{response, ClientType, MapRespCtx, MapResponse, QContinuation, RustyPipeQuery};
response, ClientType, MapRespCtx, MapRespOptions, MapResponse, QContinuation, RustyPipeQuery,
};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -180,16 +178,12 @@ impl RustyPipeQuery {
continuation: &channel_info_ctoken(channel_id, &random_target()), continuation: &channel_info_ctoken(channel_id, &random_target()),
}; };
self.execute_request_ctx::<response::ChannelAbout, _, _>( self.execute_request::<response::ChannelAbout, _, _>(
ClientType::Desktop, ClientType::Desktop,
"channel_info", "channel_info",
channel_id, channel_id,
"browse", "browse",
&request_body, &request_body,
MapRespOptions {
unlocalized: true,
..Default::default()
},
) )
.await .await
} }
@ -230,7 +224,6 @@ impl MapResponse<Channel<Paginator<VideoItem>>> for response::Channel {
mapper.ctoken, mapper.ctoken,
visitor_data, visitor_data,
ContinuationEndpoint::Browse, ContinuationEndpoint::Browse,
false,
); );
Ok(MapResult { Ok(MapResult {
@ -280,7 +273,7 @@ impl MapResponse<Channel<Paginator<PlaylistItem>>> for response::Channel {
impl MapResponse<ChannelInfo> for response::ChannelAbout { impl MapResponse<ChannelInfo> for response::ChannelAbout {
fn map_response(self, ctx: &MapRespCtx<'_>) -> Result<MapResult<ChannelInfo>, ExtractionError> { fn map_response(self, ctx: &MapRespCtx<'_>) -> Result<MapResult<ChannelInfo>, ExtractionError> {
// Channel info is always fetched in English. There is no localized data // Channel info is always fetched in English. There is no localized data there
// and it allows parsing the country name. // and it allows parsing the country name.
let lang = Language::En; let lang = Language::En;
@ -335,7 +328,7 @@ impl MapResponse<ChannelInfo> for response::ChannelAbout {
.video_count_text .video_count_text
.and_then(|txt| util::parse_numeric_or_warn(&txt, &mut warnings)), .and_then(|txt| util::parse_numeric_or_warn(&txt, &mut warnings)),
create_date: about.joined_date_text.and_then(|txt| { create_date: about.joined_date_text.and_then(|txt| {
timeago::parse_textual_date_or_warn(lang, ctx.utc_offset, &txt, &mut warnings) timeago::parse_textual_date_or_warn(lang, &txt, &mut warnings)
.map(OffsetDateTime::date) .map(OffsetDateTime::date)
}), }),
view_count: about view_count: about
@ -490,7 +483,7 @@ fn map_channel(
.avatar_view_model .avatar_view_model
.image .image
.into(), .into(),
verification: hdata.title.map(Verification::from).unwrap_or_default(), verification: hdata.title.into(),
description: metadata.description, description: metadata.description,
tags: microformat.microformat_data_renderer.tags, tags: microformat.microformat_data_renderer.tags,
banner: hdata.banner.image_banner_view_model.image.into(), banner: hdata.banner.image_banner_view_model.image.into(),
@ -777,10 +770,8 @@ mod tests {
} }
#[rstest] #[rstest]
#[case::base("base")] fn map_channel_playlists() {
#[case::lockup("20241109_lockup")] let json_path = path!(*TESTFILES / "channel" / "channel_playlists.json");
fn map_channel_playlists(#[case] name: &str) {
let json_path = path!(*TESTFILES / "channel" / format!("channel_playlists_{name}.json"));
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();
let channel: response::Channel = let channel: response::Channel =
@ -794,7 +785,7 @@ mod tests {
"deserialization/mapping warnings: {:?}", "deserialization/mapping warnings: {:?}",
map_res.warnings map_res.warnings
); );
insta::assert_ron_snapshot!(format!("map_channel_playlists_{name}"), map_res.c); insta::assert_ron_snapshot!("map_channel_playlists", map_res.c);
} }
#[rstest] #[rstest]

View file

@ -3,7 +3,7 @@ use std::fmt::Debug;
use crate::{ use crate::{
error::{Error, ExtractionError}, error::{Error, ExtractionError},
model::ChannelRss, model::ChannelRss,
report::Report, report::{Report, RustyPipeInfo},
util, util,
}; };
@ -45,7 +45,7 @@ impl RustyPipeQuery {
Err(e) => { Err(e) => {
if let Some(reporter) = &self.client.inner.reporter { if let Some(reporter) = &self.client.inner.reporter {
let report = Report { let report = Report {
info: self.rp_info(), info: RustyPipeInfo::new(Some(self.opts.lang)),
level: crate::report::Level::ERR, level: crate::report::Level::ERR,
operation: "channel_rss", operation: "channel_rss",
error: Some(e.to_string()), error: Some(e.to_string()),

File diff suppressed because it is too large Load diff

View file

@ -5,14 +5,10 @@ use regex::Regex;
use tracing::debug; use tracing::debug;
use crate::{ use crate::{
client::{ client::{response::url_endpoint::NavigationEndpoint, MapRespCtxSource, QContinuation},
response::{music_item::map_album_type, url_endpoint::NavigationEndpoint},
MapRespOptions, QContinuation,
},
error::{Error, ExtractionError}, error::{Error, ExtractionError},
model::{ model::{
paginator::Paginator, traits::FromYtItem, AlbumItem, AlbumType, ArtistId, MusicArtist, paginator::Paginator, traits::FromYtItem, AlbumItem, ArtistId, MusicArtist, MusicItem,
MusicItem,
}, },
param::{AlbumFilter, AlbumOrder}, param::{AlbumFilter, AlbumOrder},
serializer::MapResult, serializer::MapResult,
@ -113,9 +109,8 @@ impl RustyPipeQuery {
artist_id, artist_id,
"browse", "browse",
&request_body, &request_body,
MapRespOptions { MapRespCtxSource {
artist: Some(first_page.artist.clone()), artist: Some(first_page.artist.clone()),
visitor_data: first_page.visitor_data.as_deref(),
..Default::default() ..Default::default()
}, },
) )
@ -179,7 +174,8 @@ fn map_artist_page(
.contents .contents
.into_iter() .into_iter()
.next() .next()
.map(|c| c.tab_renderer.content.section_list_renderer.contents) .and_then(|tab| tab.tab_renderer.content)
.map(|c| c.section_list_renderer.contents)
.unwrap_or_default(); .unwrap_or_default();
let mut mapper = MusicListMapper::with_artist( let mut mapper = MusicListMapper::with_artist(
@ -210,12 +206,11 @@ fn map_artist_page(
} }
} }
} }
mapper.album_type = AlbumType::Single;
mapper.map_response(shelf.contents); mapper.map_response(shelf.contents);
} }
response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => { response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => {
let mut extendable_albums = false; let mut extendable_albums = false;
mapper.album_type = AlbumType::Single;
if let Some(h) = shelf.header { if let Some(h) = shelf.header {
if let Some(button) = h if let Some(button) = h
.music_carousel_shelf_basic_header_renderer .music_carousel_shelf_basic_header_renderer
@ -254,12 +249,6 @@ fn map_artist_page(
} }
} }
} }
mapper.album_type = map_album_type(
h.music_carousel_shelf_basic_header_renderer
.title
.first_str(),
ctx.lang,
);
} }
if !skip_extendables || !extendable_albums { if !skip_extendables || !extendable_albums {
@ -330,7 +319,6 @@ struct FirstAlbumPage {
albums: Vec<AlbumItem>, albums: Vec<AlbumItem>,
ctoken: Option<String>, ctoken: Option<String>,
artist: ArtistId, artist: ArtistId,
visitor_data: Option<String>,
} }
impl MapResponse<FirstAlbumPage> for response::MusicArtistAlbums { impl MapResponse<FirstAlbumPage> for response::MusicArtistAlbums {
@ -384,7 +372,6 @@ impl MapResponse<FirstAlbumPage> for response::MusicArtistAlbums {
albums: mapped.c.albums, albums: mapped.c.albums,
ctoken, ctoken,
artist: artist_id, artist: artist_id,
visitor_data: ctx.visitor_data.map(str::to_owned),
}, },
warnings: mapped.warnings, warnings: mapped.warnings,
}) })
@ -425,7 +412,6 @@ mod tests {
#[case::only_singles("only_singles", "UCfwCE5VhPMGxNPFxtVv7lRw")] #[case::only_singles("only_singles", "UCfwCE5VhPMGxNPFxtVv7lRw")]
#[case::no_artist("no_artist", "UCh8gHdtzO2tXd593_bjErWg")] #[case::no_artist("no_artist", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::only_more_singles("only_more_singles", "UC0aXrjVxG5pZr99v77wZdPQ")] #[case::only_more_singles("only_more_singles", "UC0aXrjVxG5pZr99v77wZdPQ")]
#[case::grouped_albums("20250113_grouped_albums", "UCOR4_bSVIXPsGa4BbCSt60Q")]
fn map_music_artist(#[case] name: &str, #[case] id: &str) { fn map_music_artist(#[case] name: &str, #[case] id: &str) {
let json_path = path!(*TESTFILES / "music_artist" / format!("artist_{name}.json")); let json_path = path!(*TESTFILES / "music_artist" / format!("artist_{name}.json"));
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();

View file

@ -91,7 +91,7 @@ impl MapResponse<MusicCharts> for response::MusicCharts {
.and_then(|btn| btn.button_renderer.navigation_endpoint.music_page()) .and_then(|btn| btn.button_renderer.navigation_endpoint.music_page())
.map(|mp| (mp.typ, mp.id)) .map(|mp| (mp.typ, mp.id))
}) { }) {
Some((MusicPageType::Playlist { .. }, id)) => { Some((MusicPageType::Playlist, id)) => {
// Top music videos (first shelf with associated playlist) // Top music videos (first shelf with associated playlist)
if top_playlist_id.is_none() { if top_playlist_id.is_none() {
mapper_top.map_response(shelf.contents); mapper_top.map_response(shelf.contents);
@ -113,12 +113,12 @@ impl MapResponse<MusicCharts> for response::MusicCharts {
}); });
let mapped_top = mapper_top.conv_items::<TrackItem>(); let mapped_top = mapper_top.conv_items::<TrackItem>();
let mapped_trending = mapper_trending.conv_items::<TrackItem>(); let mut mapped_trending = mapper_trending.conv_items::<TrackItem>();
let mapped_other = mapper_other.group_items(); let mut mapped_other = mapper_other.group_items();
let mut warnings = mapped_top.warnings; let mut warnings = mapped_top.warnings;
warnings.extend(mapped_trending.warnings); warnings.append(&mut mapped_trending.warnings);
warnings.extend(mapped_other.warnings); warnings.append(&mut mapped_other.warnings);
Ok(MapResult { Ok(MapResult {
c: MusicCharts { c: MusicCharts {

View file

@ -37,7 +37,7 @@ struct QRadio<'a> {
} }
impl RustyPipeQuery { impl RustyPipeQuery {
/// Get the metadata of a YouTube Music track /// Get the metadata of a YouTube music track
#[tracing::instrument(skip(self), level = "error")] #[tracing::instrument(skip(self), level = "error")]
pub async fn music_details<S: AsRef<str> + Debug>( pub async fn music_details<S: AsRef<str> + Debug>(
&self, &self,
@ -61,7 +61,7 @@ impl RustyPipeQuery {
.await .await
} }
/// Get the lyrics of a YouTube Music track /// Get the lyrics of a YouTube music track
/// ///
/// The `lyrics_id` has to be obtained using [`RustyPipeQuery::music_details`]. /// The `lyrics_id` has to be obtained using [`RustyPipeQuery::music_details`].
#[tracing::instrument(skip(self), level = "error")] #[tracing::instrument(skip(self), level = "error")]
@ -269,14 +269,7 @@ impl MapResponse<Paginator<TrackItem>> for response::MusicDetails {
.map(|c| c.next_continuation_data.continuation); .map(|c| c.next_continuation_data.continuation);
Ok(MapResult { Ok(MapResult {
c: Paginator::new_ext( c: Paginator::new_ext(None, tracks, ctoken, None, ContinuationEndpoint::MusicNext),
None,
tracks,
ctoken,
None,
ContinuationEndpoint::MusicNext,
false,
),
warnings, warnings,
}) })
} }

View file

@ -6,14 +6,12 @@ use crate::{
model::{ model::{
paginator::{ContinuationEndpoint, Paginator}, paginator::{ContinuationEndpoint, Paginator},
richtext::RichText, richtext::RichText,
AlbumId, ChannelId, MusicAlbum, MusicPlaylist, TrackItem, TrackType, AlbumId, ChannelId, MusicAlbum, MusicPlaylist, TrackItem,
}, },
serializer::{text::TextComponents, MapResult}, serializer::{text::TextComponents, MapResult},
util::{self, TryRemove, DOT_SEPARATOR}, util::{self, TryRemove, DOT_SEPARATOR},
}; };
use self::response::url_endpoint::MusicPageType;
use super::{ use super::{
response::{ response::{
self, self,
@ -87,7 +85,7 @@ impl RustyPipeQuery {
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(i, track)| { .filter_map(|(i, track)| {
if track.track_type.is_video() { if track.is_video {
Some((i, track.name.clone())) Some((i, track.name.clone()))
} else { } else {
None None
@ -104,7 +102,7 @@ impl RustyPipeQuery {
for (i, title) in to_replace { for (i, title) in to_replace {
let found_track = playlist.tracks.items.iter().find_map(|track| { let found_track = playlist.tracks.items.iter().find_map(|track| {
if track.name == title && track.track_type.is_track() { if track.name == title && !track.is_video {
Some((track.id.clone(), track.duration)) Some((track.id.clone(), track.duration))
} else { } else {
None None
@ -115,7 +113,7 @@ impl RustyPipeQuery {
if let Some(duration) = duration { if let Some(duration) = duration {
album.tracks[i].duration = Some(duration); album.tracks[i].duration = Some(duration);
} }
album.tracks[i].track_type = TrackType::Track; album.tracks[i].is_video = false;
} }
} }
} }
@ -182,16 +180,14 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
let mut mapper = MusicListMapper::new(ctx.lang); let mut mapper = MusicListMapper::new(ctx.lang);
mapper.map_response(shelf.contents); mapper.map_response(shelf.contents);
let ctoken = mapper.ctoken.clone().or_else(|| {
shelf
.continuations
.into_iter()
.next()
.map(|cont| cont.next_continuation_data.continuation)
});
let map_res = mapper.conv_items(); let map_res = mapper.conv_items();
let ctoken = shelf
.continuations
.into_iter()
.next()
.map(|cont| cont.next_continuation_data.continuation);
let track_count = if ctoken.is_some() { let track_count = if ctoken.is_some() {
header.as_ref().and_then(|h| { header.as_ref().and_then(|h| {
let parts = h let parts = h
@ -217,39 +213,14 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
Some(header) => { Some(header) => {
let h = header.music_detail_header_renderer; let h = header.music_detail_header_renderer;
let (from_ytm, channel) = match h.facepile { let st = match h.strapline_text_one {
Some(facepile) => { Some(s) => s,
let from_ytm = facepile.avatar_stack_view_model.text.starts_with("YouTube"); None => h.subtitle,
let channel = facepile
.avatar_stack_view_model
.renderer_context
.command_context
.and_then(|c| {
c.on_tap
.innertube_command
.music_page()
.filter(|p| p.typ == MusicPageType::User)
.map(|p| p.id)
})
.map(|id| ChannelId {
id,
name: facepile.avatar_stack_view_model.text,
});
(from_ytm && channel.is_none(), channel)
}
None => {
let st = match h.strapline_text_one {
Some(s) => s,
None => h.subtitle,
};
let from_ytm = st.0.iter().any(util::is_ytm);
let channel = st.0.into_iter().find_map(|c| ChannelId::try_from(c).ok());
(from_ytm, channel)
}
}; };
let from_ytm = st.0.iter().any(util::is_ytm);
let channel = st.0.into_iter().find_map(|c| ChannelId::try_from(c).ok());
( (
from_ytm, from_ytm,
channel, channel,
@ -300,7 +271,6 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
ctoken, ctoken,
ctx.visitor_data.map(str::to_owned), ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicBrowse, ContinuationEndpoint::MusicBrowse,
ctx.authenticated,
), ),
related_playlists: Paginator::new_ext( related_playlists: Paginator::new_ext(
None, None,
@ -308,7 +278,6 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
related_ctoken, related_ctoken,
ctx.visitor_data.map(str::to_owned), ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicBrowse, ContinuationEndpoint::MusicBrowse,
ctx.authenticated,
), ),
}, },
warnings: map_res.warnings, warnings: map_res.warnings,
@ -515,7 +484,6 @@ mod tests {
#[case::nomusic("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe")] #[case::nomusic("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe")]
#[case::two_columns("20240228_twoColumns", "RDCLAK5uy_kb7EBi6y3GrtJri4_ZH56Ms786DFEimbM")] #[case::two_columns("20240228_twoColumns", "RDCLAK5uy_kb7EBi6y3GrtJri4_ZH56Ms786DFEimbM")]
#[case::n_album("20240228_album", "OLAK5uy_kdSWBZ-9AZDkYkuy0QCc3p0KO9DEHVNH0")] #[case::n_album("20240228_album", "OLAK5uy_kdSWBZ-9AZDkYkuy0QCc3p0KO9DEHVNH0")]
#[case::facepile("20241125_facepile", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe")]
fn map_music_playlist(#[case] name: &str, #[case] id: &str) { fn map_music_playlist(#[case] name: &str, #[case] id: &str) {
let json_path = path!(*TESTFILES / "music_playlist" / format!("playlist_{name}.json")); let json_path = path!(*TESTFILES / "music_playlist" / format!("playlist_{name}.json"));
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();

View file

@ -9,7 +9,7 @@ use crate::{
paginator::{ContinuationEndpoint, Paginator}, paginator::{ContinuationEndpoint, Paginator},
traits::FromYtItem, traits::FromYtItem,
AlbumItem, ArtistItem, MusicItem, MusicPlaylistItem, MusicSearchResult, AlbumItem, ArtistItem, MusicItem, MusicPlaylistItem, MusicSearchResult,
MusicSearchSuggestion, TrackItem, UserItem, MusicSearchSuggestion, TrackItem,
}, },
param::search_filter::MusicSearchFilter, param::search_filter::MusicSearchFilter,
serializer::MapResult, serializer::MapResult,
@ -57,7 +57,7 @@ impl RustyPipeQuery {
.await .await
} }
/// Search YouTube Music and return items of all types /// Search YouTube music and return items of all types
pub async fn music_search_main<S: AsRef<str>>( pub async fn music_search_main<S: AsRef<str>>(
&self, &self,
query: S, query: S,
@ -121,15 +121,6 @@ impl RustyPipeQuery {
.await .await
} }
/// Search YouTube Music users
pub async fn music_search_users<S: AsRef<str>>(
&self,
query: S,
) -> Result<MusicSearchResult<UserItem>, Error> {
self.music_search(query, Some(MusicSearchFilter::Users))
.await
}
/// Get YouTube Music search suggestions /// Get YouTube Music search suggestions
#[tracing::instrument(skip(self), level = "error")] #[tracing::instrument(skip(self), level = "error")]
pub async fn music_search_suggestion<S: AsRef<str> + Debug>( pub async fn music_search_suggestion<S: AsRef<str> + Debug>(
@ -189,7 +180,6 @@ impl<T: FromYtItem> MapResponse<MusicSearchResult<T>> for response::MusicSearch
response::music_search::ItemSection::None => {} response::music_search::ItemSection::None => {}
}); });
let ctoken = ctoken.or(mapper.ctoken.clone());
let map_res = mapper.conv_items(); let map_res = mapper.conv_items();
Ok(MapResult { Ok(MapResult {
@ -200,7 +190,6 @@ impl<T: FromYtItem> MapResponse<MusicSearchResult<T>> for response::MusicSearch
ctoken, ctoken,
ctx.visitor_data.map(str::to_owned), ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicSearch, ContinuationEndpoint::MusicSearch,
false,
), ),
corrected_query, corrected_query,
}, },

View file

@ -1,228 +0,0 @@
use std::fmt::Debug;
use crate::{
client::{
response::{self, music_item::MusicListMapper},
ClientType, MapResponse, QBrowseParams, RustyPipeQuery,
},
error::{Error, ExtractionError},
model::{
paginator::{ContinuationEndpoint, Paginator},
AlbumItem, ArtistItem, HistoryItem, MusicPlaylist, MusicPlaylistItem, TrackItem,
},
serializer::MapResult,
};
use super::{MapRespCtx, MapRespOptions, QContinuation};
impl RustyPipeQuery {
/// Get a list of tracks from YouTube Music which the current user recently played
///
/// Requires authentication cookies.
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_history(&self) -> Result<Paginator<HistoryItem<TrackItem>>, Error> {
let request_body = QBrowseParams {
browse_id: "FEmusic_history",
params: "oggECgIIAQ%3D%3D",
};
self.clone()
.authenticated()
.execute_request::<response::MusicHistory, _, _>(
ClientType::DesktopMusic,
"music_history",
"",
"browse",
&request_body,
)
.await
}
/// Get more YouTube Music history items from the given continuation token
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_history_continuation<S: AsRef<str> + Debug>(
&self,
ctoken: S,
visitor_data: Option<&str>,
) -> Result<Paginator<HistoryItem<TrackItem>>, Error> {
let ctoken = ctoken.as_ref();
let request_body = QContinuation {
continuation: ctoken,
};
self.clone()
.authenticated()
.execute_request_ctx::<response::MusicContinuation, _, _>(
ClientType::Desktop,
"history_continuation",
ctoken,
"browse",
&request_body,
MapRespOptions {
visitor_data,
..Default::default()
},
)
.await
}
/// Get a list of YouTube Music artists which the current user subscribed to
///
/// Requires authentication cookies.
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_saved_artists(&self) -> Result<Paginator<ArtistItem>, Error> {
self.clone()
.authenticated()
.continuation(
"4qmFsgIyEh5GRW11c2ljX2xpYnJhcnlfY29ycHVzX2FydGlzdHMaEGdnTUdLZ1FJQUJBQm9BWUI%3D",
ContinuationEndpoint::MusicBrowse,
None,
)
.await
}
/// Get a list of YouTube Music albums which the current user has added to their collection
///
/// Requires authentication cookies.
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_saved_albums(&self) -> Result<Paginator<AlbumItem>, Error> {
self.clone()
.authenticated()
.continuation(
"4qmFsgIoEhRGRW11c2ljX2xpa2VkX2FsYnVtcxoQZ2dNR0tnUUlBQkFCb0FZQg%3D%3D",
ContinuationEndpoint::MusicBrowse,
None,
)
.await
}
/// Get a list of YouTube Music tracks which the current user has added to their collection
///
/// Contains both liked tracks and tracks from saved albums.
///
/// Requires authentication cookies.
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_saved_tracks(&self) -> Result<Paginator<TrackItem>, Error> {
self.clone()
.authenticated()
.continuation(
"4qmFsgIoEhRGRW11c2ljX2xpa2VkX3ZpZGVvcxoQZ2dNR0tnUUlBQkFCb0FZQg%3D%3D",
ContinuationEndpoint::MusicBrowse,
None,
)
.await
}
/// Get a list of YouTube Music playlists which the current user has added to their collection
///
/// Requires authentication cookies.
#[tracing::instrument(skip(self), level = "error")]
pub async fn music_saved_playlists(&self) -> Result<Paginator<MusicPlaylistItem>, Error> {
self.clone()
.authenticated()
.continuation(
"4qmFsgIrEhdGRW11c2ljX2xpa2VkX3BsYXlsaXN0cxoQZ2dNR0tnUUlBQkFCb0FZQg%3D%3D",
ContinuationEndpoint::MusicBrowse,
None,
)
.await
}
/// Get all liked YouTube Music tracks of the logged-in user
///
/// The difference to [`RustyPipeQuery::music_saved_tracks`] is that this function only returns
/// tracks that were explicitly liked by the user.
///
/// Requires authentication cookies.
pub async fn music_liked_tracks(&self) -> Result<MusicPlaylist, Error> {
self.clone()
.authenticated()
.music_playlist("LM")
.await
.map_err(crate::util::map_internal_playlist_err)
}
}
impl MapResponse<Paginator<HistoryItem<TrackItem>>> for response::MusicHistory {
fn map_response(
self,
ctx: &MapRespCtx<'_>,
) -> Result<MapResult<Paginator<HistoryItem<TrackItem>>>, ExtractionError> {
let contents = match self.contents {
response::music_playlist::Contents::SingleColumnBrowseResultsRenderer(c) => {
c.contents
.into_iter()
.next()
.ok_or(ExtractionError::InvalidData("no content".into()))?
.tab_renderer
.content
.section_list_renderer
}
response::music_playlist::Contents::TwoColumnBrowseResultsRenderer {
secondary_contents,
..
} => secondary_contents.section_list_renderer,
};
let mut map_res = MapResult::default();
for shelf in contents.contents {
let shelf = if let response::music_item::ItemSection::MusicShelfRenderer(s) = shelf {
s
} else {
continue;
};
let mut mapper = MusicListMapper::new(ctx.lang);
mapper.map_response(shelf.contents);
mapper.conv_history_items(shelf.title, ctx.utc_offset, &mut map_res);
}
let ctoken = contents
.continuations
.into_iter()
.next()
.map(|c| c.next_continuation_data.continuation);
Ok(MapResult {
c: Paginator::new_ext(
None,
map_res.c,
ctoken,
ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicBrowse,
true,
),
warnings: map_res.warnings,
})
}
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::BufReader};
use path_macro::path;
use crate::util::tests::TESTFILES;
use super::*;
#[test]
fn map_history() {
let json_path = path!(*TESTFILES / "music_userdata" / "music_history.json");
let json_file = File::open(json_path).unwrap();
let history: response::MusicHistory =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res = history.map_response(&MapRespCtx::test("")).unwrap();
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
insta::assert_ron_snapshot!(map_res.c, {
".items[].playback_date" => "[date]",
});
}
}

View file

@ -8,15 +8,9 @@ use crate::model::{
}; };
use crate::serializer::MapResult; use crate::serializer::MapResult;
#[cfg(feature = "userdata")] use super::response::music_item::{map_queue_item, MusicListMapper, PlaylistPanelVideo};
use crate::model::{HistoryItem, TrackItem, VideoItem};
use super::response::{
music_item::{map_queue_item, MusicListMapper, PlaylistPanelVideo},
YouTubeListItem,
};
use super::{ use super::{
response, ClientType, MapRespCtx, MapRespOptions, MapResponse, QContinuation, RustyPipeQuery, response, ClientType, MapRespCtx, MapRespCtxSource, MapResponse, QContinuation, RustyPipeQuery,
}; };
impl RustyPipeQuery { impl RustyPipeQuery {
@ -41,7 +35,7 @@ impl RustyPipeQuery {
ctoken, ctoken,
endpoint.as_str(), endpoint.as_str(),
&request_body, &request_body,
MapRespOptions { MapRespCtxSource {
visitor_data, visitor_data,
..Default::default() ..Default::default()
}, },
@ -61,7 +55,7 @@ impl RustyPipeQuery {
ctoken, ctoken,
endpoint.as_str(), endpoint.as_str(),
&request_body, &request_body,
MapRespOptions { MapRespCtxSource {
visitor_data, visitor_data,
..Default::default() ..Default::default()
}, },
@ -83,7 +77,6 @@ fn map_yt_paginator<T: FromYtItem>(
ctoken: p.ctoken, ctoken: p.ctoken,
visitor_data: p.visitor_data, visitor_data: p.visitor_data,
endpoint, endpoint,
authenticated: p.authenticated,
} }
} }
@ -97,51 +90,37 @@ fn map_ytm_paginator<T: FromYtItem>(
ctoken: p.ctoken, ctoken: p.ctoken,
visitor_data: p.visitor_data, visitor_data: p.visitor_data,
endpoint, endpoint,
authenticated: p.authenticated,
} }
} }
fn continuation_items(response: response::Continuation) -> MapResult<Vec<YouTubeListItem>> {
response
.on_response_received_actions
.and_then(|actions| {
actions
.into_iter()
.map(|action| action.append_continuation_items_action.continuation_items)
.reduce(|mut acc, mut items| {
acc.c.append(&mut items.c);
acc.warnings.append(&mut items.warnings);
acc
})
})
.or_else(|| {
response
.continuation_contents
.map(|contents| contents.rich_grid_continuation.contents)
})
.unwrap_or_default()
}
impl MapResponse<Paginator<YouTubeItem>> for response::Continuation { impl MapResponse<Paginator<YouTubeItem>> for response::Continuation {
fn map_response( fn map_response(
self, self,
ctx: &MapRespCtx<'_>, ctx: &MapRespCtx<'_>,
) -> Result<MapResult<Paginator<YouTubeItem>>, ExtractionError> { ) -> Result<MapResult<Paginator<YouTubeItem>>, ExtractionError> {
let estimated_results = self.estimated_results; let items = self
let items = continuation_items(self); .on_response_received_actions
.and_then(|actions| {
actions
.into_iter()
.map(|action| action.append_continuation_items_action.continuation_items)
.reduce(|mut acc, mut items| {
acc.c.append(&mut items.c);
acc.warnings.append(&mut items.warnings);
acc
})
})
.or_else(|| {
self.continuation_contents
.map(|contents| contents.rich_grid_continuation.contents)
})
.unwrap_or_default();
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang); let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang);
mapper.map_response(items); mapper.map_response(items);
Ok(MapResult { Ok(MapResult {
c: Paginator::new_ext( c: Paginator::new(self.estimated_results, mapper.items, mapper.ctoken),
estimated_results,
mapper.items,
mapper.ctoken,
ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::Browse,
ctx.authenticated,
),
warnings: mapper.warnings, warnings: mapper.warnings,
}) })
} }
@ -202,122 +181,14 @@ impl MapResponse<Paginator<MusicItem>> for response::MusicContinuation {
None => {} None => {}
} }
for a in self.on_response_received_actions {
mapper.map_response(a.append_continuation_items_action.continuation_items);
}
let ctoken = mapper.ctoken.clone().or_else(|| {
continuations
.into_iter()
.next()
.map(|cont| cont.next_continuation_data.continuation)
});
let map_res = mapper.items(); let map_res = mapper.items();
Ok(MapResult {
c: Paginator::new_ext(
None,
map_res.c,
ctoken,
ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicBrowse,
ctx.authenticated,
),
warnings: map_res.warnings,
})
}
}
#[cfg(feature = "userdata")]
impl MapResponse<Paginator<HistoryItem<VideoItem>>> for response::Continuation {
fn map_response(
self,
ctx: &MapRespCtx<'_>,
) -> Result<MapResult<Paginator<HistoryItem<VideoItem>>>, ExtractionError> {
let mut map_res = MapResult::default();
let mut ctoken = None;
let items = continuation_items(self);
for item in items.c {
match item {
response::YouTubeListItem::ItemSectionRenderer { header, contents } => {
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
mapper.map_response(contents);
mapper.conv_history_items(
header.map(|h| h.item_section_header_renderer.title),
ctx.utc_offset,
&mut map_res,
);
}
response::YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint,
} => {
if ctoken.is_none() {
ctoken = Some(continuation_endpoint.continuation_command.token);
}
}
_ => {}
}
}
Ok(MapResult {
c: Paginator::new_ext(
None,
map_res.c,
ctoken,
ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::Browse,
ctx.authenticated,
),
warnings: map_res.warnings,
})
}
}
#[cfg(feature = "userdata")]
impl MapResponse<Paginator<HistoryItem<TrackItem>>> for response::MusicContinuation {
fn map_response(
self,
ctx: &MapRespCtx<'_>,
) -> Result<MapResult<Paginator<HistoryItem<TrackItem>>>, ExtractionError> {
let mut map_res = MapResult::default();
let mut continuations = Vec::new();
let mut map_shelf = |shelf: response::music_item::MusicShelf| {
let mut mapper = MusicListMapper::new(ctx.lang);
mapper.map_response(shelf.contents);
mapper.conv_history_items(shelf.title, ctx.utc_offset, &mut map_res);
continuations.extend(shelf.continuations);
};
match self.continuation_contents {
Some(response::music_item::ContinuationContents::MusicShelfContinuation(shelf)) => {
map_shelf(shelf);
}
Some(response::music_item::ContinuationContents::SectionListContinuation(contents)) => {
for c in contents.contents {
if let response::music_item::ItemSection::MusicShelfRenderer(shelf) = c {
map_shelf(shelf);
}
}
}
_ => {}
}
let ctoken = continuations let ctoken = continuations
.into_iter() .into_iter()
.next() .next()
.map(|cont| cont.next_continuation_data.continuation); .map(|cont| cont.next_continuation_data.continuation);
Ok(MapResult { Ok(MapResult {
c: Paginator::new_ext( c: Paginator::new(None, map_res.c, ctoken),
None,
map_res.c,
ctoken,
ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::MusicBrowse,
ctx.authenticated,
),
warnings: map_res.warnings, warnings: map_res.warnings,
}) })
} }
@ -327,18 +198,12 @@ impl<T: FromYtItem> Paginator<T> {
/// Get the next page from the paginator (or `None` if the paginator is exhausted) /// Get the next page from the paginator (or `None` if the paginator is exhausted)
pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> { pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken { Ok(match &self.ctoken {
Some(ctoken) => { Some(ctoken) => Some(
let q = if self.authenticated { query
&query.as_ref().clone().authenticated() .as_ref()
} else { .continuation(ctoken, self.endpoint, self.visitor_data.as_deref())
query.as_ref() .await?,
}; ),
Some(
q.continuation(ctoken, self.endpoint, self.visitor_data.as_deref())
.await?,
)
}
_ => None, _ => None,
}) })
} }
@ -427,40 +292,6 @@ impl Paginator<Comment> {
} }
} }
#[cfg(feature = "userdata")]
#[cfg_attr(docsrs, doc(cfg(feature = "userdata")))]
impl Paginator<HistoryItem<VideoItem>> {
/// Get the next page from the paginator (or `None` if the paginator is exhausted)
pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken {
Some(ctoken) => Some(
query
.as_ref()
.history_continuation(ctoken, self.visitor_data.as_deref())
.await?,
),
_ => None,
})
}
}
#[cfg(feature = "userdata")]
#[cfg_attr(docsrs, doc(cfg(feature = "userdata")))]
impl Paginator<HistoryItem<TrackItem>> {
/// Get the next page from the paginator (or `None` if the paginator is exhausted)
pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken {
Some(ctoken) => Some(
query
.as_ref()
.music_history_continuation(ctoken, self.visitor_data.as_deref())
.await?,
),
_ => None,
})
}
}
macro_rules! paginator { macro_rules! paginator {
($entity_type:ty) => { ($entity_type:ty) => {
impl Paginator<$entity_type> { impl Paginator<$entity_type> {
@ -542,12 +373,6 @@ macro_rules! paginator {
} }
paginator!(Comment); paginator!(Comment);
#[cfg(feature = "userdata")]
#[cfg_attr(docsrs, doc(cfg(feature = "userdata")))]
paginator!(HistoryItem<VideoItem>);
#[cfg(feature = "userdata")]
#[cfg_attr(docsrs, doc(cfg(feature = "userdata")))]
paginator!(HistoryItem<TrackItem>);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -558,10 +383,7 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
model::{ model::{MusicPlaylistItem, PlaylistItem, TrackItem, VideoItem},
AlbumItem, ArtistItem, ChannelItem, MusicPlaylistItem, PlaylistItem, TrackItem,
VideoItem,
},
util::tests::TESTFILES, util::tests::TESTFILES,
}; };
@ -632,32 +454,10 @@ mod tests {
insta::assert_ron_snapshot!(format!("map_{name}"), paginator); insta::assert_ron_snapshot!(format!("map_{name}"), paginator);
} }
#[rstest]
#[case::subscriptions("subscriptions", path!("userdata" / "subscriptions.json"))]
fn map_continuation_channels(#[case] name: &str, #[case] path: PathBuf) {
let json_path = path!(*TESTFILES / path);
let json_file = File::open(json_path).unwrap();
let items: response::Continuation =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Paginator<YouTubeItem>> =
items.map_response(&MapRespCtx::test("")).unwrap();
let paginator: Paginator<ChannelItem> =
map_yt_paginator(map_res.c, ContinuationEndpoint::Browse);
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
insta::assert_ron_snapshot!(format!("map_{name}"), paginator);
}
#[rstest] #[rstest]
#[case::playlist_tracks("playlist_tracks", path!("music_playlist" / "playlist_cont.json"))] #[case::playlist_tracks("playlist_tracks", path!("music_playlist" / "playlist_cont.json"))]
#[case::search_tracks("search_tracks", path!("music_search" / "tracks_cont.json"))] #[case::search_tracks("search_tracks", path!("music_search" / "tracks_cont.json"))]
#[case::radio_tracks("radio_tracks", path!("music_details" / "radio_cont.json"))] #[case::radio_tracks("radio_tracks", path!("music_details" / "radio_cont.json"))]
#[case::saved_tracks("saved_tracks", path!("music_userdata" / "saved_tracks.json"))]
fn map_continuation_tracks(#[case] name: &str, #[case] path: PathBuf) { fn map_continuation_tracks(#[case] name: &str, #[case] path: PathBuf) {
let json_path = path!(*TESTFILES / path); let json_path = path!(*TESTFILES / path);
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();
@ -677,51 +477,8 @@ mod tests {
insta::assert_ron_snapshot!(format!("map_{name}"), paginator); insta::assert_ron_snapshot!(format!("map_{name}"), paginator);
} }
#[rstest]
#[case::saved_artists("saved_artists", path!("music_userdata" / "saved_artists.json"))]
fn map_continuation_artists(#[case] name: &str, #[case] path: PathBuf) {
let json_path = path!(*TESTFILES / path);
let json_file = File::open(json_path).unwrap();
let items: response::MusicContinuation =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Paginator<MusicItem>> =
items.map_response(&MapRespCtx::test("")).unwrap();
let paginator: Paginator<ArtistItem> =
map_ytm_paginator(map_res.c, ContinuationEndpoint::MusicBrowse);
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
insta::assert_ron_snapshot!(format!("map_{name}"), paginator);
}
#[rstest]
#[case::saved_albums("saved_albums", path!("music_userdata" / "saved_albums.json"))]
fn map_continuation_albums(#[case] name: &str, #[case] path: PathBuf) {
let json_path = path!(*TESTFILES / path);
let json_file = File::open(json_path).unwrap();
let items: response::MusicContinuation =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Paginator<MusicItem>> =
items.map_response(&MapRespCtx::test("")).unwrap();
let paginator: Paginator<AlbumItem> =
map_ytm_paginator(map_res.c, ContinuationEndpoint::MusicBrowse);
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
insta::assert_ron_snapshot!(format!("map_{name}"), paginator);
}
#[rstest] #[rstest]
#[case::playlist_related("playlist_related", path!("music_playlist" / "playlist_related.json"))] #[case::playlist_related("playlist_related", path!("music_playlist" / "playlist_related.json"))]
#[case::saved_playlists("saved_playlists", path!("music_userdata" / "saved_playlists.json"))]
fn map_continuation_music_playlists(#[case] name: &str, #[case] path: PathBuf) { fn map_continuation_music_playlists(#[case] name: &str, #[case] path: PathBuf) {
let json_path = path!(*TESTFILES / path); let json_path = path!(*TESTFILES / path);
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();

View file

@ -7,16 +7,14 @@ use std::{
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use serde::Serialize; use serde::Serialize;
use time::OffsetDateTime;
use url::Url; use url::Url;
use crate::{ use crate::{
deobfuscate::{DeobfData, Deobfuscator}, deobfuscate::Deobfuscator,
error::{internal::DeobfError, AuthError, Error, ExtractionError, UnavailabilityReason}, error::{internal::DeobfError, Error, ExtractionError, UnavailabilityReason},
model::{ model::{
traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, DrmLicense, traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, Frameset, Subtitle,
DrmSystem, Frameset, Subtitle, VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoStream,
VideoPlayerDrm, VideoStream,
}, },
util, util,
}; };
@ -26,7 +24,8 @@ use super::{
self, self,
player::{self, Format}, player::{self, Format},
}, },
ClientType, MapRespCtx, MapRespOptions, MapResponse, MapResult, PoToken, RustyPipeQuery, ClientType, MapRespCtx, MapRespCtxSource, MapResponse, MapResult, RustyPipeQuery,
DEFAULT_PLAYER_CLIENT_ORDER,
}; };
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -35,15 +34,17 @@ struct QPlayer<'a> {
/// Website playback context /// Website playback context
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
playback_context: Option<QPlaybackContext<'a>>, playback_context: Option<QPlaybackContext<'a>>,
/// Content playback nonce (mobile only, 16 random chars)
#[serde(skip_serializing_if = "Option::is_none")]
cpn: Option<String>,
/// YouTube video ID /// YouTube video ID
video_id: &'a str, video_id: &'a str,
/// Set to true to allow extraction of streams with sensitive content /// Set to true to allow extraction of streams with sensitive content
content_check_ok: bool, content_check_ok: bool,
/// Probably refers to allowing sensitive content, too /// Probably refers to allowing sensitive content, too
racy_check_ok: bool, racy_check_ok: bool,
/// Botguard data
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
service_integrity_dimensions: Option<ServiceIntegrity>, params: Option<&'a str>,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -61,35 +62,10 @@ struct QContentPlaybackContext<'a> {
referer: String, referer: String,
} }
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct QDrmLicense<'a> {
drm_system: &'a str,
video_id: &'a str,
cpn: &'a str,
session_id: &'a str,
license_request: &'a str,
drm_params: &'a str,
drm_video_feature: &'a str,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ServiceIntegrity {
po_token: String,
}
#[derive(Default)]
struct PlayerPoToken {
visitor_data: Option<String>,
session_po_token: Option<PoToken>,
content_po_token: Option<ServiceIntegrity>,
}
impl RustyPipeQuery { impl RustyPipeQuery {
/// Get YouTube player data (video/audio streams + basic metadata) /// Get YouTube player data (video/audio streams + basic metadata)
pub async fn player<S: AsRef<str> + Debug>(&self, video_id: S) -> Result<VideoPlayer, Error> { pub async fn player<S: AsRef<str> + Debug>(&self, video_id: S) -> Result<VideoPlayer, Error> {
self.player_from_clients(video_id, self.player_client_order()) self.player_from_clients(video_id, DEFAULT_PLAYER_CLIENT_ORDER)
.await .await
} }
@ -97,91 +73,48 @@ impl RustyPipeQuery {
/// ///
/// The clients are used in the given order. If a client cannot fetch the requested video, /// The clients are used in the given order. If a client cannot fetch the requested video,
/// an attempt is made with the next one. /// an attempt is made with the next one.
///
/// If an age-restricted video is detected, it will automatically use the [`ClientType::TvHtml5Embed`]
/// since it is the only one that can circumvent age restrictions.
pub async fn player_from_clients<S: AsRef<str> + Debug>( pub async fn player_from_clients<S: AsRef<str> + Debug>(
&self, &self,
video_id: S, video_id: S,
clients: &[ClientType], clients: &[ClientType],
) -> Result<VideoPlayer, Error> { ) -> Result<VideoPlayer, Error> {
let video_id = video_id.as_ref(); let video_id = video_id.as_ref();
let mut last_e = None; let mut last_e = Error::Other("no clients".into());
let mut clients_iter = clients.iter().peekable();
while let Some(client) = clients_iter.next() {
if self.opts.auth == Some(true) && !self.auth_enabled(*client) {
// If no client has auth enabled, return NoLogin error instead of "no clients"
if last_e.is_none() {
last_e = Some(Error::Auth(AuthError::NoLogin));
}
continue;
}
for client in clients {
let res = self.player_from_client(video_id, *client).await; let res = self.player_from_client(video_id, *client).await;
match res { match res {
Ok(res) => return Ok(res), Ok(res) => return Ok(res),
Err(Error::Extraction(e)) => { Err(Error::Extraction(e)) => {
if e.use_login() { if e.use_login() && self.auth_enabled() {
if let Some(c) = self.auth_enabled_client(clients) { tracing::info!("{e}; fetching player with login");
tracing::info!("{e}; fetching player with login");
match self match self
.clone() .clone()
.authenticated() .authenticated()
.player_from_client(video_id, c) .player_from_client(video_id, *client)
.await .await
{ {
Ok(res) => return Ok(res), Ok(res) => return Ok(res),
Err(Error::Extraction(e)) => { Err(Error::Extraction(e)) => {
if !e.switch_client() { if !e.switch_client() {
return Err(Error::Extraction(e)); return Err(Error::Extraction(e));
}
} }
Err(e) => return Err(e),
} }
} else { Err(e) => return Err(e),
return Err(Error::Extraction(e));
} }
last_e = Error::Extraction(e);
} else if !e.switch_client() { } else if !e.switch_client() {
return Err(Error::Extraction(e)); return Err(Error::Extraction(e));
} }
if let Some(next_client) = clients_iter.peek() {
tracing::warn!("error fetching player with {client:?} client: {e}; retrying with {next_client:?} client");
}
last_e = Some(Error::Extraction(e));
} }
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} }
Err(last_e.unwrap_or(Error::Other("no clients".into()))) Err(last_e)
}
async fn get_player_po_token(&self, video_id: &str) -> Result<PlayerPoToken, Error> {
if let Some(bg) = &self.client.inner.botguard {
let visitor_data = self.get_visitor_data(false).await?;
if bg.po_token_cache {
let session_token = self.get_session_po_token(&visitor_data).await?;
Ok(PlayerPoToken {
visitor_data: Some(visitor_data),
session_po_token: Some(session_token),
content_po_token: None,
})
} else {
let (po_tokens, valid_until) =
self.get_po_tokens(&[video_id, &visitor_data]).await?;
let mut po_tokens = po_tokens.into_iter();
let po_token = po_tokens.next().unwrap();
let session_po_token = po_tokens.next().unwrap();
Ok(PlayerPoToken {
visitor_data: Some(visitor_data),
session_po_token: Some(PoToken {
po_token: session_po_token,
valid_until,
}),
content_po_token: Some(ServiceIntegrity { po_token }),
})
}
} else {
Ok(PlayerPoToken::default())
}
} }
/// Get YouTube player data (video/audio streams + basic metadata) using the specified client /// Get YouTube player data (video/audio streams + basic metadata) using the specified client
@ -192,37 +125,32 @@ impl RustyPipeQuery {
client_type: ClientType, client_type: ClientType,
) -> Result<VideoPlayer, Error> { ) -> Result<VideoPlayer, Error> {
let video_id = video_id.as_ref(); let video_id = video_id.as_ref();
let deobf = self.client.get_deobf_data().await?;
let (deobf, player_po) = tokio::try_join!( let request_body = if client_type.is_web() {
async { QPlayer {
if client_type.needs_deobf() { playback_context: Some(QPlaybackContext {
Ok::<_, Error>(Some(self.client.get_deobf_data().await?)) content_playback_context: QContentPlaybackContext {
} else { signature_timestamp: &deobf.sts,
Ok(None) referer: format!("https://www.youtube.com/watch?v={video_id}"),
} },
}, }),
async { cpn: None,
if client_type.needs_po_token() { video_id,
self.get_player_po_token(video_id).await content_check_ok: true,
} else { racy_check_ok: true,
Ok(PlayerPoToken::default()) params: None,
} }
} else {
QPlayer {
playback_context: None,
cpn: Some(util::generate_content_playback_nonce()),
video_id,
content_check_ok: true,
racy_check_ok: true,
// Source: https://github.com/TeamNewPipe/NewPipeExtractor/pull/1168
params: Some("CgIIAQ%3D%3D").filter(|_| client_type == ClientType::Android),
} }
)?;
let playback_context = deobf.as_ref().map(|deobf| QPlaybackContext {
content_playback_context: QContentPlaybackContext {
signature_timestamp: &deobf.sts,
referer: format!("https://www.youtube.com/watch?v={video_id}"),
},
});
let request_body = QPlayer {
playback_context,
video_id,
content_check_ok: true,
racy_check_ok: true,
service_integrity_dimensions: player_po.content_po_token,
}; };
self.execute_request_ctx::<response::Player, _, _>( self.execute_request_ctx::<response::Player, _, _>(
@ -231,65 +159,13 @@ impl RustyPipeQuery {
video_id, video_id,
"player", "player",
&request_body, &request_body,
MapRespOptions { MapRespCtxSource {
visitor_data: player_po.visitor_data.as_deref(), deobf: Some(&deobf),
deobf: deobf.as_ref(),
unlocalized: true,
session_po_token: player_po.session_po_token,
..Default::default() ..Default::default()
}, },
) )
.await .await
} }
/// Get the default order of client types when fetching player data
///
/// The order may change in the future in case YouTube applies changes to their
/// platform that disable a client or make it less reliable.
pub fn player_client_order(&self) -> &'static [ClientType] {
if self.client.inner.botguard.is_some() {
&[ClientType::Desktop, ClientType::Ios, ClientType::Tv]
} else {
&[ClientType::Ios, ClientType::Tv]
}
}
/// Get a license to play back DRM protected videos
///
/// Requires authentication (either via OAuth or cookies).
#[tracing::instrument(skip(self), level = "error")]
pub async fn drm_license(
&self,
video_id: &str,
drm_system: DrmSystem,
session_id: &str,
drm_params: &str,
license_request: &[u8],
) -> Result<DrmLicense, Error> {
let client_type = self
.auth_enabled_client(&[ClientType::Desktop, ClientType::Tv])
.ok_or(Error::Auth(AuthError::NoLogin))?;
let request_body = QDrmLicense {
drm_system: drm_system.req_param(),
video_id,
cpn: &util::generate_content_playback_nonce(),
session_id,
license_request: &data_encoding::BASE64.encode(license_request),
drm_params,
drm_video_feature: "DRM_VIDEO_FEATURE_SDR",
};
self.clone()
.authenticated()
.execute_request::<response::DrmLicense, _, _>(
client_type,
"drm_license",
video_id,
"player/get_drm_license",
&request_body,
)
.await
}
} }
impl MapResponse<VideoPlayer> for response::Player { impl MapResponse<VideoPlayer> for response::Player {
@ -321,9 +197,8 @@ impl MapResponse<VideoPlayer> for response::Player {
"Premium" => Some(UnavailabilityReason::Premium), "Premium" => Some(UnavailabilityReason::Premium),
"members-only" => Some(UnavailabilityReason::MembersOnly), "members-only" => Some(UnavailabilityReason::MembersOnly),
"country" => Some(UnavailabilityReason::Geoblocked), "country" => Some(UnavailabilityReason::Geoblocked),
"version" | "websites" => Some(UnavailabilityReason::UnsupportedClient), "Android" | "websites" => Some(UnavailabilityReason::UnsupportedClient),
"bot" => Some(UnavailabilityReason::IpBan), "bot" => Some(UnavailabilityReason::IpBan),
"later." => Some(UnavailabilityReason::TryAgain),
_ => None, _ => None,
}) })
.unwrap_or_default(); .unwrap_or_default();
@ -401,10 +276,7 @@ impl MapResponse<VideoPlayer> for response::Player {
}; };
let streams = if !is_live { let streams = if !is_live {
let mut mapper = StreamsMapper::new( let mut mapper = StreamsMapper::new(Deobfuscator::new(ctx.deobf.unwrap())?);
ctx.deobf,
ctx.session_po_token.as_ref().map(|t| t.po_token.as_str()),
)?;
mapper.map_streams(streaming_data.formats); mapper.map_streams(streaming_data.formats);
mapper.map_streams(streaming_data.adaptive_formats); mapper.map_streams(streaming_data.adaptive_formats);
let mut res = mapper.output()?; let mut res = mapper.output()?;
@ -478,30 +350,6 @@ impl MapResponse<VideoPlayer> for response::Player {
}) })
.unwrap_or_default(); .unwrap_or_default();
let drm = streaming_data
.drm_params
.zip(self.heartbeat_params.drm_session_id)
.map(|(drm_params, drm_session_id)| VideoPlayerDrm {
widevine_service_cert: self
.player_config
.web_drm_config
.and_then(|c| c.widevine_service_cert)
.and_then(|c| data_encoding::BASE64URL.decode(c.as_bytes()).ok()),
drm_params,
authorized_track_types: streaming_data
.initial_authorized_drm_track_types
.into_iter()
.map(|t| t.into())
.collect(),
drm_session_id,
});
let mut valid_until = OffsetDateTime::now_utc()
+ time::Duration::seconds(streaming_data.expires_in_seconds.into());
if let Some(pot) = &ctx.session_po_token {
valid_until = valid_until.min(pot.valid_until);
}
Ok(MapResult { Ok(MapResult {
c: VideoPlayer { c: VideoPlayer {
details: video_info, details: video_info,
@ -510,11 +358,9 @@ impl MapResponse<VideoPlayer> for response::Player {
audio_streams: streams.audio_streams, audio_streams: streams.audio_streams,
subtitles, subtitles,
expires_in_seconds: streaming_data.expires_in_seconds, expires_in_seconds: streaming_data.expires_in_seconds,
valid_until,
hls_manifest_url: streaming_data.hls_manifest_url, hls_manifest_url: streaming_data.hls_manifest_url,
dash_manifest_url: streaming_data.dash_manifest_url, dash_manifest_url: streaming_data.dash_manifest_url,
preview_frames, preview_frames,
drm,
client_type: ctx.client_type, client_type: ctx.client_type,
visitor_data: self visitor_data: self
.response_context .response_context
@ -526,9 +372,8 @@ impl MapResponse<VideoPlayer> for response::Player {
} }
} }
struct StreamsMapper<'a> { struct StreamsMapper {
deobf: Option<Deobfuscator>, deobf: Deobfuscator,
session_po_token: Option<&'a str>,
streams: Streams, streams: Streams,
warnings: Vec<String>, warnings: Vec<String>,
/// First stream mapping error /// First stream mapping error
@ -546,25 +391,16 @@ struct Streams {
audio_streams: Vec<AudioStream>, audio_streams: Vec<AudioStream>,
} }
impl<'a> StreamsMapper<'a> { impl StreamsMapper {
fn new( fn new(deobf: Deobfuscator) -> Self {
deobf_data: Option<&DeobfData>, Self {
session_po_token: Option<&'a str>,
) -> Result<Self, DeobfError> {
let deobf = match deobf_data {
Some(deobf_data) => Some(Deobfuscator::new(deobf_data)?),
None => None,
};
Ok(Self {
deobf, deobf,
session_po_token,
streams: Streams::default(), streams: Streams::default(),
warnings: Vec::new(), warnings: Vec::new(),
first_err: None, first_err: None,
last_nsig: String::new(), last_nsig: String::new(),
last_nsig_deobf: String::new(), last_nsig_deobf: String::new(),
}) }
} }
fn map_streams(&mut self, mut streams: MapResult<Vec<Format>>) { fn map_streams(&mut self, mut streams: MapResult<Vec<Format>>) {
@ -624,12 +460,6 @@ impl<'a> StreamsMapper<'a> {
}) })
} }
fn deobf(&self) -> Result<&Deobfuscator, DeobfError> {
self.deobf
.as_ref()
.ok_or(DeobfError::Other("no deobfuscator"))
}
fn cipher_to_url_params( fn cipher_to_url_params(
&self, &self,
signature_cipher: &str, signature_cipher: &str,
@ -650,7 +480,7 @@ impl<'a> StreamsMapper<'a> {
let (url_base, mut url_params) = let (url_base, mut url_params) =
util::url_to_params(raw_url).or(Err(DeobfError::Extraction("url params")))?; util::url_to_params(raw_url).or(Err(DeobfError::Extraction("url params")))?;
let deobf_sig = self.deobf()?.deobfuscate_sig(sig)?; let deobf_sig = self.deobf.deobfuscate_sig(sig)?;
url_params.insert(sp.to_string(), deobf_sig); url_params.insert(sp.to_string(), deobf_sig);
Ok((url_base, url_params)) Ok((url_base, url_params))
@ -661,7 +491,7 @@ impl<'a> StreamsMapper<'a> {
let nsig = if n == &self.last_nsig { let nsig = if n == &self.last_nsig {
self.last_nsig_deobf.to_owned() self.last_nsig_deobf.to_owned()
} else { } else {
let nsig = self.deobf()?.deobfuscate_nsig(n)?; let nsig = self.deobf.deobfuscate_nsig(n)?;
self.last_nsig.clone_from(n); self.last_nsig.clone_from(n);
self.last_nsig_deobf.clone_from(&nsig); self.last_nsig_deobf.clone_from(&nsig);
nsig nsig
@ -698,10 +528,6 @@ impl<'a> StreamsMapper<'a> {
}?; }?;
self.deobf_nsig(&mut url_params)?; self.deobf_nsig(&mut url_params)?;
if let Some(pot) = self.session_po_token {
url_params.insert("pot".to_owned(), pot.to_owned());
}
let url = Url::parse_with_params(url_base.as_str(), url_params.iter()) let url = Url::parse_with_params(url_base.as_str(), url_params.iter())
.map_err(|_| ExtractionError::InvalidData("could not combine URL".into()))?; .map_err(|_| ExtractionError::InvalidData("could not combine URL".into()))?;
@ -748,8 +574,6 @@ impl<'a> StreamsMapper<'a> {
format, format,
codec: get_video_codec(codecs), codec: get_video_codec(codecs),
mime: f.mime_type, mime: f.mime_type,
drm_track_type: f.drm_track_type.map(|t| t.into()),
drm_systems: f.drm_families.into_iter().map(|t| t.into()).collect(),
}) })
} }
@ -773,11 +597,7 @@ impl<'a> StreamsMapper<'a> {
itag: f.itag, itag: f.itag,
bitrate: f.bitrate, bitrate: f.bitrate,
average_bitrate: f.average_bitrate.unwrap_or(f.bitrate), average_bitrate: f.average_bitrate.unwrap_or(f.bitrate),
size: f.content_length.ok_or_else(|| { size: f.content_length.unwrap(),
ExtractionError::InvalidData(
format!("no audio content length. itag: {}", f.itag).into(),
)
})?,
index_range: f.index_range, index_range: f.index_range,
init_range: f.init_range, init_range: f.init_range,
duration_ms: f.approx_duration_ms, duration_ms: f.approx_duration_ms,
@ -789,8 +609,6 @@ impl<'a> StreamsMapper<'a> {
track: f track: f
.audio_track .audio_track
.map(|t| self.map_audio_track(t, map_res.xtags)), .map(|t| self.map_audio_track(t, map_res.xtags)),
drm_track_type: f.drm_track_type.map(|t| t.into()),
drm_systems: f.drm_families.into_iter().map(|t| t.into()).collect(),
}) })
} }
@ -892,52 +710,17 @@ fn get_audio_codec(codecs: Vec<&str>) -> AudioCodec {
return AudioCodec::Mp4a; return AudioCodec::Mp4a;
} else if codec.starts_with("opus") { } else if codec.starts_with("opus") {
return AudioCodec::Opus; return AudioCodec::Opus;
} else if codec.starts_with("ac-3") {
return AudioCodec::Ac3;
} else if codec.starts_with("ec-3") {
return AudioCodec::Ec3;
} }
} }
AudioCodec::Unknown AudioCodec::Unknown
} }
impl MapResponse<DrmLicense> for response::DrmLicense {
fn map_response(self, _ctx: &MapRespCtx<'_>) -> Result<MapResult<DrmLicense>, ExtractionError> {
if self.status != "LICENSE_STATUS_OK" {
return Err(ExtractionError::InvalidData(self.status.into()));
}
let license = DrmLicense {
license: data_encoding::BASE64URL
.decode(self.license.as_bytes())
.map_err(|_| ExtractionError::InvalidData("license: invalid b64".into()))?,
authorized_formats: self
.authorized_formats
.into_iter()
.filter_map(|f| {
let key: Option<[u8; 16]> = data_encoding::BASE64URL
.decode(f.key_id.as_bytes())
.ok()
.and_then(|k| k.try_into().ok());
key.map(|k| (f.track_type.into(), k))
})
.collect(),
};
Ok(MapResult {
c: license,
warnings: Vec::new(),
})
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{fs::File, io::BufReader}; use std::{fs::File, io::BufReader};
use path_macro::path; use path_macro::path;
use rstest::rstest; use rstest::rstest;
use time::UtcOffset;
use super::*; use super::*;
use crate::{deobfuscate::DeobfData, param::Language, util::tests::TESTFILES}; use crate::{deobfuscate::DeobfData, param::Language, util::tests::TESTFILES};
@ -969,13 +752,10 @@ mod tests {
.map_response(&MapRespCtx { .map_response(&MapRespCtx {
id: "pPvd8UxmSbQ", id: "pPvd8UxmSbQ",
lang: Language::En, lang: Language::En,
utc_offset: UtcOffset::UTC,
deobf: Some(&DEOBF_DATA), deobf: Some(&DEOBF_DATA),
visitor_data: None, visitor_data: None,
client_type, client_type,
artist: None, artist: None,
authenticated: false,
session_po_token: None,
}) })
.unwrap(); .unwrap();
@ -984,15 +764,24 @@ mod tests {
"deserialization/mapping warnings: {:?}", "deserialization/mapping warnings: {:?}",
map_res.warnings map_res.warnings
); );
let is_desktop = name == "desktop" || name == "desktopmusic";
insta::assert_ron_snapshot!(format!("map_player_data_{name}"), map_res.c, { insta::assert_ron_snapshot!(format!("map_player_data_{name}"), map_res.c, {
".valid_until" => "[date]" ".details.publish_date" => insta::dynamic_redaction(move |value, _path| {
if is_desktop {
assert!(value.as_str().unwrap().starts_with("2019-05-30T00:00:00"));
"2019-05-30T00:00:00"
} else {
assert_eq!(value, insta::internals::Content::None);
"~"
}
}),
}); });
} }
#[test] #[test]
fn cipher_to_url() { fn cipher_to_url() {
let signature_cipher = "s=w%3DAe%3DA6aDNQLkViKS7LOm9QtxZJHKwb53riq9qEFw-ecBWJCAiA%3DcEg0tn3dty9jEHszfzh4Ud__bg9CEHVx4ix-7dKsIPAhIQRw8JQ0qOA&sp=sig&url=https://rr5---sn-h0jelnez.googlevideo.com/videoplayback%3Fexpire%3D1659376413%26ei%3Dvb7nYvH5BMK8gAfBj7ToBQ%26ip%3D2003%253Ade%253Aaf06%253A6300%253Ac750%253A1b77%253Ac74a%253A80e3%26id%3Do-AB_BABwrXZJN428ZwDxq5ScPn2AbcGODnRlTVhCQ3mj2%26itag%3D251%26source%3Dyoutube%26requiressl%3Dyes%26mh%3DhH%26mm%3D31%252C26%26mn%3Dsn-h0jelnez%252Csn-4g5ednsl%26ms%3Dau%252Conr%26mv%3Dm%26mvi%3D5%26pl%3D37%26initcwndbps%3D1588750%26spc%3DlT-Khi831z8dTejFIRCvCEwx_6romtM%26vprv%3D1%26mime%3Daudio%252Fwebm%26ns%3Db_Mq_qlTFcSGlG9RpwpM9xQH%26gir%3Dyes%26clen%3D3781277%26dur%3D229.301%26lmt%3D1655510291473933%26mt%3D1659354538%26fvip%3D5%26keepalive%3Dyes%26fexp%3D24001373%252C24007246%26c%3DWEB%26rbqsm%3Dfr%26txp%3D4532434%26n%3Dd2g6G2hVqWIXxedQ%26sparams%3Dexpire%252Cei%252Cip%252Cid%252Citag%252Csource%252Crequiressl%252Cspc%252Cvprv%252Cmime%252Cns%252Cgir%252Cclen%252Cdur%252Clmt%26lsparams%3Dmh%252Cmm%252Cmn%252Cms%252Cmv%252Cmvi%252Cpl%252Cinitcwndbps%26lsig%3DAG3C_xAwRQIgCKCGJ1iu4wlaGXy3jcJyU3inh9dr1FIfqYOZEG_MdmACIQCbungkQYFk7EhD6K2YvLaHFMjKOFWjw001_tLb0lPDtg%253D%253D"; let signature_cipher = "s=w%3DAe%3DA6aDNQLkViKS7LOm9QtxZJHKwb53riq9qEFw-ecBWJCAiA%3DcEg0tn3dty9jEHszfzh4Ud__bg9CEHVx4ix-7dKsIPAhIQRw8JQ0qOA&sp=sig&url=https://rr5---sn-h0jelnez.googlevideo.com/videoplayback%3Fexpire%3D1659376413%26ei%3Dvb7nYvH5BMK8gAfBj7ToBQ%26ip%3D2003%253Ade%253Aaf06%253A6300%253Ac750%253A1b77%253Ac74a%253A80e3%26id%3Do-AB_BABwrXZJN428ZwDxq5ScPn2AbcGODnRlTVhCQ3mj2%26itag%3D251%26source%3Dyoutube%26requiressl%3Dyes%26mh%3DhH%26mm%3D31%252C26%26mn%3Dsn-h0jelnez%252Csn-4g5ednsl%26ms%3Dau%252Conr%26mv%3Dm%26mvi%3D5%26pl%3D37%26initcwndbps%3D1588750%26spc%3DlT-Khi831z8dTejFIRCvCEwx_6romtM%26vprv%3D1%26mime%3Daudio%252Fwebm%26ns%3Db_Mq_qlTFcSGlG9RpwpM9xQH%26gir%3Dyes%26clen%3D3781277%26dur%3D229.301%26lmt%3D1655510291473933%26mt%3D1659354538%26fvip%3D5%26keepalive%3Dyes%26fexp%3D24001373%252C24007246%26c%3DWEB%26rbqsm%3Dfr%26txp%3D4532434%26n%3Dd2g6G2hVqWIXxedQ%26sparams%3Dexpire%252Cei%252Cip%252Cid%252Citag%252Csource%252Crequiressl%252Cspc%252Cvprv%252Cmime%252Cns%252Cgir%252Cclen%252Cdur%252Clmt%26lsparams%3Dmh%252Cmm%252Cmn%252Cms%252Cmv%252Cmvi%252Cpl%252Cinitcwndbps%26lsig%3DAG3C_xAwRQIgCKCGJ1iu4wlaGXy3jcJyU3inh9dr1FIfqYOZEG_MdmACIQCbungkQYFk7EhD6K2YvLaHFMjKOFWjw001_tLb0lPDtg%253D%253D";
let mut mapper = StreamsMapper::new(Some(&DEOBF_DATA), None).unwrap(); let mut mapper = StreamsMapper::new(Deobfuscator::new(&DEOBF_DATA).unwrap());
let url = mapper let url = mapper
.map_url(&None, &Some(signature_cipher.to_owned())) .map_url(&None, &Some(signature_cipher.to_owned()))
.unwrap() .unwrap()

View file

@ -203,13 +203,8 @@ impl MapResponse<Playlist> for response::Playlist {
.as_deref() .as_deref()
.or(last_update_txt2.as_deref()) .or(last_update_txt2.as_deref())
.and_then(|txt| { .and_then(|txt| {
timeago::parse_textual_date_or_warn( timeago::parse_textual_date_or_warn(ctx.lang, txt, &mut mapper.warnings)
ctx.lang, .map(OffsetDateTime::date)
ctx.utc_offset,
txt,
&mut mapper.warnings,
)
.map(OffsetDateTime::date)
}); });
Ok(MapResult { Ok(MapResult {
@ -222,7 +217,6 @@ impl MapResponse<Playlist> for response::Playlist {
mapper.ctoken, mapper.ctoken,
ctx.visitor_data.map(str::to_owned), ctx.visitor_data.map(str::to_owned),
ContinuationEndpoint::Browse, ContinuationEndpoint::Browse,
ctx.authenticated,
), ),
video_count: n_videos, video_count: n_videos,
thumbnail: thumbnails.into(), thumbnail: thumbnails.into(),

View file

@ -2,14 +2,11 @@ use serde::Deserialize;
use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError}; use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError};
use super::{ use super::{
video_item::YouTubeListRenderer, Alert, AttachmentRun, AvatarViewModel, ChannelBadge, video_item::YouTubeListRenderer, Alert, ChannelBadge, ContentRenderer, ContentsRenderer,
ContentRenderer, ContentsRenderer, ContinuationActionWrap, ImageView, ContinuationActionWrap, ImageView, PageHeaderRendererContent, PhMetadataView, ResponseContext,
PageHeaderRendererContent, PhMetadataView, ResponseContext, Thumbnails, TwoColumnBrowseResults, Thumbnails, TwoColumnBrowseResults,
};
use crate::{
model::Verification,
serializer::text::{AttributedText, Text, TextComponent},
}; };
use crate::serializer::text::{AttributedText, Text, TextComponent};
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -124,7 +121,7 @@ pub(crate) enum CarouselHeaderRendererItem {
pub(crate) struct PageHeaderRendererInner { pub(crate) struct PageHeaderRendererInner {
/// Channel title (only used to extract verification badges) /// Channel title (only used to extract verification badges)
#[serde_as(as = "DefaultOnError")] #[serde_as(as = "DefaultOnError")]
pub title: Option<PhTitleView>, pub title: PhTitleView,
/// Channel avatar /// Channel avatar
pub image: PhAvatarView, pub image: PhAvatarView,
/// Channel metadata (subscribers, video count) /// Channel metadata (subscribers, video count)
@ -133,7 +130,7 @@ pub(crate) struct PageHeaderRendererInner {
pub banner: PhBannerView, pub banner: PhBannerView,
} }
#[derive(Debug, Deserialize)] #[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PhTitleView { pub(crate) struct PhTitleView {
pub dynamic_text_view_model: PhTitleView2, pub dynamic_text_view_model: PhTitleView2,
@ -153,6 +150,58 @@ pub(crate) struct PhTitleView3 {
pub attachment_runs: Vec<AttachmentRun>, pub attachment_runs: Vec<AttachmentRun>,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRun {
pub element: AttachmentRunElement,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElement {
#[serde(rename = "type")]
pub typ: AttachmentRunElementType,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementType {
pub image_type: AttachmentRunElementImageType,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImageType {
pub image: AttachmentRunElementImage,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImage {
#[serde_as(as = "VecSkipError<_>")]
pub sources: Vec<AttachmentRunElementImageSource>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImageSource {
pub client_resource: ClientResource,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ClientResource {
pub image_name: IconName,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum IconName {
CheckCircleFilled,
MusicFilled,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PhAvatarView { pub(crate) struct PhAvatarView {
@ -162,7 +211,13 @@ pub(crate) struct PhAvatarView {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PhAvatarView2 { pub(crate) struct PhAvatarView2 {
pub avatar: AvatarViewModel, pub avatar: PhAvatarView3,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhAvatarView3 {
pub avatar_view_model: ImageView,
} }
#[derive(Default, Debug, Deserialize)] #[derive(Default, Debug, Deserialize)]
@ -275,9 +330,15 @@ impl From<PhTitleView> for crate::model::Verification {
.dynamic_text_view_model .dynamic_text_view_model
.text .text
.attachment_runs .attachment_runs
.into_iter() .iter()
.next() .find_map(|r| {
.map(Verification::from) r.element.typ.image_type.image.sources.first().map(|s| {
match s.client_resource.image_name {
IconName::CheckCircleFilled => crate::model::Verification::Verified,
IconName::MusicFilled => crate::model::Verification::Artist,
}
})
})
.unwrap_or_default() .unwrap_or_default()
} }
} }

View file

@ -1,8 +0,0 @@
use serde::Deserialize;
use super::{video_item::YouTubeListRendererWrap, Tab, TwoColumnBrowseResults};
#[derive(Debug, Deserialize)]
pub(crate) struct History {
pub contents: TwoColumnBrowseResults<Tab<YouTubeListRendererWrap>>,
}

View file

@ -30,7 +30,6 @@ pub(crate) use music_new::MusicNew;
pub(crate) use music_playlist::MusicPlaylist; pub(crate) use music_playlist::MusicPlaylist;
pub(crate) use music_search::MusicSearch; pub(crate) use music_search::MusicSearch;
pub(crate) use music_search::MusicSearchSuggestion; pub(crate) use music_search::MusicSearchSuggestion;
pub(crate) use player::DrmLicense;
pub(crate) use player::Player; pub(crate) use player::Player;
pub(crate) use playlist::Playlist; pub(crate) use playlist::Playlist;
pub(crate) use search::Search; pub(crate) use search::Search;
@ -47,15 +46,6 @@ pub(crate) mod channel_rss;
#[cfg(feature = "rss")] #[cfg(feature = "rss")]
pub(crate) use channel_rss::ChannelRss; pub(crate) use channel_rss::ChannelRss;
#[cfg(feature = "userdata")]
pub(crate) mod history;
#[cfg(feature = "userdata")]
pub(crate) use history::History;
#[cfg(feature = "userdata")]
pub(crate) mod music_history;
#[cfg(feature = "userdata")]
pub(crate) use music_history::MusicHistory;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -123,12 +113,6 @@ pub(crate) struct ImageView {
pub image: Thumbnails, pub image: Thumbnails,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AvatarViewModel {
pub avatar_view_model: ImageView,
}
/// List of images in different resolutions. /// List of images in different resolutions.
/// Not only used for thumbnails, but also for avatars and banners. /// Not only used for thumbnails, but also for avatars and banners.
#[derive(Default, Debug, Deserialize)] #[derive(Default, Debug, Deserialize)]
@ -204,92 +188,23 @@ pub(crate) enum ChannelBadgeStyle {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Alert { pub(crate) struct Alert {
pub alert_renderer: TextBox, pub alert_renderer: AlertRenderer,
} }
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct TextBox { pub(crate) struct AlertRenderer {
#[serde_as(as = "Text")] #[serde_as(as = "Text")]
pub text: String, pub text: String,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SimpleHeaderRenderer {
#[serde_as(as = "Text")]
pub title: String,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TextComponentBox {
#[serde_as(as = "AttributedText")]
pub text: TextComponent,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ResponseContext { pub(crate) struct ResponseContext {
pub visitor_data: Option<String>, pub visitor_data: Option<String>,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRun {
pub element: AttachmentRunElement,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElement {
#[serde(rename = "type")]
pub typ: AttachmentRunElementType,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementType {
pub image_type: AttachmentRunElementImageType,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImageType {
pub image: AttachmentRunElementImage,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImage {
#[serde_as(as = "VecSkipError<_>")]
pub sources: Vec<AttachmentRunElementImageSource>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AttachmentRunElementImageSource {
pub client_resource: ClientResource,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ClientResource {
pub image_name: IconName,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum IconName {
CheckCircleFilled,
#[serde(alias = "AUDIO_BADGE")]
MusicFilled,
}
// CONTINUATION // CONTINUATION
#[serde_as] #[serde_as]
@ -428,17 +343,6 @@ impl From<Thumbnails> for Vec<crate::model::Thumbnail> {
} }
} }
impl ContentImage {
pub(crate) fn into_image(self) -> ImageViewOl {
match self {
ContentImage::ThumbnailViewModel(image) => image,
ContentImage::CollectionThumbnailViewModel { primary_thumbnail } => {
primary_thumbnail.thumbnail_view_model
}
}
}
}
impl From<Vec<ChannelBadge>> for crate::model::Verification { impl From<Vec<ChannelBadge>> for crate::model::Verification {
fn from(badges: Vec<ChannelBadge>) -> Self { fn from(badges: Vec<ChannelBadge>) -> Self {
badges badges
@ -462,25 +366,6 @@ impl From<Icon> for crate::model::Verification {
} }
} }
impl From<AttachmentRun> for crate::model::Verification {
fn from(value: AttachmentRun) -> Self {
match value
.element
.typ
.image_type
.image
.sources
.into_iter()
.next()
.map(|s| s.client_resource.image_name)
{
Some(IconName::CheckCircleFilled) => Self::Verified,
Some(IconName::MusicFilled) => Self::Artist,
None => Self::None,
}
}
}
pub(crate) fn alerts_to_err(id: &str, alerts: Option<Vec<Alert>>) -> ExtractionError { pub(crate) fn alerts_to_err(id: &str, alerts: Option<Vec<Alert>>) -> ExtractionError {
ExtractionError::NotFound { ExtractionError::NotFound {
id: id.to_owned(), id: id.to_owned(),
@ -595,11 +480,9 @@ pub(crate) struct PhMetadataView {
pub content_metadata_view_model: PhMetadataView2, pub content_metadata_view_model: PhMetadataView2,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataView2 { pub(crate) struct PhMetadataView2 {
#[serde_as(as = "VecSkipError<_>")]
pub metadata_rows: Vec<PhMetadataRow>, pub metadata_rows: Vec<PhMetadataRow>,
} }
@ -615,26 +498,17 @@ pub(crate) struct PhMetadataRow {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum MetadataPart { pub(crate) enum MetadataPart {
Text(#[serde_as(as = "AttributedText")] TextComponent), Text(#[serde_as(deserialize_as = "AttributedText")] String),
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
AvatarStack { AvatarStack {
avatar_stack_view_model: TextComponentBox, avatar_stack_view_model: AvatarStackViewModel,
}, },
} }
impl MetadataPart { impl MetadataPart {
pub fn into_text_component(self) -> TextComponent {
match self {
MetadataPart::Text(text_component) => text_component,
MetadataPart::AvatarStack {
avatar_stack_view_model,
} => avatar_stack_view_model.text,
}
}
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
match self { match self {
MetadataPart::Text(s) => s.as_str(), MetadataPart::Text(s) => s,
MetadataPart::AvatarStack { MetadataPart::AvatarStack {
avatar_stack_view_model, avatar_stack_view_model,
} => avatar_stack_view_model.text.as_str(), } => avatar_stack_view_model.text.as_str(),
@ -642,50 +516,10 @@ impl MetadataPart {
} }
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) enum ContentImage {
ThumbnailViewModel(ImageViewOl),
#[serde(rename_all = "camelCase")]
CollectionThumbnailViewModel {
primary_thumbnail: ThumbnailViewModelWrap,
},
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ThumbnailViewModelWrap {
pub thumbnail_view_model: ImageViewOl,
}
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ImageViewOl { pub(crate) struct AvatarStackViewModel {
pub image: Thumbnails, #[serde_as(deserialize_as = "AttributedText")]
#[serde_as(as = "VecSkipError<_>")] pub text: TextComponent,
pub overlays: Vec<ImageViewOverlay>,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ImageViewOverlay {
pub thumbnail_overlay_badge_view_model: ThumbnailOverlayBadgeViewModel,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ThumbnailOverlayBadgeViewModel {
#[serde_as(as = "VecSkipError<_>")]
pub thumbnail_badges: Vec<ThumbnailBadges>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ThumbnailBadges {
pub thumbnail_badge_view_model: TextBox,
}
#[derive(Debug, Deserialize)]
pub(crate) struct Empty {}

View file

@ -14,7 +14,7 @@ use super::{
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicArtist { pub(crate) struct MusicArtist {
pub contents: SingleColumnBrowseResult<Tab<SectionList<ItemSection>>>, pub contents: SingleColumnBrowseResult<Tab<Option<SectionList<ItemSection>>>>,
pub header: Header, pub header: Header,
} }

View file

@ -3,8 +3,8 @@ use serde_with::{serde_as, DefaultOnError, VecSkipError};
use crate::serializer::text::Text; use crate::serializer::text::Text;
use super::AlertRenderer;
use super::ContentsRenderer; use super::ContentsRenderer;
use super::TextBox;
use super::{ use super::{
music_item::{ItemSection, PlaylistPanelRenderer}, music_item::{ItemSection, PlaylistPanelRenderer},
ContentRenderer, ContentRenderer,
@ -115,7 +115,7 @@ pub(crate) struct MusicLyrics {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum ListOrMessage<T> { pub(crate) enum ListOrMessage<T> {
SectionListRenderer(ContentsRenderer<T>), SectionListRenderer(ContentsRenderer<T>),
MessageRenderer(TextBox), MessageRenderer(AlertRenderer),
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View file

@ -1,8 +0,0 @@
use serde::Deserialize;
use super::music_playlist::Contents;
#[derive(Debug, Deserialize)]
pub(crate) struct MusicHistory {
pub contents: Contents,
}

View file

@ -4,7 +4,7 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkip
use crate::{ use crate::{
model::{ model::{
self, traits::FromYtItem, AlbumId, AlbumItem, AlbumType, ArtistId, ArtistItem, ChannelId, self, traits::FromYtItem, AlbumId, AlbumItem, AlbumType, ArtistId, ArtistItem, ChannelId,
MusicItem, MusicItemType, MusicPlaylistItem, TrackItem, UserItem, MusicItem, MusicItemType, MusicPlaylistItem, TrackItem,
}, },
param::Language, param::Language,
serializer::{ serializer::{
@ -18,15 +18,10 @@ use super::{
url_endpoint::{ url_endpoint::{
BrowseEndpointWrap, MusicPage, MusicPageType, MusicVideoType, NavigationEndpoint, PageType, BrowseEndpointWrap, MusicPage, MusicPageType, MusicVideoType, NavigationEndpoint, PageType,
}, },
ContentsRenderer, ContinuationActionWrap, ContinuationEndpoint, MusicContinuationData, ContentsRenderer, MusicContinuationData, Thumbnails, ThumbnailsWrap,
SimpleHeaderRenderer, Thumbnails, ThumbnailsWrap,
}; };
#[cfg(feature = "userdata")] #[serde_as]
use crate::model::HistoryItem;
#[cfg(feature = "userdata")]
use time::UtcOffset;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum ItemSection { pub(crate) enum ItemSection {
@ -44,9 +39,6 @@ pub(crate) enum ItemSection {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicShelf { pub(crate) struct MusicShelf {
#[cfg(feature = "userdata")]
#[serde_as(as = "Option<Text>")]
pub title: Option<String>,
/// Playlist ID (only for playlists) /// Playlist ID (only for playlists)
pub playlist_id: Option<String>, pub playlist_id: Option<String>,
pub contents: MapResult<Vec<MusicResponseItem>>, pub contents: MapResult<Vec<MusicResponseItem>>,
@ -93,10 +85,6 @@ pub(crate) enum MusicResponseItem {
MusicResponsiveListItemRenderer(ListMusicItem), MusicResponsiveListItemRenderer(ListMusicItem),
MusicTwoRowItemRenderer(CoverMusicItem), MusicTwoRowItemRenderer(CoverMusicItem),
MessageRenderer(serde::de::IgnoredAny), MessageRenderer(serde::de::IgnoredAny),
#[serde(rename_all = "camelCase")]
ContinuationItemRenderer {
continuation_endpoint: ContinuationEndpoint,
},
} }
#[serde_as] #[serde_as]
@ -284,7 +272,7 @@ pub(crate) struct QueueMusicItem {
#[derive(Default, Debug, Deserialize)] #[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicThumbnailRenderer { pub(crate) struct MusicThumbnailRenderer {
#[serde(default, alias = "croppedSquareThumbnailRenderer")] #[serde(alias = "croppedSquareThumbnailRenderer")]
pub music_thumbnail_renderer: ThumbnailsWrap, pub music_thumbnail_renderer: ThumbnailsWrap,
} }
@ -333,14 +321,10 @@ impl From<MusicThumbnailRenderer> for Vec<model::Thumbnail> {
} }
/// Music list continuation response model /// Music list continuation response model
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicContinuation { pub(crate) struct MusicContinuation {
pub continuation_contents: Option<ContinuationContents>, pub continuation_contents: Option<ContinuationContents>,
#[serde(default)]
#[serde_as(as = "VecSkipError<_>")]
pub on_response_received_actions: Vec<ContinuationActionWrap<MusicResponseItem>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -412,7 +396,15 @@ pub(crate) struct GridRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct GridHeader { pub(crate) struct GridHeader {
pub grid_header_renderer: SimpleHeaderRenderer, pub grid_header_renderer: GridHeaderRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GridHeaderRenderer {
#[serde_as(as = "Text")]
pub title: String,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -427,6 +419,14 @@ pub(crate) struct SimpleHeader {
pub music_header_renderer: SimpleHeaderRenderer, pub music_header_renderer: SimpleHeaderRenderer,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SimpleHeaderRenderer {
#[serde_as(as = "Text")]
pub title: String,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum TrackBadge { pub(crate) enum TrackBadge {
@ -443,13 +443,10 @@ pub(crate) struct MusicListMapper {
/// Artists list + various artists flag /// Artists list + various artists flag
artists: Option<(Vec<ArtistId>, bool)>, artists: Option<(Vec<ArtistId>, bool)>,
album: Option<AlbumId>, album: Option<AlbumId>,
/// Default album type in case an album is unlabeled
pub album_type: AlbumType,
artist_page: bool, artist_page: bool,
search_suggestion: bool, search_suggestion: bool,
items: Vec<MusicItem>, items: Vec<MusicItem>,
warnings: Vec<String>, warnings: Vec<String>,
pub ctoken: Option<String>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -466,12 +463,10 @@ impl MusicListMapper {
lang, lang,
artists: None, artists: None,
album: None, album: None,
album_type: AlbumType::Single,
artist_page: false, artist_page: false,
search_suggestion: false, search_suggestion: false,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
ctoken: None,
} }
} }
@ -480,12 +475,10 @@ impl MusicListMapper {
lang, lang,
artists: None, artists: None,
album: None, album: None,
album_type: AlbumType::Single,
artist_page: false, artist_page: false,
search_suggestion: true, search_suggestion: true,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
ctoken: None,
} }
} }
@ -495,12 +488,10 @@ impl MusicListMapper {
lang, lang,
artists: Some((vec![artist], false)), artists: Some((vec![artist], false)),
album: None, album: None,
album_type: AlbumType::Single,
artist_page: true, artist_page: true,
search_suggestion: false, search_suggestion: false,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
ctoken: None,
} }
} }
@ -510,12 +501,10 @@ impl MusicListMapper {
lang, lang,
artists: Some((artists, by_va)), artists: Some((artists, by_va)),
album: Some(album), album: Some(album),
album_type: AlbumType::Single,
artist_page: false, artist_page: false,
search_suggestion: false, search_suggestion: false,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
ctoken: None,
} }
} }
@ -527,12 +516,6 @@ impl MusicListMapper {
// Tile // Tile
MusicResponseItem::MusicTwoRowItemRenderer(item) => self.map_tile(item), MusicResponseItem::MusicTwoRowItemRenderer(item) => self.map_tile(item),
MusicResponseItem::MessageRenderer(_) => Ok(None), MusicResponseItem::MessageRenderer(_) => Ok(None),
MusicResponseItem::ContinuationItemRenderer {
continuation_endpoint,
} => {
self.ctoken = Some(continuation_endpoint.continuation_command.token);
Ok(None)
}
} }
} }
@ -552,7 +535,7 @@ impl MusicListMapper {
etype etype
} }
/// Map a ListMusicItem (album/playlist item, search result) /// Map a ListMusicItem (album/playlist tile)
fn map_list_item(&mut self, item: ListMusicItem) -> Result<Option<MusicItemType>, String> { fn map_list_item(&mut self, item: ListMusicItem) -> Result<Option<MusicItemType>, String> {
let mut columns = item.flex_columns.into_iter(); let mut columns = item.flex_columns.into_iter();
let c1 = columns.next(); let c1 = columns.next();
@ -776,7 +759,7 @@ impl MusicListMapper {
artist_id, artist_id,
album, album,
view_count, view_count,
track_type: vtype.into(), is_video: vtype.is_video(),
track_nr, track_nr,
by_va, by_va,
})); }));
@ -784,16 +767,8 @@ impl MusicListMapper {
} }
// Artist / Album / Playlist // Artist / Album / Playlist
Some((page_type, id)) => { Some((page_type, id)) => {
// Ignore "Shuffle all" button and builtin "Liked music" and "Saved episodes" playlists
if page_type == MusicPageType::None
|| (page_type == (MusicPageType::Playlist { is_podcast: false })
&& matches!(id.as_str(), "MLCT" | "LM" | "SE"))
{
return Ok(None);
}
let mut subtitle_parts = c2 let mut subtitle_parts = c2
.ok_or_else(|| format!("{id}: could not get subtitle"))? .ok_or_else(|| "could not get subtitle".to_owned())?
.renderer .renderer
.text .text
.split(util::DOT_SEPARATOR) .split(util::DOT_SEPARATOR)
@ -854,7 +829,7 @@ impl MusicListMapper {
})); }));
Ok(Some(MusicItemType::Album)) Ok(Some(MusicItemType::Album))
} }
MusicPageType::Playlist { is_podcast } => { MusicPageType::Playlist => {
// Part 1 may be the "Playlist" label // Part 1 may be the "Playlist" label
let (channel_p, tcount_p) = match subtitle_p3 { let (channel_p, tcount_p) = match subtitle_p3 {
Some(_) => (subtitle_p2, subtitle_p3), Some(_) => (subtitle_p2, subtitle_p3),
@ -880,23 +855,9 @@ impl MusicListMapper {
channel, channel,
track_count, track_count,
from_ytm, from_ytm,
is_podcast,
})); }));
Ok(Some(MusicItemType::Playlist)) Ok(Some(MusicItemType::Playlist))
} }
MusicPageType::User => {
// Part 1 may be the "Profile" label
let handle = map_channel_handle(subtitle_p2.as_ref())
.or_else(|| map_channel_handle(subtitle_p1.as_ref()));
self.items.push(MusicItem::User(UserItem {
id,
name: title,
handle,
avatar: item.thumbnail.into(),
}));
Ok(Some(MusicItemType::User))
}
MusicPageType::None => { MusicPageType::None => {
// There may be broken YT channels from the artist search. They can be skipped. // There may be broken YT channels from the artist search. They can be skipped.
Ok(None) Ok(None)
@ -956,7 +917,7 @@ impl MusicListMapper {
artists, artists,
album: None, album: None,
view_count, view_count,
track_type: vtype.into(), is_video: vtype.is_video(),
track_nr: None, track_nr: None,
by_va, by_va,
})); }));
@ -981,7 +942,7 @@ impl MusicListMapper {
} }
MusicPageType::Album => { MusicPageType::Album => {
let mut year = None; let mut year = None;
let mut album_type = self.album_type; let mut album_type = AlbumType::Single;
let (artists, by_va) = let (artists, by_va) =
match (subtitle_p1, subtitle_p2, &self.artists, self.artist_page) { match (subtitle_p1, subtitle_p2, &self.artists, self.artist_page) {
@ -1028,7 +989,7 @@ impl MusicListMapper {
})); }));
Ok(Some(MusicItemType::Album)) Ok(Some(MusicItemType::Album))
} }
MusicPageType::Playlist { is_podcast } => { MusicPageType::Playlist => {
// When the playlist subtitle has only 1 part, it is a playlist from YT Music // When the playlist subtitle has only 1 part, it is a playlist from YT Music
// (featured on the startpage or in genres) // (featured on the startpage or in genres)
let from_ytm = subtitle_p2 let from_ytm = subtitle_p2
@ -1045,11 +1006,10 @@ impl MusicListMapper {
channel, channel,
track_count: None, track_count: None,
from_ytm, from_ytm,
is_podcast,
})); }));
Ok(Some(MusicItemType::Playlist)) Ok(Some(MusicItemType::Playlist))
} }
MusicPageType::None | MusicPageType::User => Ok(None), MusicPageType::None => Ok(None),
}, },
None => Err("could not determine item type".to_owned()), None => Err("could not determine item type".to_owned()),
} }
@ -1120,7 +1080,7 @@ impl MusicListMapper {
artists, artists,
album: None, album: None,
view_count: None, view_count: None,
track_type: vtype.into(), is_video: vtype.is_video(),
track_nr: None, track_nr: None,
by_va, by_va,
})); }));
@ -1157,14 +1117,14 @@ impl MusicListMapper {
artists, artists,
album, album,
view_count, view_count,
track_type: vtype.into(), is_video: vtype.is_video(),
track_nr: None, track_nr: None,
by_va, by_va,
})); }));
} }
Some(MusicItemType::Track) Some(MusicItemType::Track)
} }
MusicPageType::Playlist { is_podcast } => { MusicPageType::Playlist => {
let from_ytm = subtitle_p2 let from_ytm = subtitle_p2
.as_ref() .as_ref()
.and_then(|p| p.0.first()) .and_then(|p| p.0.first())
@ -1181,23 +1141,9 @@ impl MusicListMapper {
channel, channel,
track_count, track_count,
from_ytm, from_ytm,
is_podcast,
})); }));
Some(MusicItemType::Playlist) Some(MusicItemType::Playlist)
} }
MusicPageType::User => {
// Part 1 may be the "Profile" label
let handle = map_channel_handle(subtitle_p2.as_ref())
.or_else(|| map_channel_handle(subtitle_p1.as_ref()));
self.items.push(MusicItem::User(UserItem {
id: music_page.id,
name: card.title,
handle,
avatar: card.thumbnail.into(),
}));
Some(MusicItemType::User)
}
MusicPageType::None => None, MusicPageType::None => None,
}, },
None => { None => {
@ -1260,7 +1206,6 @@ impl MusicListMapper {
MusicItem::Album(album) => albums.push(album), MusicItem::Album(album) => albums.push(album),
MusicItem::Artist(artist) => artists.push(artist), MusicItem::Artist(artist) => artists.push(artist),
MusicItem::Playlist(playlist) => playlists.push(playlist), MusicItem::Playlist(playlist) => playlists.push(playlist),
MusicItem::User(_) => {}
} }
} }
@ -1274,33 +1219,6 @@ impl MusicListMapper {
warnings: self.warnings, warnings: self.warnings,
} }
} }
#[cfg(feature = "userdata")]
pub fn conv_history_items(
self,
date_txt: Option<String>,
utc_offset: UtcOffset,
res: &mut MapResult<Vec<HistoryItem<TrackItem>>>,
) {
res.warnings.extend(self.warnings);
res.c.extend(
self.items
.into_iter()
.filter_map(TrackItem::from_ytm_item)
.map(|item| HistoryItem {
item,
playback_date: date_txt.as_deref().and_then(|s| {
timeago::parse_textual_date_to_d(
self.lang,
utc_offset,
s,
&mut res.warnings,
)
}),
playback_date_txt: date_txt.clone(),
}),
);
}
} }
/// Map TextComponents containing artist names to a list of artists and a 'Various Artists' flag /// Map TextComponents containing artist names to a list of artists and a 'Various Artists' flag
@ -1338,12 +1256,6 @@ fn map_artist_id_fallback(
.or_else(|| fallback_artist.and_then(|a| a.id.clone())) .or_else(|| fallback_artist.and_then(|a| a.id.clone()))
} }
fn map_channel_handle(st: Option<&TextComponents>) -> Option<String> {
st.map(|t| t.first_str())
.filter(|t| t.starts_with('@'))
.map(str::to_owned)
}
pub(crate) fn map_artist_id(entries: Vec<MusicItemMenuEntry>) -> Option<String> { pub(crate) fn map_artist_id(entries: Vec<MusicItemMenuEntry>) -> Option<String> {
entries.into_iter().find_map(|i| { entries.into_iter().find_map(|i| {
if let NavigationEndpoint::Browse { if let NavigationEndpoint::Browse {
@ -1414,7 +1326,7 @@ pub(crate) fn map_queue_item(item: QueueMusicItem, lang: Language) -> MapResult<
artist_id, artist_id,
album, album,
view_count, view_count,
track_type: MusicVideoType::from_is_video(is_video).into(), is_video,
track_nr: None, track_nr: None,
by_va, by_va,
}, },
@ -1435,18 +1347,13 @@ mod tests {
fn map_album_type_samples() { fn map_album_type_samples() {
let json_path = path!(*TESTFILES / "dict" / "album_type_samples.json"); let json_path = path!(*TESTFILES / "dict" / "album_type_samples.json");
let json_file = File::open(json_path).unwrap(); let json_file = File::open(json_path).unwrap();
let atype_samples: BTreeMap<Language, BTreeMap<String, String>> = let atype_samples: BTreeMap<Language, BTreeMap<AlbumType, String>> =
serde_json::from_reader(BufReader::new(json_file)).unwrap(); serde_json::from_reader(BufReader::new(json_file)).unwrap();
for (lang, entry) in &atype_samples { for (lang, entry) in &atype_samples {
for (album_type_str, txt) in entry { for (album_type, txt) in entry {
let album_type_n = album_type_str.split('_').next().unwrap();
let album_type = serde_plain::from_str::<AlbumType>(album_type_n).unwrap();
let res = map_album_type(txt, *lang); let res = map_album_type(txt, *lang);
assert_eq!( assert_eq!(res, *album_type, "lang: {lang}, txt: {txt}");
res, album_type,
"{album_type_str}: lang: {lang}, txt: {txt}"
);
} }
} }
} }

View file

@ -1,13 +1,12 @@
use serde::Deserialize; use serde::Deserialize;
use serde_with::{serde_as, DefaultOnError, VecSkipError}; use serde_with::{serde_as, DefaultOnError, VecSkipError};
use crate::serializer::text::{AttributedText, Text, TextComponents}; use crate::serializer::text::{Text, TextComponents};
use super::{ use super::{
music_item::{ music_item::{
Button, ItemSection, MusicContentsRenderer, MusicItemMenuEntry, MusicThumbnailRenderer, Button, ItemSection, MusicContentsRenderer, MusicItemMenuEntry, MusicThumbnailRenderer,
}, },
url_endpoint::OnTapWrap,
ContentsRenderer, SectionList, Tab, ContentsRenderer, SectionList, Tab,
}; };
@ -84,10 +83,6 @@ pub(crate) struct HeaderRenderer {
#[serde(default)] #[serde(default)]
#[serde_as(as = "Text")] #[serde_as(as = "Text")]
pub second_subtitle: Vec<String>, pub second_subtitle: Vec<String>,
/// Channel (newer data model)
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub facepile: Option<AvatarStackViewModelWrap>,
#[serde(default)] #[serde(default)]
#[serde_as(as = "DefaultOnError")] #[serde_as(as = "DefaultOnError")]
pub menu: Option<HeaderMenu>, pub menu: Option<HeaderMenu>,
@ -140,29 +135,6 @@ impl From<Description> for TextComponents {
} }
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AvatarStackViewModelWrap {
pub avatar_stack_view_model: AvatarStackViewModel,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AvatarStackViewModel {
// #[serde(default)]
// pub avatars: Vec<AvatarViewModel>,
#[serde_as(as = "AttributedText")]
pub text: String,
pub renderer_context: AvatarStackRendererContext,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AvatarStackRendererContext {
pub command_context: Option<OnTapWrap>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Microformat { pub(crate) struct Microformat {

View file

@ -2,9 +2,9 @@ use std::ops::Range;
use serde::Deserialize; use serde::Deserialize;
use serde_with::serde_as; use serde_with::serde_as;
use serde_with::{DefaultOnError, DisplayFromStr, VecSkipError}; use serde_with::{DefaultOnError, DisplayFromStr};
use super::{Empty, ResponseContext, Thumbnails}; use super::{ResponseContext, Thumbnails};
use crate::serializer::{text::Text, MapResult}; use crate::serializer::{text::Text, MapResult};
#[serde_as] #[serde_as]
@ -19,10 +19,6 @@ pub(crate) struct Player {
#[serde_as(deserialize_as = "DefaultOnError")] #[serde_as(deserialize_as = "DefaultOnError")]
pub storyboards: Option<Storyboards>, pub storyboards: Option<Storyboards>,
pub response_context: ResponseContext, pub response_context: ResponseContext,
#[serde(default)]
pub player_config: PlayerConfig,
#[serde(default)]
pub heartbeat_params: HeartbeatParams,
} }
#[serde_as] #[serde_as]
@ -61,6 +57,9 @@ pub(crate) enum PlayabilityStatus {
}, },
} }
#[derive(Debug, Deserialize)]
pub(crate) struct Empty {}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ErrorScreen { pub(crate) struct ErrorScreen {
@ -89,10 +88,6 @@ pub(crate) struct StreamingData {
pub dash_manifest_url: Option<String>, pub dash_manifest_url: Option<String>,
/// Only on livestreams /// Only on livestreams
pub hls_manifest_url: Option<String>, pub hls_manifest_url: Option<String>,
pub drm_params: Option<String>,
#[serde(default)]
#[serde_as(deserialize_as = "VecSkipError<_>")]
pub initial_authorized_drm_track_types: Vec<DrmTrackType>,
} }
#[serde_as] #[serde_as]
@ -141,16 +136,13 @@ pub(crate) struct Format {
pub audio_track: Option<AudioTrack>, pub audio_track: Option<AudioTrack>,
pub signature_cipher: Option<String>, pub signature_cipher: Option<String>,
#[serde(default)]
#[serde_as(deserialize_as = "VecSkipError<_>")]
pub drm_families: Vec<DrmFamily>,
pub drm_track_type: Option<DrmTrackType>,
} }
impl Format { impl Format {
pub fn is_audio(&self) -> bool { pub fn is_audio(&self) -> bool {
self.audio_quality.is_some() && self.audio_sample_rate.is_some() self.content_length.is_some()
&& self.audio_quality.is_some()
&& self.audio_sample_rate.is_some()
} }
pub fn is_video(&self) -> bool { pub fn is_video(&self) -> bool {
@ -162,7 +154,7 @@ impl Format {
} }
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub(crate) enum Quality { pub(crate) enum Quality {
Tiny, Tiny,
@ -176,7 +168,7 @@ pub(crate) enum Quality {
Hd2160, Hd2160,
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum AudioQuality { pub(crate) enum AudioQuality {
#[serde(rename = "AUDIO_QUALITY_ULTRALOW")] #[serde(rename = "AUDIO_QUALITY_ULTRALOW")]
UltraLow, UltraLow,
@ -188,7 +180,7 @@ pub(crate) enum AudioQuality {
High, High,
} }
#[derive(Default, Clone, Copy, Debug, Deserialize, PartialEq, Eq)] #[derive(Default, Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum FormatType { pub(crate) enum FormatType {
#[default] #[default]
@ -203,7 +195,7 @@ pub(crate) struct ColorInfo {
pub primaries: Primaries, pub primaries: Primaries,
} }
#[derive(Default, Clone, Copy, Debug, Deserialize, PartialEq, Eq)] #[derive(Default, Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum Primaries { pub(crate) enum Primaries {
#[default] #[default]
@ -211,24 +203,6 @@ pub(crate) enum Primaries {
ColorPrimariesBt2020, ColorPrimariesBt2020,
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(clippy::enum_variant_names)]
pub(crate) enum DrmTrackType {
DrmTrackTypeAudio,
DrmTrackTypeSd,
DrmTrackTypeHd,
DrmTrackTypeUhd1,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum DrmFamily {
Widevine,
Playready,
Fairplay,
}
#[derive(Default, Debug, Deserialize)] #[derive(Default, Debug, Deserialize)]
#[serde(default, rename_all = "camelCase")] #[serde(default, rename_all = "camelCase")]
pub(crate) struct AudioTrack { pub(crate) struct AudioTrack {
@ -290,57 +264,3 @@ pub(crate) struct Storyboards {
pub(crate) struct StoryboardRenderer { pub(crate) struct StoryboardRenderer {
pub spec: String, pub spec: String,
} }
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PlayerConfig {
pub web_drm_config: Option<WebDrmConfig>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct WebDrmConfig {
pub widevine_service_cert: Option<String>,
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct HeartbeatParams {
pub drm_session_id: Option<String>,
}
impl From<DrmTrackType> for crate::model::DrmTrackType {
fn from(value: DrmTrackType) -> Self {
match value {
DrmTrackType::DrmTrackTypeAudio => Self::Audio,
DrmTrackType::DrmTrackTypeSd => Self::Sd,
DrmTrackType::DrmTrackTypeHd => Self::Hd,
DrmTrackType::DrmTrackTypeUhd1 => Self::Uhd1,
}
}
}
impl From<DrmFamily> for crate::model::DrmSystem {
fn from(value: DrmFamily) -> Self {
match value {
DrmFamily::Widevine => Self::Widevine,
DrmFamily::Playready => Self::Playready,
DrmFamily::Fairplay => Self::Fairplay,
}
}
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DrmLicense {
pub status: String,
pub license: String,
pub authorized_formats: Vec<AuthorizedFormat>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AuthorizedFormat {
pub track_type: DrmTrackType,
pub key_id: String,
}

View file

@ -4,9 +4,9 @@ use serde_with::{serde_as, DefaultOnError, VecSkipError};
use crate::serializer::text::{AttributedText, Text, TextComponent, TextComponents}; use crate::serializer::text::{AttributedText, Text, TextComponent, TextComponents};
use super::{ use super::{
url_endpoint::OnTapWrap, video_item::YouTubeListRenderer, Alert, ContentRenderer, url_endpoint::NavigationEndpoint, video_item::YouTubeListRenderer, Alert, ContentRenderer,
ContentsRenderer, ImageView, PageHeaderRendererContent, PhMetadataView, ResponseContext, ContentsRenderer, ImageView, PageHeaderRendererContent, PhMetadataView, ResponseContext,
SectionList, Tab, TextBox, ThumbnailsWrap, TwoColumnBrowseResults, SectionList, Tab, ThumbnailsWrap, TwoColumnBrowseResults,
}; };
#[serde_as] #[serde_as]
@ -70,7 +70,15 @@ pub(crate) struct PlaylistHeaderBanner {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Byline { pub(crate) struct Byline {
pub playlist_byline_renderer: TextBox, pub playlist_byline_renderer: BylineRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct BylineRenderer {
#[serde_as(as = "Text")]
pub text: String,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -173,5 +181,17 @@ pub(crate) struct ActionsRow {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ButtonAction { pub(crate) struct ButtonAction {
pub button_view_model: OnTapWrap, pub button_view_model: ButtonViewModel,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ButtonViewModel {
pub on_tap: ActionOnTap,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ActionOnTap {
pub innertube_command: NavigationEndpoint,
} }

View file

@ -1,12 +1,7 @@
use serde::Deserialize; use serde::Deserialize;
use serde_with::{serde_as, DefaultOnError}; use serde_with::{serde_as, DefaultOnError};
use crate::{ use crate::{model::UrlTarget, util};
model::{TrackType, UrlTarget},
util,
};
use super::Empty;
/// navigation/resolve_url response model /// navigation/resolve_url response model
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -37,9 +32,6 @@ pub(crate) enum NavigationEndpoint {
WatchPlaylist { WatchPlaylist {
watch_playlist_endpoint: WatchPlaylistEndpoint, watch_playlist_endpoint: WatchPlaylistEndpoint,
}, },
#[serde(rename_all = "camelCase")]
#[allow(unused)]
CreatePlaylist { create_playlist_endpoint: Empty },
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -162,18 +154,6 @@ pub(crate) struct WatchEndpointConfig {
pub music_video_type: MusicVideoType, pub music_video_type: MusicVideoType,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct OnTap {
pub innertube_command: NavigationEndpoint,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct OnTapWrap {
pub on_tap: OnTap,
}
#[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
pub(crate) enum MusicVideoType { pub(crate) enum MusicVideoType {
#[default] #[default]
@ -199,16 +179,6 @@ impl MusicVideoType {
} }
} }
impl From<MusicVideoType> for TrackType {
fn from(value: MusicVideoType) -> Self {
match value {
MusicVideoType::Video => Self::Video,
MusicVideoType::Track => Self::Track,
MusicVideoType::Episode => Self::Episode,
}
}
}
#[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
pub(crate) enum PageType { pub(crate) enum PageType {
#[serde( #[serde(
@ -255,9 +225,8 @@ impl PageType {
pub(crate) enum MusicPageType { pub(crate) enum MusicPageType {
Artist, Artist,
Album, Album,
Playlist { is_podcast: bool }, Playlist,
Track { vtype: MusicVideoType }, Track { vtype: MusicVideoType },
User,
None, None,
} }
@ -266,13 +235,11 @@ impl From<PageType> for MusicPageType {
match t { match t {
PageType::Artist => MusicPageType::Artist, PageType::Artist => MusicPageType::Artist,
PageType::Album => MusicPageType::Album, PageType::Album => MusicPageType::Album,
PageType::Playlist => MusicPageType::Playlist { is_podcast: false }, PageType::Playlist | PageType::Podcast => MusicPageType::Playlist,
PageType::Podcast => MusicPageType::Playlist { is_podcast: true }, PageType::Channel | PageType::Unknown => MusicPageType::None,
PageType::Channel => MusicPageType::User,
PageType::Episode => MusicPageType::Track { PageType::Episode => MusicPageType::Track {
vtype: MusicVideoType::Episode, vtype: MusicVideoType::Episode,
}, },
PageType::Unknown => MusicPageType::None,
} }
} }
} }
@ -341,11 +308,7 @@ impl NavigationEndpoint {
watch_playlist_endpoint, watch_playlist_endpoint,
} => Some(MusicPage { } => Some(MusicPage {
id: watch_playlist_endpoint.playlist_id, id: watch_playlist_endpoint.playlist_id,
typ: MusicPageType::Playlist { is_podcast: false }, typ: MusicPageType::Playlist,
}),
NavigationEndpoint::CreatePlaylist { .. } => Some(MusicPage {
id: String::new(),
typ: MusicPageType::None,
}), }),
} }
} }
@ -390,7 +353,6 @@ impl NavigationEndpoint {
NavigationEndpoint::WatchPlaylist { NavigationEndpoint::WatchPlaylist {
watch_playlist_endpoint, watch_playlist_endpoint,
} => Some(watch_playlist_endpoint.playlist_id), } => Some(watch_playlist_endpoint.playlist_id),
NavigationEndpoint::CreatePlaylist { .. } => None,
} }
} }
} }

View file

@ -624,7 +624,6 @@ pub(crate) struct CommentViewModelWrap {
pub(crate) struct CommentViewModel { pub(crate) struct CommentViewModel {
pub comment_id: String, pub comment_id: String,
pub comment_key: String, pub comment_key: String,
pub comment_surface_key: String,
pub toolbar_state_key: String, pub toolbar_state_key: String,
} }
@ -696,7 +695,6 @@ pub(crate) struct AuthorCommentBadgeRenderer {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum Payload { pub(crate) enum Payload {
CommentEntityPayload(CommentEntityPayload), CommentEntityPayload(CommentEntityPayload),
CommentSurfaceEntityPayload(CommentSurfaceEntityPayload),
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
EngagementToolbarStateEntityPayload { EngagementToolbarStateEntityPayload {
heart_state: HeartState, heart_state: HeartState,
@ -718,13 +716,6 @@ pub(crate) struct CommentEntityPayload {
pub avatar: ImageView, pub avatar: ImageView,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct CommentSurfaceEntityPayload {
pub voice_reply_container_view_model: Option<VoiceReplyContainer>,
}
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -781,17 +772,3 @@ pub(crate) struct ContinuationButton {
pub(crate) struct ContinuationButtonRenderer { pub(crate) struct ContinuationButtonRenderer {
pub command: ContinuationEndpoint, pub command: ContinuationEndpoint,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct VoiceReplyContainer {
pub voice_reply_container_view_model: VoiceReplyContainer2,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct VoiceReplyContainer2 {
#[serde_as(as = "AttributedText")]
pub transcript_text: TextComponents,
}

View file

@ -4,9 +4,12 @@ use serde_with::{
}; };
use time::OffsetDateTime; use time::OffsetDateTime;
use super::{ChannelBadge, ContentImage, ContinuationEndpoint, PhMetadataView, Thumbnails}; use super::{ChannelBadge, ContinuationEndpoint, Thumbnails};
use crate::{ use crate::{
model::{Channel, ChannelItem, ChannelTag, PlaylistItem, VideoItem, YouTubeItem}, model::{
Channel, ChannelId, ChannelItem, ChannelTag, PlaylistItem, Verification, VideoItem,
YouTubeItem,
},
param::Language, param::Language,
serializer::{ serializer::{
text::{AttributedText, Text, TextComponent}, text::{AttributedText, Text, TextComponent},
@ -15,11 +18,6 @@ use crate::{
util::{self, timeago, TryRemove}, util::{self, timeago, TryRemove},
}; };
#[cfg(feature = "userdata")]
use crate::{client::response::SimpleHeaderRenderer, model::HistoryItem};
#[cfg(feature = "userdata")]
use time::UtcOffset;
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -35,8 +33,6 @@ pub(crate) enum YouTubeListItem {
ChannelRenderer(ChannelRenderer), ChannelRenderer(ChannelRenderer),
LockupViewModel(LockupViewModel),
/// Continauation items are located at the end of a list /// Continauation items are located at the end of a list
/// and contain the continuation token for progressive loading /// and contain the continuation token for progressive loading
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -68,8 +64,6 @@ pub(crate) enum YouTubeListItem {
/// GridRenderer: contains videos on channel page /// GridRenderer: contains videos on channel page
#[serde(alias = "expandedShelfContentsRenderer", alias = "gridRenderer")] #[serde(alias = "expandedShelfContentsRenderer", alias = "gridRenderer")]
ItemSectionRenderer { ItemSectionRenderer {
#[cfg(feature = "userdata")]
header: Option<ItemSectionHeader>,
#[serde(alias = "items")] #[serde(alias = "items")]
contents: MapResult<Vec<YouTubeListItem>>, contents: MapResult<Vec<YouTubeListItem>>,
}, },
@ -171,44 +165,6 @@ pub(crate) struct ShortsOverlayMetadata {
pub secondary_text: Option<String>, pub secondary_text: Option<String>,
} }
/// Generalized list item, currently only used for channel playlists and YTM items
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LockupViewModel {
pub content_id: String,
#[serde(default)]
#[serde_as(deserialize_as = "DefaultOnError")]
pub content_type: LockupContentType,
pub content_image: ContentImage,
pub metadata: LockupViewModelMetadata,
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(clippy::enum_variant_names)]
pub(crate) enum LockupContentType {
LockupContentTypePlaylist,
LockupContentTypeVideo,
#[default]
Unknown,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LockupViewModelMetadata {
pub lockup_metadata_view_model: LockupViewModelMetadataInner,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LockupViewModelMetadataInner {
#[serde_as(as = "AttributedText")]
pub title: String,
pub metadata: PhMetadataView,
}
/// Video displayed in a playlist /// Video displayed in a playlist
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -301,13 +257,6 @@ pub(crate) struct YouTubeListRenderer {
pub contents: MapResult<Vec<YouTubeListItem>>, pub contents: MapResult<Vec<YouTubeListItem>>,
} }
#[cfg(feature = "userdata")]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ItemSectionHeader {
pub item_section_header_renderer: SimpleHeaderRenderer,
}
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -523,18 +472,19 @@ impl<T> YouTubeListMapper<T> {
thumbnail: video.thumbnail.into(), thumbnail: video.thumbnail.into(),
channel: video channel: video
.channel .channel
.and_then(|c| ChannelTag::try_from(c).ok()) .and_then(|c| {
.map(|mut c| { ChannelId::try_from(c).ok().map(|c| ChannelTag {
c.avatar = video id: c.id,
.channel_thumbnail_supported_renderers name: c.name,
.map(|tn| tn.channel_thumbnail_with_link_renderer.thumbnail) avatar: video
.or(video.channel_thumbnail) .channel_thumbnail_supported_renderers
.unwrap_or_default() .map(|tn| tn.channel_thumbnail_with_link_renderer.thumbnail)
.into(); .or(video.channel_thumbnail)
if !c.verification.verified() { .unwrap_or_default()
c.verification = video.owner_badges.into(); .into(),
} verification: video.owner_badges.into(),
c subscriber_count: None,
})
}) })
.or_else(|| self.channel.clone()), .or_else(|| self.channel.clone()),
publish_date: video publish_date: video
@ -616,7 +566,16 @@ impl<T> YouTubeListMapper<T> {
} }
fn map_playlist_video(&mut self, video: PlaylistVideoRenderer) -> VideoItem { fn map_playlist_video(&mut self, video: PlaylistVideoRenderer) -> VideoItem {
let channel = ChannelTag::try_from(video.channel).ok(); let channel = ChannelId::try_from(video.channel)
.ok()
.map(|ch| ChannelTag {
id: ch.id,
name: ch.name,
avatar: Vec::new(),
verification: Verification::None,
subscriber_count: None,
});
let mut video_info = video.video_info.into_iter(); let mut video_info = video.video_info.into_iter();
let video_info1 = video_info let video_info1 = video_info
.next() .next()
@ -679,12 +638,14 @@ impl<T> YouTubeListMapper<T> {
.into(), .into(),
channel: playlist channel: playlist
.channel .channel
.and_then(|c| ChannelTag::try_from(c).ok()) .and_then(|c| {
.map(|mut c| { ChannelId::try_from(c).ok().map(|c| ChannelTag {
if !c.verification.verified() { id: c.id,
c.verification = playlist.owner_badges.into(); name: c.name,
} avatar: Vec::new(),
c verification: playlist.owner_badges.into(),
subscriber_count: None,
})
}) })
.or_else(|| self.channel.clone()), .or_else(|| self.channel.clone()),
video_count: playlist.video_count.or_else(|| { video_count: playlist.video_count.or_else(|| {
@ -720,89 +681,6 @@ impl<T> YouTubeListMapper<T> {
short_description: channel.description_snippet, short_description: channel.description_snippet,
} }
} }
fn map_lockup(&mut self, lockup: LockupViewModel) -> Option<YouTubeItem> {
let md = lockup.metadata.lockup_metadata_view_model;
let tn = lockup.content_image.into_image();
match lockup.content_type {
LockupContentType::LockupContentTypePlaylist => {
Some(YouTubeItem::Playlist(PlaylistItem {
id: lockup.content_id,
name: md.title,
thumbnail: tn.image.into(),
channel: self.channel.clone(),
video_count: tn
.overlays
.first()
.and_then(|ol| {
ol.thumbnail_overlay_badge_view_model
.thumbnail_badges
.first()
})
.and_then(|badge| {
util::parse_numeric(&badge.thumbnail_badge_view_model.text).ok()
}),
}))
}
LockupContentType::LockupContentTypeVideo => {
let mut mdr = md
.metadata
.content_metadata_view_model
.metadata_rows
.into_iter();
let channel = mdr
.next()
.and_then(|r| r.metadata_parts.into_iter().next())
.and_then(|p| ChannelTag::try_from(p.into_text_component()).ok());
let (view_count, publish_date_txt) = mdr
.next()
.map(|metadata_row| {
let mut parts = metadata_row.metadata_parts.into_iter();
let p1 = parts.next();
let p2 = parts.next();
(
p1.and_then(|p| {
util::parse_large_numstr_or_warn(
p.as_str(),
self.lang,
&mut self.warnings,
)
}),
p2.map(|p2| p2.into_text_component().into_string()),
)
})
.unwrap_or_default();
Some(YouTubeItem::Video(VideoItem {
id: lockup.content_id,
name: md.title,
duration: tn
.overlays
.first()
.and_then(|ol| {
ol.thumbnail_overlay_badge_view_model
.thumbnail_badges
.first()
})
.and_then(|badge| {
util::parse_video_length(&badge.thumbnail_badge_view_model.text)
}),
thumbnail: tn.image.into(),
channel,
publish_date: publish_date_txt.as_deref().and_then(|t| {
timeago::parse_timeago_dt_or_warn(self.lang, t, &mut self.warnings)
}),
publish_date_txt,
view_count,
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
}))
}
LockupContentType::Unknown => None,
}
}
} }
impl YouTubeListMapper<YouTubeItem> { impl YouTubeListMapper<YouTubeItem> {
@ -833,11 +711,6 @@ impl YouTubeListMapper<YouTubeItem> {
let mapped = YouTubeItem::Channel(self.map_channel(channel)); let mapped = YouTubeItem::Channel(self.map_channel(channel));
self.items.push(mapped); self.items.push(mapped);
} }
YouTubeListItem::LockupViewModel(lockup) => {
if let Some(mapped) = self.map_lockup(lockup) {
self.items.push(mapped);
}
}
YouTubeListItem::ContinuationItemRenderer { YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint, continuation_endpoint,
} => self.ctoken = Some(continuation_endpoint.continuation_command.token), } => self.ctoken = Some(continuation_endpoint.continuation_command.token),
@ -847,7 +720,7 @@ impl YouTubeListMapper<YouTubeItem> {
YouTubeListItem::RichItemRenderer { content } => { YouTubeListItem::RichItemRenderer { content } => {
self.map_item(*content); self.map_item(*content);
} }
YouTubeListItem::ItemSectionRenderer { mut contents, .. } => { YouTubeListItem::ItemSectionRenderer { mut contents } => {
self.warnings.append(&mut contents.warnings); self.warnings.append(&mut contents.warnings);
contents.c.into_iter().for_each(|it| self.map_item(it)); contents.c.into_iter().for_each(|it| self.map_item(it));
} }
@ -881,11 +754,6 @@ impl YouTubeListMapper<VideoItem> {
let mapped = self.map_playlist_video(video); let mapped = self.map_playlist_video(video);
self.items.push(mapped); self.items.push(mapped);
} }
YouTubeListItem::LockupViewModel(lockup) => {
if let Some(YouTubeItem::Video(mapped)) = self.map_lockup(lockup) {
self.items.push(mapped);
}
}
YouTubeListItem::ContinuationItemRenderer { YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint, continuation_endpoint,
} => self.ctoken = Some(continuation_endpoint.continuation_command.token), } => self.ctoken = Some(continuation_endpoint.continuation_command.token),
@ -895,7 +763,7 @@ impl YouTubeListMapper<VideoItem> {
YouTubeListItem::RichItemRenderer { content } => { YouTubeListItem::RichItemRenderer { content } => {
self.map_item(*content); self.map_item(*content);
} }
YouTubeListItem::ItemSectionRenderer { mut contents, .. } => { YouTubeListItem::ItemSectionRenderer { mut contents } => {
self.warnings.append(&mut contents.warnings); self.warnings.append(&mut contents.warnings);
contents.c.into_iter().for_each(|it| self.map_item(it)); contents.c.into_iter().for_each(|it| self.map_item(it));
} }
@ -907,23 +775,6 @@ impl YouTubeListMapper<VideoItem> {
self.warnings.append(&mut res.warnings); self.warnings.append(&mut res.warnings);
res.c.into_iter().for_each(|item| self.map_item(item)); res.c.into_iter().for_each(|item| self.map_item(item));
} }
#[cfg(feature = "userdata")]
pub(crate) fn conv_history_items(
self,
date_txt: Option<String>,
utc_offset: UtcOffset,
res: &mut MapResult<Vec<HistoryItem<VideoItem>>>,
) {
res.warnings.extend(self.warnings);
res.c.extend(self.items.into_iter().map(|item| HistoryItem {
item,
playback_date: date_txt.as_deref().and_then(|s| {
timeago::parse_textual_date_to_d(self.lang, utc_offset, s, &mut res.warnings)
}),
playback_date_txt: date_txt.clone(),
}));
}
} }
impl YouTubeListMapper<PlaylistItem> { impl YouTubeListMapper<PlaylistItem> {
@ -933,11 +784,6 @@ impl YouTubeListMapper<PlaylistItem> {
let mapped = self.map_playlist(playlist); let mapped = self.map_playlist(playlist);
self.items.push(mapped); self.items.push(mapped);
} }
YouTubeListItem::LockupViewModel(lockup) => {
if let Some(YouTubeItem::Playlist(mapped)) = self.map_lockup(lockup) {
self.items.push(mapped);
}
}
YouTubeListItem::ContinuationItemRenderer { YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint, continuation_endpoint,
} => self.ctoken = Some(continuation_endpoint.continuation_command.token), } => self.ctoken = Some(continuation_endpoint.continuation_command.token),
@ -947,7 +793,7 @@ impl YouTubeListMapper<PlaylistItem> {
YouTubeListItem::RichItemRenderer { content } => { YouTubeListItem::RichItemRenderer { content } => {
self.map_item(*content); self.map_item(*content);
} }
YouTubeListItem::ItemSectionRenderer { mut contents, .. } => { YouTubeListItem::ItemSectionRenderer { mut contents } => {
self.warnings.append(&mut contents.warnings); self.warnings.append(&mut contents.warnings);
contents.c.into_iter().for_each(|it| self.map_item(it)); contents.c.into_iter().for_each(|it| self.map_item(it));
} }

View file

@ -120,9 +120,8 @@ impl<T: FromYtItem> MapResponse<SearchResult<T>> for response::Search {
.filter_map(T::from_yt_item) .filter_map(T::from_yt_item)
.collect(), .collect(),
mapper.ctoken, mapper.ctoken,
ctx.visitor_data.map(str::to_owned), None,
ContinuationEndpoint::Search, ContinuationEndpoint::Search,
false,
), ),
corrected_query: mapper.corrected_query, corrected_query: mapper.corrected_query,
visitor_data: self visitor_data: self

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA", description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [ tags: [
"electronics", "electronics",
@ -125,7 +125,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -166,7 +166,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -207,7 +207,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -248,7 +248,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -330,7 +330,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -371,7 +371,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -412,7 +412,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -453,7 +453,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -494,7 +494,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -535,7 +535,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -576,7 +576,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -617,7 +617,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -658,7 +658,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -699,7 +699,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -740,7 +740,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -781,7 +781,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -822,7 +822,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -863,7 +863,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -904,7 +904,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -945,7 +945,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -986,7 +986,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1027,7 +1027,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1068,7 +1068,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1109,7 +1109,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1150,7 +1150,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1191,7 +1191,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1232,7 +1232,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1273,7 +1273,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1314,7 +1314,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(884000), subscriber_count: Some(884000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA", description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [ tags: [
"electronics", "electronics",
@ -109,7 +109,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(2), video_count: Some(2),
@ -128,7 +128,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(1), video_count: Some(1),
@ -147,7 +147,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(9), video_count: Some(9),
@ -166,7 +166,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(2), video_count: Some(2),
@ -185,7 +185,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(4), video_count: Some(4),
@ -204,7 +204,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(18), video_count: Some(18),
@ -223,7 +223,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(3), video_count: Some(3),
@ -242,7 +242,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(8), video_count: Some(8),
@ -261,7 +261,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(13), video_count: Some(13),
@ -280,7 +280,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(9), video_count: Some(9),
@ -299,7 +299,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(7), video_count: Some(7),
@ -318,7 +318,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(3), video_count: Some(3),
@ -337,7 +337,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(8), video_count: Some(8),
@ -356,7 +356,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(2), video_count: Some(2),
@ -375,7 +375,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(3), video_count: Some(3),
@ -394,7 +394,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(10), video_count: Some(10),
@ -413,7 +413,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(1), video_count: Some(1),
@ -432,7 +432,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(9), video_count: Some(9),
@ -451,7 +451,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(16), video_count: Some(16),
@ -470,7 +470,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(7), video_count: Some(7),
@ -489,7 +489,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(6), video_count: Some(6),
@ -508,7 +508,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(12), video_count: Some(12),
@ -527,7 +527,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(1), video_count: Some(1),
@ -546,7 +546,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(5), video_count: Some(5),
@ -565,7 +565,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(2), video_count: Some(2),
@ -584,7 +584,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(4), video_count: Some(4),
@ -603,7 +603,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(1), video_count: Some(1),
@ -622,7 +622,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(2), video_count: Some(2),
@ -641,7 +641,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(9), video_count: Some(9),
@ -660,7 +660,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(881000), subscriber_count: Some(881000),
)), )),
video_count: Some(1), video_count: Some(1),

View file

@ -1,672 +0,0 @@
---
source: src/client/channel.rs
expression: map_res.c
---
Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
handle: Some("@EEVblog"),
subscriber_count: Some(952000),
video_count: Some(2),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/ytc/AIdro_l17lYcTcRSydZeQK-RuiSfEeH5eX9m4irSNQj6109v5MQ=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ytc/AIdro_l17lYcTcRSydZeQK-RuiSfEeH5eX9m4irSNQj6109v5MQ=s120-c-k-c0x00ffffff-no-rj",
width: 120,
height: 120,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ytc/AIdro_l17lYcTcRSydZeQK-RuiSfEeH5eX9m4irSNQj6109v5MQ=s160-c-k-c0x00ffffff-no-rj",
width: 160,
height: 160,
),
],
verification: verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [
"electronics",
"engineering",
"maker",
"hacker",
"design",
"circuit",
"hardware",
"pic",
"atmel",
"oscilloscope",
"multimeter",
"diy",
"hobby",
"review",
"teardown",
"microcontroller",
"arduino",
"video",
"blog",
"tutorial",
"how-to",
"interview",
"rant",
"industry",
"news",
"mailbag",
"dumpster diving",
"debunking",
],
banner: [
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w1060-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 1060,
height: 175,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w1138-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 1138,
height: 188,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w1707-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 1707,
height: 283,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w2120-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 2120,
height: 351,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w2276-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 2276,
height: 377,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/yIJ9ad80n49rK-YUcZLe_8bLmR-aGyg5ybDH_XKIc0GDWrC6s1Wzz8lxnq3_hux_5b6NHPZ9=w2560-fcrop64=1,00005a57ffffa5a8-k-c0xffffffff-no-nd-rj",
width: 2560,
height: 424,
),
],
has_shorts: true,
has_live: true,
visitor_data: None,
content: Paginator(
count: None,
items: [
PlaylistItem(
id: "PLvOlSehNtuHv268f0mW5m1t_hq_RVGRSA",
name: "Jellybean Components Series",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/XYdmX8w8xwI/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCqmf6TGfDinNXhgU29ZxOkv2u9sQ",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(5),
),
PlaylistItem(
id: "PLvOlSehNtuHu46I7nFuUg3LC3PpiWTR4f",
name: "Tandy Electronics / Radio Shack & Computers",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/uUXxY6gA-7g/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAlIVvQ4Axx40Xa_i8F56qmppXEXg",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(11),
),
PlaylistItem(
id: "PLvOlSehNtuHuS01_RNCnvpzyk7bycYCmM",
name: "Open Source Hardware",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/m_8jh_MpWBE/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBx6U5iikp5rSO78dIWdy1RQ_BLNQ",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(4),
),
PlaylistItem(
id: "PLvOlSehNtuHuwwQ1fpquOJuA5MSfD4iD6",
name: "Fluke Multimeters",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ymJc5oxthlw/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDAOiw39aJajjAdroLnuj_fh60Ryw",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(22),
),
PlaylistItem(
id: "PLvOlSehNtuHs2LwEdDwTp3n7mxb-MyBbo",
name: "EEVacademy Digital Design Tutorial Series",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/lJ3q9RHIatU/hqdefault.jpg?sqp=-oaymwExCOADEI4CSFryq4qpAyMIARUAAIhCGAHwAQH4Af4JgALQBYoCDAgAEAEYQyBXKGUwDw==&rs=AOn4CLBaaQaTJzi7H-zjwSsTlNJdBsyqvQ",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(5),
),
PlaylistItem(
id: "PLvOlSehNtuHu2v8THrRMt8E9ziHtRXPm7",
name: "AI / ChatGPT",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/g5_Ts9SWbYs/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBmZPW6EiAvTCsI86BFg4BxXLj66A",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHvXuXRmoBUys09Dwi1heNii",
name: "Shorts",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ndvJtQ8nxV4/hqdefault.jpg?sqp=-oaymwExCOADEI4CSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYNyBTKH8wDw==&rs=AOn4CLDD0qOLs38KPJtqdG6zCeVLQMf62Q",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(1),
),
PlaylistItem(
id: "PLvOlSehNtuHv3gxNg5BGoZJJu9htoAGB6",
name: "Microcontrollers",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/L9Wrv7nW-S8/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDiAT5izyig1ntMSUhvSOVuYSsG1Q",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(1),
),
PlaylistItem(
id: "PLvOlSehNtuHvllTQ-vwvY26E3Bvrov93Y",
name: "Bypass Capacitors",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/1xicZF9glH0/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAFb2FcbpdtAG1xLjmdkdIm1hFvgA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(4),
),
PlaylistItem(
id: "PLvOlSehNtuHtOV3AEwhuea4TnviddKfAj",
name: "MacGyver Project",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/4yosozyeIP4/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAkwsCiJjFkWhYxtcg5NgfnQbkZrA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHuvHE5GQrQJxWXHdmW2l5IF",
name: "Calculators",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/S3R4r2xvVYQ/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLB7HH5drG-33c1SyRe9kyZBrXvm3A",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(1),
),
PlaylistItem(
id: "PLvOlSehNtuHs6wRwVSaErU0BEnLiHfnKJ",
name: "BM235",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/WPyEFB4cHkA/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAzBuQFV8T9hM8adlPvv58C9TeDug",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(9),
),
PlaylistItem(
id: "PLvOlSehNtuHu4k0ZkKFLsysSB5iava6Qu",
name: "Vibration Measurement",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/uus_cpZiqsU/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCqdsjWVFaLOkEcXgbZD2Eca8MnuQ",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHtdQF-m5UFZ5GEjABadI3kI",
name: "Component Selection",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/uq1DMWtjL2U/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAbgb1Jdb5P69JGdZQ-a8asLLyYdA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(6),
),
PlaylistItem(
id: "PLvOlSehNtuHtlndPUSOPgsujUdq1c5Mr9",
name: "Solar Roadways",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/oIImmlfCyzo/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBxApgyGu3dNXRGoqLctVUnESpEIA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(23),
),
PlaylistItem(
id: "PLvOlSehNtuHvD6M_7WeN071OVsZFE0_q-",
name: "Electronics Tutorials - AC Circuit Theory Series",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/rrPtvYYJ2-g/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBEVc71xxSjJ-xlA_dDQaYIjdHyUw",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHtVLq2MDPIz82BWMIZcuwhK",
name: "Electronics Tutorial - DC Fundamentals",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/xSRe_4TQbuo/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDP4V24_MG6vzvUZsHep9WFSCCY6Q",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(8),
),
PlaylistItem(
id: "PLvOlSehNtuHvIDfW3x2p4BY6l4RYgfBJE",
name: "Oscilloscope Probing",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/OiAmER1OJh4/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAXeGAvEc8y3pEsPUxWdsNIP9UmPw",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(14),
),
PlaylistItem(
id: "PLvOlSehNtuHu6Jjb8U82eKQfvKhJVl0Bu",
name: "Thermal Design",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/8ruFVmxf0zs/hqdefault.jpg?sqp=-oaymwExCOADEI4CSFryq4qpAyMIARUAAIhCGAHwAQH4Af4JgALQBYoCDAgAEAEYfyA1KDUwDw==&rs=AOn4CLD6PMawyYXKe8KT1-Y6vWjQc2xIDw",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(9),
),
PlaylistItem(
id: "PLvOlSehNtuHs-X2Awg33PCBNrP2BGFVhC",
name: "Electric Cars",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/CPcZm1Tu5VI/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCsm8De0QaHPaeCZqxMp_F464fWzg",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(9),
),
PlaylistItem(
id: "PLvOlSehNtuHuLODLTeq3PM-OJRP2nzNUa",
name: "Designing a better uCurrent",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/0AEVilxXAAo/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCjotFuRjPPBHd2LWzt3lviPj9HaA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHtvTKP4RTNW1-08Kmzy1pvA",
name: "EMC Compliance & Measurement",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/lYmfVMWbIHQ/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBtygEqMXx7Lwe5SuBWt2q0CSahYA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(8),
),
PlaylistItem(
id: "PLvOlSehNtuHuUTpCrTVX7BdU68l2aVqMv",
name: "Power Counter Display Project",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/nTpE1Nw3Yy4/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAbPl28_i7isizY6A1t2_c6gV8BAQ",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(2),
),
PlaylistItem(
id: "PLvOlSehNtuHvm120Tq40nKrM5SUBlolN3",
name: "Live - Ask Dave",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/gQ7TTuiDH1M/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBMnucUil90WeDSIeFz8mZCOtEv9g",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(3),
),
PlaylistItem(
id: "PLvOlSehNtuHsiF93KOLoF1KAHArmIW9lC",
name: "Padauk Microcontroller",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/r45r4rV5JOI/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCn4kGWcjBOhk3vN8QPMDa9L3mkKA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(10),
),
PlaylistItem(
id: "PLvOlSehNtuHvxTzBLwUFw4My4rtrNFzED",
name: "Other Debunking Videos",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/WopuF9vD7KE/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBv5buh3qMs4feQaPj6Fy6bxl_vuA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(1),
),
PlaylistItem(
id: "PLvOlSehNtuHt2pJ7X5tumuM4Wa3r1OC7Q",
name: "Audio & Speakers",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/qHbkw0Gm7pk/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCJBYXTDttGHTm51j3bfwqxOqVFig",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(9),
),
PlaylistItem(
id: "PLvOlSehNtuHtX7OearWdmqGzqiu4DHKWi",
name: "Cameras",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/g9umAQ1-an4/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCB5jNm9U-rypnpthK_N321LpYWew",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(16),
),
PlaylistItem(
id: "PLvOlSehNtuHu-TaNRp27_PiXjBG5wY9Gv",
name: "Cryptocurrency",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ibPgfzd9zd8/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDe3IXT88HR3XxnxfqrpAxh6pfYMg",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(7),
),
PlaylistItem(
id: "PLvOlSehNtuHvmK-VGcZ33ZuATmcNB8tvH",
name: "LCD Tutorial",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ZYvxgl-9tNM/hqdefault.jpg?sqp=-oaymwEXCOADEI4CSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDv2WT4Chl1_H2G43AjfSFpPcKVoA",
width: 480,
height: 270,
),
],
channel: Some(ChannelTag(
id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog",
avatar: [],
verification: verified,
subscriber_count: Some(952000),
)),
video_count: Some(6),
),
],
ctoken: Some("4qmFsgLCARIYVUMyRGpGRTdYZjExVVJacVdCaWdjVk9RGnRFZ2x3YkdGNWJHbHpkSE1ZQXlBQk1BRTRBZW9EUEVOblRrUlJhbEZUU2tKSmFWVkZlREpVTW5oVVdsZG9UMlJJVmtsa2JURk1URlphU0ZreGIzcE5NWEF4VVZaU2RGa3dOVU5QU0ZJeVUwTm5PQSUzRCUzRJoCL2Jyb3dzZS1mZWVkVUMyRGpGRTdYZjExVVJacVdCaWdjVk9RcGxheWxpc3RzMTA0"),
endpoint: browse,
),
)

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n", description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n",
tags: [], tags: [],
banner: [ banner: [
@ -81,7 +81,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -107,7 +107,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -133,7 +133,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -159,7 +159,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -185,7 +185,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -211,7 +211,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -237,7 +237,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -263,7 +263,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -315,7 +315,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -341,7 +341,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -367,7 +367,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -393,7 +393,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -419,7 +419,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -445,7 +445,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -471,7 +471,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -497,7 +497,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -523,7 +523,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -549,7 +549,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -575,7 +575,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -601,7 +601,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -627,7 +627,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -653,7 +653,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -679,7 +679,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -705,7 +705,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -731,7 +731,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -757,7 +757,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -783,7 +783,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -809,7 +809,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -835,7 +835,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -861,7 +861,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -887,7 +887,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -913,7 +913,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -939,7 +939,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -965,7 +965,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -991,7 +991,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1017,7 +1017,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1043,7 +1043,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1069,7 +1069,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1095,7 +1095,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1121,7 +1121,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1147,7 +1147,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1173,7 +1173,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1199,7 +1199,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1225,7 +1225,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1251,7 +1251,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1277,7 +1277,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1303,7 +1303,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3360000), subscriber_count: Some(3360000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 160, height: 160,
), ),
], ],
verification: verified, verification: Verified,
description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n", description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n",
tags: [], tags: [],
banner: [ banner: [
@ -81,7 +81,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -107,7 +107,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -133,7 +133,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -159,7 +159,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -185,7 +185,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -211,7 +211,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -237,7 +237,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -263,7 +263,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -315,7 +315,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -341,7 +341,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -367,7 +367,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -393,7 +393,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -419,7 +419,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -445,7 +445,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -471,7 +471,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -497,7 +497,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -523,7 +523,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -549,7 +549,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -575,7 +575,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -601,7 +601,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -627,7 +627,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -653,7 +653,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -679,7 +679,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -705,7 +705,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -731,7 +731,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -757,7 +757,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -783,7 +783,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -809,7 +809,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -835,7 +835,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -861,7 +861,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -887,7 +887,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -913,7 +913,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -939,7 +939,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -965,7 +965,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -991,7 +991,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1017,7 +1017,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1043,7 +1043,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1069,7 +1069,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1095,7 +1095,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1121,7 +1121,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1147,7 +1147,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1173,7 +1173,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1199,7 +1199,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1225,7 +1225,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1251,7 +1251,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1277,7 +1277,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1303,7 +1303,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3740000), subscriber_count: Some(3740000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 160, height: 160,
), ),
], ],
verification: verified, verification: Verified,
description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n", description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n",
tags: [], tags: [],
banner: [ banner: [
@ -81,7 +81,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -107,7 +107,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -133,7 +133,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -159,7 +159,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -185,7 +185,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -211,7 +211,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -237,7 +237,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -263,7 +263,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -315,7 +315,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -341,7 +341,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -367,7 +367,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -393,7 +393,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -419,7 +419,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -445,7 +445,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -471,7 +471,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -497,7 +497,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -523,7 +523,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -549,7 +549,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -575,7 +575,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -601,7 +601,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -627,7 +627,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -653,7 +653,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -679,7 +679,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -705,7 +705,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -731,7 +731,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -757,7 +757,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -783,7 +783,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -809,7 +809,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -835,7 +835,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -861,7 +861,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -887,7 +887,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -913,7 +913,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -939,7 +939,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -965,7 +965,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -991,7 +991,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1017,7 +1017,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1043,7 +1043,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1069,7 +1069,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1095,7 +1095,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1121,7 +1121,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1147,7 +1147,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1173,7 +1173,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1199,7 +1199,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1225,7 +1225,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1251,7 +1251,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1277,7 +1277,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1303,7 +1303,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(3970000), subscriber_count: Some(3970000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n", description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n",
tags: [], tags: [],
banner: [ banner: [
@ -96,7 +96,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -137,7 +137,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -178,7 +178,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -219,7 +219,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -260,7 +260,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -301,7 +301,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -342,7 +342,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -383,7 +383,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -424,7 +424,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -465,7 +465,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -506,7 +506,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -547,7 +547,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -588,7 +588,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -629,7 +629,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -670,7 +670,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -711,7 +711,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -752,7 +752,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -793,7 +793,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -834,7 +834,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -875,7 +875,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -916,7 +916,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -957,7 +957,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -998,7 +998,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1039,7 +1039,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1080,7 +1080,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1121,7 +1121,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1162,7 +1162,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1203,7 +1203,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1244,7 +1244,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1285,7 +1285,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2930000), subscriber_count: Some(2930000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA", description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [ tags: [
"electronics", "electronics",
@ -125,7 +125,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -166,7 +166,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -207,7 +207,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -248,7 +248,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -330,7 +330,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -371,7 +371,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -412,7 +412,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -453,7 +453,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -494,7 +494,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -535,7 +535,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -576,7 +576,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -617,7 +617,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -658,7 +658,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -699,7 +699,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -740,7 +740,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -781,7 +781,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -822,7 +822,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -863,7 +863,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -904,7 +904,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -945,7 +945,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -986,7 +986,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1027,7 +1027,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1068,7 +1068,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1109,7 +1109,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1150,7 +1150,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1191,7 +1191,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1232,7 +1232,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1273,7 +1273,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1314,7 +1314,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(883000), subscriber_count: Some(883000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -20,7 +20,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "April 14-16 & 21-23, 2023\n", description: "April 14-16 & 21-23, 2023\n",
tags: [ tags: [
"coachella", "coachella",
@ -70,7 +70,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -111,7 +111,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -152,7 +152,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -193,7 +193,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -234,7 +234,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -275,7 +275,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -316,7 +316,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -357,7 +357,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -398,7 +398,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -439,7 +439,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -480,7 +480,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -521,7 +521,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -562,7 +562,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -603,7 +603,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -644,7 +644,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -685,7 +685,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -726,7 +726,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -767,7 +767,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -808,7 +808,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -849,7 +849,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -890,7 +890,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -931,7 +931,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -972,7 +972,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1013,7 +1013,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1054,7 +1054,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1095,7 +1095,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1136,7 +1136,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1177,7 +1177,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1218,7 +1218,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1259,7 +1259,7 @@ Channel(
id: "UCHF66aWLOxBW4l6VkSrS3cQ", id: "UCHF66aWLOxBW4l6VkSrS3cQ",
name: "Coachella", name: "Coachella",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2710000), subscriber_count: Some(2710000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 160, height: 160,
), ),
], ],
verification: verified, verification: Verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA", description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [ tags: [
"electronics", "electronics",
@ -125,7 +125,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -166,7 +166,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -207,7 +207,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -248,7 +248,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -330,7 +330,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -371,7 +371,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -412,7 +412,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -453,7 +453,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -494,7 +494,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -535,7 +535,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -576,7 +576,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -617,7 +617,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -658,7 +658,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -699,7 +699,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -740,7 +740,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -781,7 +781,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -822,7 +822,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -863,7 +863,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -904,7 +904,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -945,7 +945,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -986,7 +986,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1027,7 +1027,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1068,7 +1068,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1109,7 +1109,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1150,7 +1150,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1191,7 +1191,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1232,7 +1232,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1273,7 +1273,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1314,7 +1314,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(933000), subscriber_count: Some(933000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA", description: "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON\'T DO PAID VIDEO SPONSORSHIPS, DON\'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don\'t be offended if I don\'t have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA",
tags: [ tags: [
"electronics", "electronics",
@ -125,7 +125,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -166,7 +166,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -207,7 +207,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -248,7 +248,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -289,7 +289,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -330,7 +330,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -371,7 +371,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -412,7 +412,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -453,7 +453,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -494,7 +494,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -535,7 +535,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -576,7 +576,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -617,7 +617,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -658,7 +658,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -699,7 +699,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -740,7 +740,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -781,7 +781,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -822,7 +822,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -863,7 +863,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -904,7 +904,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -945,7 +945,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -986,7 +986,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1027,7 +1027,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1068,7 +1068,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1109,7 +1109,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1150,7 +1150,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1191,7 +1191,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1232,7 +1232,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1273,7 +1273,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1314,7 +1314,7 @@ Channel(
id: "UC2DjFE7Xf11URZqWBigcVOQ", id: "UC2DjFE7Xf11URZqWBigcVOQ",
name: "EEVblog", name: "EEVblog",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(880000), subscriber_count: Some(880000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: none, verification: None,
description: "", description: "",
tags: [], tags: [],
banner: [], banner: [],

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "Welcome to The Good Life by Sensual Musique.\nThe second official channel of Sensual Musique. You can find a lot of music, live streams and some other things on this channel. Stay tuned :)\n\nSubmit your music here: submit.sensualmusiquenetwork@gmail.com", description: "Welcome to The Good Life by Sensual Musique.\nThe second official channel of Sensual Musique. You can find a lot of music, live streams and some other things on this channel. Stay tuned :)\n\nSubmit your music here: submit.sensualmusiquenetwork@gmail.com",
tags: [ tags: [
"live radio", "live radio",
@ -109,7 +109,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -150,7 +150,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -191,7 +191,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -232,7 +232,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -273,7 +273,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -314,7 +314,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -355,7 +355,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -396,7 +396,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -437,7 +437,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -478,7 +478,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -519,7 +519,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -560,7 +560,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -601,7 +601,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -642,7 +642,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -683,7 +683,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -724,7 +724,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -765,7 +765,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -806,7 +806,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -847,7 +847,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -888,7 +888,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -929,7 +929,7 @@ Channel(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(760000), subscriber_count: Some(760000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: none, verification: None,
description: "", description: "",
tags: [], tags: [],
banner: [ banner: [

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n", description: "Hi, Im Tina, aka Doobydobap!\n\nFood is the medium I use to tell stories and connect with people who share the same passion as I do. Whether its because youre hungry at midnight or trying to learn how to cook, I hope you enjoy watching my content and recipes. Don\'t yuck my yum!\n\nwww.doobydobap.com\n",
tags: [], tags: [],
banner: [ banner: [
@ -81,7 +81,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -107,7 +107,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -148,7 +148,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -174,7 +174,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -200,7 +200,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -226,7 +226,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -267,7 +267,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -293,7 +293,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -319,7 +319,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -345,7 +345,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -386,7 +386,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -412,7 +412,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -453,7 +453,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -479,7 +479,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -505,7 +505,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -531,7 +531,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -557,7 +557,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -583,7 +583,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -609,7 +609,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -635,7 +635,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -676,7 +676,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -702,7 +702,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -728,7 +728,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -754,7 +754,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -780,7 +780,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -806,7 +806,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -832,7 +832,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -858,7 +858,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -899,7 +899,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -925,7 +925,7 @@ Channel(
id: "UCh8gHdtzO2tXd593_bjErWg", id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap", name: "Doobydobap",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(2840000), subscriber_count: Some(2840000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -25,7 +25,7 @@ Channel(
height: 176, height: 176,
), ),
], ],
verification: verified, verification: Verified,
description: "BRAND NEW SECOND CHANNEL: https://youtube.com/channel/UCcsQYra-bISsFxNqnd6Javw\n\nJoin my Discord: https://discord.gg/2YcarWsc8S\n", description: "BRAND NEW SECOND CHANNEL: https://youtube.com/channel/UCcsQYra-bISsFxNqnd6Javw\n\nJoin my Discord: https://discord.gg/2YcarWsc8S\n",
tags: [ tags: [
"politics", "politics",
@ -113,7 +113,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: Some("2022-09-27T16:00:00Z"), publish_date: Some("2022-09-27T16:00:00Z"),
@ -154,7 +154,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -195,7 +195,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -236,7 +236,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -277,7 +277,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -318,7 +318,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -359,7 +359,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -400,7 +400,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -441,7 +441,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -482,7 +482,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -523,7 +523,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -564,7 +564,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -605,7 +605,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -646,7 +646,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -687,7 +687,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -728,7 +728,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -769,7 +769,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -810,7 +810,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -851,7 +851,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -892,7 +892,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -933,7 +933,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -974,7 +974,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1015,7 +1015,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1056,7 +1056,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1097,7 +1097,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1138,7 +1138,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1179,7 +1179,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1220,7 +1220,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1261,7 +1261,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",
@ -1302,7 +1302,7 @@ Channel(
id: "UCcvfHa-GHSOHFAjU0-Ie57A", id: "UCcvfHa-GHSOHFAjU0-Ie57A",
name: "Adam Something", name: "Adam Something",
avatar: [], avatar: [],
verification: verified, verification: Verified,
subscriber_count: Some(947000), subscriber_count: Some(947000),
)), )),
publish_date: "[date]", publish_date: "[date]",

View file

@ -1,943 +0,0 @@
---
source: src/client/music_artist.rs
expression: artist
---
MusicArtist(
id: "UCOR4_bSVIXPsGa4BbCSt60Q",
name: "Trailerpark",
header_image: [
Thumbnail(
url: "https://lh3.googleusercontent.com/II101BviJo-tGcGg1KKWSU8D3EZjALHQMbQ4v-7-hP4Zfk1pBESaTCLcz8eQb-hggzxq4Z1MuFkBeRE=w540-h225-p-l90-rj",
width: 540,
height: 225,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/II101BviJo-tGcGg1KKWSU8D3EZjALHQMbQ4v-7-hP4Zfk1pBESaTCLcz8eQb-hggzxq4Z1MuFkBeRE=w721-h300-p-l90-rj",
width: 721,
height: 300,
),
],
description: None,
wikipedia_url: None,
subscriber_count: Some(270000),
tracks: [
TrackItem(
id: "YvidasjVLXk",
name: "Bleib in der Schule",
duration: None,
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/V_tvMqbuXgDgoAKuYZ-VFRru3cUb2WQvwO6vVBKY8pdFYAl1dkuIv_W2afjMUNN6uVNxet6r7mHISh0s=w60-h60-l90-rj",
width: 60,
height: 60,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/V_tvMqbuXgDgoAKuYZ-VFRru3cUb2WQvwO6vVBKY8pdFYAl1dkuIv_W2afjMUNN6uVNxet6r7mHISh0s=w120-h120-l90-rj",
width: 120,
height: 120,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: Some(AlbumId(
id: "MPREb_8PsIyll0LFV",
name: "Bleib in der Schule",
)),
view_count: Some(71000000),
track_type: track,
track_nr: None,
by_va: false,
),
TrackItem(
id: "h3T_NXRUUjM",
name: "Fledermausland",
duration: None,
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w60-h60-l90-rj",
width: 60,
height: 60,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w120-h120-l90-rj",
width: 120,
height: 120,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: Some(AlbumId(
id: "MPREb_POeT6m0bw9q",
name: "Crackstreet Boys II X Version",
)),
view_count: Some(30000000),
track_type: track,
track_nr: None,
by_va: false,
),
TrackItem(
id: "XZfoFwWvkGQ",
name: "Sterben kannst du überall",
duration: None,
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/eQCwnR4YLYnizEhQKeSDDE3rulSTo64cTfs8fxR1K-3iWUfC477SHV0ZOOoQa2vJuvr_9i_WDYI-wbo=w60-h60-l90-rj",
width: 60,
height: 60,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/eQCwnR4YLYnizEhQKeSDDE3rulSTo64cTfs8fxR1K-3iWUfC477SHV0ZOOoQa2vJuvr_9i_WDYI-wbo=w120-h120-l90-rj",
width: 120,
height: 120,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: Some(AlbumId(
id: "MPREb_UYdRV1nnK2J",
name: "TP4L",
)),
view_count: Some(40000000),
track_type: track,
track_nr: None,
by_va: false,
),
TrackItem(
id: "LOuVxwVFJhs",
name: "Selbstbefriedigung",
duration: None,
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w60-h60-l90-rj",
width: 60,
height: 60,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w120-h120-l90-rj",
width: 120,
height: 120,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: Some(AlbumId(
id: "MPREb_POeT6m0bw9q",
name: "Crackstreet Boys II X Version",
)),
view_count: Some(13000000),
track_type: track,
track_nr: None,
by_va: false,
),
TrackItem(
id: "GePZUYeIQQQ",
name: "Falsche Band",
duration: None,
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/MIuap-H2LxqP5O7Dry1LdShBFBbg5YTjIPjuXOHWyrKlmnOogsO5cTk6yXH97DhI3WjZg0z3y-jkQxaM=w60-h60-l90-rj",
width: 60,
height: 60,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/MIuap-H2LxqP5O7Dry1LdShBFBbg5YTjIPjuXOHWyrKlmnOogsO5cTk6yXH97DhI3WjZg0z3y-jkQxaM=w120-h120-l90-rj",
width: 120,
height: 120,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: Some(AlbumId(
id: "MPREb_bi34SGT1xlc",
name: "Crackstreet Boys 3 (Bonus Tracks Version)",
)),
view_count: Some(13000000),
track_type: track,
track_nr: None,
by_va: false,
),
TrackItem(
id: "0mcING0Zdis",
name: "Trailerpark - TP4L (Live Abschiedskonzert)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/0mcING0Zdis/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3k5JY0WRBeKNaotfUYrpbObz1mceA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0mcING0Zdis/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3kinVfBJUF-SDFagYKazKmS_ad75w",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(13000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "EAC-2ttHCyk",
name: "Fledermausland (Bonus Track)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/EAC-2ttHCyk/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nlrgFTz_pwbBwXFbaASgklpX78vA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EAC-2ttHCyk/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nHzhiahqhmIkZ0eUXD09BGak2MHQ",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(25000000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "Bret5VaVzJk",
name: "New Kids on the Blech (Bonus Track)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/Bret5VaVzJk/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nFa4qUxqJzCtxr-zPdzP15Ixvu-A",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Bret5VaVzJk/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3l1hGZVAWUwaJQbZXmbRpcbsMdTeQ",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(6900000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "EqP1_IcjW-s",
name: "Pimpulsiv feat. DNP, Sudden & Dana - Wohnwagensiedlung",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/EqP1_IcjW-s/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3lIeltSLpA_XwwZzdJfHnNZ0vqBzA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EqP1_IcjW-s/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nfiByY3RfcFYGfg92C5Vlkar0GJA",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(7100000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "3EoF9Of98e4",
name: "Armut treibt Jugendliche in die Popmusik",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/3EoF9Of98e4/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kvWHX-5mYREKEkf-CM3TLfjrLjlw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3EoF9Of98e4/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3lItzsg6wamh_xSdpoZxTWOHHLS-g",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(5400000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "qr0eN_uIcTs",
name: "Bleib in der Schule (Live in Berlin)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/qr0eN_uIcTs/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nspTbohIYzDFOjTg90KEmKecVVvg",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qr0eN_uIcTs/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3n0SIeq4dPTPvbGv4STsTWNt24cig",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(56000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "McgSyiug6XE",
name: "We Are Family",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/McgSyiug6XE/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nxe3Xz99BVFg-VOra20J682me5JQ",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/McgSyiug6XE/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3lSGwKx_hnqYA-CkoLHapr1PiyX6w",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
ArtistId(
id: Some("UC5HSrFHr6lMzwAyGjlClm0A"),
name: "Timi Hendrix",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(1800000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "ioZxvVhjFs8",
name: "Schlechter Tag",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/ioZxvVhjFs8/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3ltQmZbH1DF9nmho5HLGehqLSGzTw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ioZxvVhjFs8/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3lsluKPeCNxP7QoOCc24tZy4jsn7Q",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(7100000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "3jyZJEcomkw",
name: "Timi Hendrix feat. Alligatoah - Schlaflos in Guantanamo ► prod. by Mantra",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/3jyZJEcomkw/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3k46-OFTCnpEJry_PNst1C11FPA1A",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3jyZJEcomkw/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3kN1ryaQSy4M_Y9bQGh9S-tbYGqdg",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(1500000),
track_type: video,
track_nr: None,
by_va: false,
),
TrackItem(
id: "9oM-cflYhGk",
name: "Timi Hendrix - Kaiser von China (Official Video) 🐲",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/9oM-cflYhGk/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3m6MksfA1NWyIMv6cTk03J21pA0NQ",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9oM-cflYhGk/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3n7oy_XobzQBkUVxEx08iSKNPIB0Q",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album: None,
view_count: Some(1100000),
track_type: video,
track_nr: None,
by_va: false,
),
],
albums: [
AlbumItem(
id: "MPREb_UYdRV1nnK2J",
name: "TP4L",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/eQCwnR4YLYnizEhQKeSDDE3rulSTo64cTfs8fxR1K-3iWUfC477SHV0ZOOoQa2vJuvr_9i_WDYI-wbo=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/eQCwnR4YLYnizEhQKeSDDE3rulSTo64cTfs8fxR1K-3iWUfC477SHV0ZOOoQa2vJuvr_9i_WDYI-wbo=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: album,
year: Some(2017),
by_va: false,
),
AlbumItem(
id: "MPREb_bi34SGT1xlc",
name: "Crackstreet Boys 3 (Bonus Tracks Version)",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/MIuap-H2LxqP5O7Dry1LdShBFBbg5YTjIPjuXOHWyrKlmnOogsO5cTk6yXH97DhI3WjZg0z3y-jkQxaM=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/MIuap-H2LxqP5O7Dry1LdShBFBbg5YTjIPjuXOHWyrKlmnOogsO5cTk6yXH97DhI3WjZg0z3y-jkQxaM=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: album,
year: Some(2014),
by_va: false,
),
AlbumItem(
id: "MPREb_5gkbwhqC4AJ",
name: "Goldener Schluss (Live in Berlin)",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/ilzR9UxpZFwHZnYOL0L504H6a0Y8k_zPk0AYOhBiBqIjq4TGnX-B1uKcNah56dmjPZoDvp9vGWyfgY8=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/ilzR9UxpZFwHZnYOL0L504H6a0Y8k_zPk0AYOhBiBqIjq4TGnX-B1uKcNah56dmjPZoDvp9vGWyfgY8=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: album,
year: Some(2024),
by_va: false,
),
AlbumItem(
id: "MPREb_HPXN9BBzFpV",
name: "TP4L",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/8Ftr5oIt1q6RbGkdiW7cefw-XGUplUXcjXXN7QntI1Nzh_6oR0euh7Lj2Ner3yXV--U-hVxJewkeq8A=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/8Ftr5oIt1q6RbGkdiW7cefw-XGUplUXcjXXN7QntI1Nzh_6oR0euh7Lj2Ner3yXV--U-hVxJewkeq8A=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2017),
by_va: false,
),
AlbumItem(
id: "MPREb_hcK0fXETEf9",
name: "Endlich normale Leute",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/MW37LppS1rjDQIl5GaG0BxKeWk5fs4xphr6rU0z-KmJiHbvMbA3K5ZzrA9avinP2LjNrDGwB5tSLLsqe=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/MW37LppS1rjDQIl5GaG0BxKeWk5fs4xphr6rU0z-KmJiHbvMbA3K5ZzrA9avinP2LjNrDGwB5tSLLsqe=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2017),
by_va: false,
),
AlbumItem(
id: "MPREb_R6EV2L1q0oc",
name: "Armut treibt Jugendliche in die Popmusik",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/kqKBF4JPQhKY1099AzRpJFGc2P7TFuFa2GeM7z8GGfTJ_DkfAzKTdV8gPtfVkyA5HQ0uZn3XG-VtMVj0=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/kqKBF4JPQhKY1099AzRpJFGc2P7TFuFa2GeM7z8GGfTJ_DkfAzKTdV8gPtfVkyA5HQ0uZn3XG-VtMVj0=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2017),
by_va: false,
),
AlbumItem(
id: "MPREb_oHieBHkXn3A",
name: "Dicks Sucken",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/IVvdOUgbTECe2cVKrwhhCYmhHuipV6p0t5cLqMYWm3E_23zBEABxodGiSuX3H_AxRcEZk2-4V-k3RZw6=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/IVvdOUgbTECe2cVKrwhhCYmhHuipV6p0t5cLqMYWm3E_23zBEABxodGiSuX3H_AxRcEZk2-4V-k3RZw6=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2014),
by_va: false,
),
AlbumItem(
id: "MPREb_8PsIyll0LFV",
name: "Bleib in der Schule",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/V_tvMqbuXgDgoAKuYZ-VFRru3cUb2WQvwO6vVBKY8pdFYAl1dkuIv_W2afjMUNN6uVNxet6r7mHISh0s=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/V_tvMqbuXgDgoAKuYZ-VFRru3cUb2WQvwO6vVBKY8pdFYAl1dkuIv_W2afjMUNN6uVNxet6r7mHISh0s=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2014),
by_va: false,
),
AlbumItem(
id: "MPREb_POeT6m0bw9q",
name: "Crackstreet Boys II X Version",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/1fPBoTszY4e6Nf8egSwBTHWsQT8hotwhDnjArd1SHS8gZc5asCoo_3Z2WhN1IO2KMqyYly0xm7mMZ43d=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: ep,
year: Some(2014),
by_va: false,
),
AlbumItem(
id: "MPREb_tdFqP579jQz",
name: "Bleib in der Schule (Live in Berlin)",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/VNjspSA1Fm0yFJEKUCuetOziiET6sQG9QXQCiydknEny98Lc_MEmUp8e37FtCbDz1bQ6yvM6AqpsvL0=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/VNjspSA1Fm0yFJEKUCuetOziiET6sQG9QXQCiydknEny98Lc_MEmUp8e37FtCbDz1bQ6yvM6AqpsvL0=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2024),
by_va: false,
),
AlbumItem(
id: "MPREb_kLvmX2AzYBL",
name: "Bleib in der Schule (Live at Wacken 2019)",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/dV3PCeAdRQgLOuSUdIfA4q8jVgNwSoTceeK085ZOCzEe6YBm5c9gNIvO8wGM_K2NKpip-8-PxJtWEPJo=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/dV3PCeAdRQgLOuSUdIfA4q8jVgNwSoTceeK085ZOCzEe6YBm5c9gNIvO8wGM_K2NKpip-8-PxJtWEPJo=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ArtistId(
id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
name: "Trailerpark",
),
],
artist_id: Some("UCOR4_bSVIXPsGa4BbCSt60Q"),
album_type: single,
year: Some(2014),
by_va: false,
),
],
playlists: [],
similar_artists: [
ArtistItem(
id: "UCVRREKn7V1Cb8qvf43dwZ6w",
name: "257ers",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/yPjiQ4ZVblOXbft1Yo2jd3uJXKJDuSWOP1MCAG6kTIwYqTWsOKRbZBnPhW4gjzvvVll7yVtjbu3e3Q=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/yPjiQ4ZVblOXbft1Yo2jd3uJXKJDuSWOP1MCAG6kTIwYqTWsOKRbZBnPhW4gjzvvVll7yVtjbu3e3Q=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(67300),
),
ArtistItem(
id: "UCuNyvmBfTzQZmWI2rsVX3QQ",
name: "Alligatoah",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/ffIVPiIldrcfp9UEoAbDid6fnAOajn_kgI4OisFoFhK28rk3HVdpYfe2h27T3d_hHfNR943PPSOhHw=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/ffIVPiIldrcfp9UEoAbDid6fnAOajn_kgI4OisFoFhK28rk3HVdpYfe2h27T3d_hHfNR943PPSOhHw=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(779000),
),
ArtistItem(
id: "UCO04sIqN7F4ff2-1ycVZSgQ",
name: "Sudden",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/TEdMt2cE-UCbnjm6AJtyasWv9-a3LFpdmh2X6w3iBwIMATHUtYIQ_F0cJ30vL5m6uJkqL3qFvNYLpYrN=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/TEdMt2cE-UCbnjm6AJtyasWv9-a3LFpdmh2X6w3iBwIMATHUtYIQ_F0cJ30vL5m6uJkqL3qFvNYLpYrN=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(3660),
),
ArtistItem(
id: "UC5k_3LEPSGchsGEGpqoF6dg",
name: "K.I.Z",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/PVaIRDAgRRyLMuFp7OTS7h3HEMoY9ejKxt7GLgfgi6aFt3bP-Edb1YU5t1IlGN0Z-qcrb86qspETNoI=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/PVaIRDAgRRyLMuFp7OTS7h3HEMoY9ejKxt7GLgfgi6aFt3bP-Edb1YU5t1IlGN0Z-qcrb86qspETNoI=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(522000),
),
ArtistItem(
id: "UCG8K_22LRSRwqhoJXBWGmbA",
name: "FiNCH",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/cofqKPsHr5dzuLexkKAYQF3vVMkKTT2FuZgIMXs6XIO3J8diK29qqfKQkqrga8NOCmwVl7x-w4z3mQ=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/cofqKPsHr5dzuLexkKAYQF3vVMkKTT2FuZgIMXs6XIO3J8diK29qqfKQkqrga8NOCmwVl7x-w4z3mQ=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(533000),
),
ArtistItem(
id: "UC5HSrFHr6lMzwAyGjlClm0A",
name: "Timi Hendrix",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/1yi83YgKDBSQ0rgsA2GuZRa0rBABPR2BH41DsuCfGMRmLdF9oR7vv7T6QGLbhNP8FfX6qVHUQci4YM8=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/1yi83YgKDBSQ0rgsA2GuZRa0rBABPR2BH41DsuCfGMRmLdF9oR7vv7T6QGLbhNP8FfX6qVHUQci4YM8=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(6410),
),
ArtistItem(
id: "UC9izv9vxcTVKA1IibcGTrNA",
name: "Pimpulsiv",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/QXuirXSQsdO1KUZCz-ZX-kRVSorZxIUC4YrxQD0IeSr1mY-42VwvAjf4TTownRVzm-02-U8kLM3VuETf9w=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/QXuirXSQsdO1KUZCz-ZX-kRVSorZxIUC4YrxQD0IeSr1mY-42VwvAjf4TTownRVzm-02-U8kLM3VuETf9w=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(985),
),
ArtistItem(
id: "UCgosMU69MpoCqhuS1JZj6Cw",
name: "Sido",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/HZpnexwxNS5FkIrpz6hdHZuNhBS-GKjs0C9NU8nDSTmHFlPaviqxV-dDLS_ubSEbpEvu0m2P2WT3kaQ=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/HZpnexwxNS5FkIrpz6hdHZuNhBS-GKjs0C9NU8nDSTmHFlPaviqxV-dDLS_ubSEbpEvu0m2P2WT3kaQ=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(1550000),
),
ArtistItem(
id: "UCAiLb3B6iCjxv7HhPf1S4ag",
name: "Marteria",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/Ms5gYOttabL03qfFYx7SNhRsx-K_Y7hxMN0WXgc7iquYAfLV5cgYZfTBn3nsi0_sN5BaqAaIr1z5iGc=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/Ms5gYOttabL03qfFYx7SNhRsx-K_Y7hxMN0WXgc7iquYAfLV5cgYZfTBn3nsi0_sN5BaqAaIr1z5iGc=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(422000),
),
ArtistItem(
id: "UCtoec88rzlhABHeo_4d-H8g",
name: "Dame",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/lkbE9cB4qTxtRmzkjAaLEHrpIgeCOzeBXaL4BpBRq6wp4PlCoSIFej3ita3du8lqniIA67NRYfsVwuFj=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/lkbE9cB4qTxtRmzkjAaLEHrpIgeCOzeBXaL4BpBRq6wp4PlCoSIFej3ita3du8lqniIA67NRYfsVwuFj=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(37700),
),
],
tracks_playlist_id: Some("OLAK5uy_miHesZCUQY5S9EwqfoNP2tZR9nZ0NBAeU"),
videos_playlist_id: Some("OLAK5uy_mqbgE6T9uvusUWrAxJGiImf4_P4dM7IvQ"),
radio_id: Some("RDEM7AbogW0cCnElSU0WYm1GqA"),
)

View file

@ -59,7 +59,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP343YqeP6g5VPGsgJdO1_SV4I", id: "PLwkM1QxaP343YqeP6g5VPGsgJdO1_SV4I",
@ -82,7 +81,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP340xbkARIPpiD1aHuzJVuZUg", id: "PLwkM1QxaP340xbkARIPpiD1aHuzJVuZUg",
@ -105,7 +103,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP342hjju64dtqG5wKqx2hNgjr", id: "PLwkM1QxaP342hjju64dtqG5wKqx2hNgjr",
@ -128,7 +125,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP342v1hhoB3XLiruSQOzmdmBt", id: "PLwkM1QxaP342v1hhoB3XLiruSQOzmdmBt",
@ -151,7 +147,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP342EBMza0AG10nB3oDD65RPY", id: "PLwkM1QxaP342EBMza0AG10nB3oDD65RPY",
@ -174,7 +169,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP342nVAeBVL6_Q8gbbAD8l4wb", id: "PLwkM1QxaP342nVAeBVL6_Q8gbbAD8l4wb",
@ -197,7 +191,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLwkM1QxaP3438x6ta8VJZlJSlDn43FueA", id: "PLwkM1QxaP3438x6ta8VJZlJSlDn43FueA",
@ -220,7 +213,6 @@ MusicArtist(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
], ],
similar_artists: [], similar_artists: [],

View file

@ -64,7 +64,7 @@ MusicArtist(
name: "÷ (Deluxe)", name: "÷ (Deluxe)",
)), )),
view_count: Some(5700000000), view_count: Some(5700000000),
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -96,7 +96,7 @@ MusicArtist(
name: "Shape of You", name: "Shape of You",
)), )),
view_count: Some(8700000000), view_count: Some(8700000000),
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -128,7 +128,7 @@ MusicArtist(
name: "x (Deluxe Edition)", name: "x (Deluxe Edition)",
)), )),
view_count: Some(3300000000), view_count: Some(3300000000),
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -160,7 +160,7 @@ MusicArtist(
name: "x (Deluxe Edition)", name: "x (Deluxe Edition)",
)), )),
view_count: Some(4500000000), view_count: Some(4500000000),
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -192,7 +192,7 @@ MusicArtist(
name: "Bad Habits", name: "Bad Habits",
)), )),
view_count: Some(1100000000), view_count: Some(1100000000),
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -221,7 +221,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(378000), view_count: Some(378000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -250,7 +250,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(250000), view_count: Some(250000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -279,7 +279,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(372000), view_count: Some(372000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -308,7 +308,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(1000000), view_count: Some(1000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -337,7 +337,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(3800000000), view_count: Some(3800000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -366,7 +366,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(6300000000), view_count: Some(6300000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -395,7 +395,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(1400000000), view_count: Some(1400000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -424,7 +424,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(3800000000), view_count: Some(3800000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -453,7 +453,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(641000000), view_count: Some(641000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -482,7 +482,7 @@ MusicArtist(
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album: None, album: None,
view_count: Some(364000000), view_count: Some(364000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -510,7 +510,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2024), year: Some(2024),
by_va: false, by_va: false,
), ),
@ -536,7 +536,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -562,7 +562,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -588,7 +588,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -614,7 +614,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -640,7 +640,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2019), year: Some(2019),
by_va: false, by_va: false,
), ),
@ -666,7 +666,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -692,7 +692,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: ep, album_type: Ep,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -718,7 +718,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -744,7 +744,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: album, album_type: Album,
year: Some(2011), year: Some(2011),
by_va: false, by_va: false,
), ),
@ -770,7 +770,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -796,7 +796,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -822,7 +822,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -848,7 +848,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -874,7 +874,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -900,7 +900,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2023), year: Some(2023),
by_va: false, by_va: false,
), ),
@ -926,7 +926,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -952,7 +952,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -978,7 +978,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -1004,7 +1004,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"), artist_id: Some("UClmXPfaYhXOYsNn_QUyheWQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -1028,7 +1028,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_mkPdnadBmgXk28mbGxm_5uGeKvHrec208", id: "RDCLAK5uy_mkPdnadBmgXk28mbGxm_5uGeKvHrec208",
@ -1048,7 +1047,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_m0wlRoNn5iCTTgBedfoOQ19Jq9P3XTLIA", id: "RDCLAK5uy_m0wlRoNn5iCTTgBedfoOQ19Jq9P3XTLIA",
@ -1068,7 +1066,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_l1oO11DBO4FD8U7bOrqUKK5Y_PkISUMQM", id: "RDCLAK5uy_l1oO11DBO4FD8U7bOrqUKK5Y_PkISUMQM",
@ -1088,7 +1085,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_khs3a0YMI9WYs2k1Oqb2ukWX3dA3-lnwI", id: "RDCLAK5uy_khs3a0YMI9WYs2k1Oqb2ukWX3dA3-lnwI",
@ -1108,7 +1104,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_mfdqvCAl8wodlx2P2_Ai2gNkiRDAufkkI", id: "RDCLAK5uy_mfdqvCAl8wodlx2P2_Ai2gNkiRDAufkkI",
@ -1128,7 +1123,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_nxuz8sV0R7aWiLsbDv5W9_Bvp0X9PxFjY", id: "RDCLAK5uy_nxuz8sV0R7aWiLsbDv5W9_Bvp0X9PxFjY",
@ -1148,7 +1142,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_kPqJ_FiGk-lbXtgM4IF42uokskSJZiVTI", id: "RDCLAK5uy_kPqJ_FiGk-lbXtgM4IF42uokskSJZiVTI",
@ -1168,7 +1161,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_nfs_t4FUu00E5ED6lveEBBX1VMYe1mFjk", id: "RDCLAK5uy_nfs_t4FUu00E5ED6lveEBBX1VMYe1mFjk",
@ -1188,7 +1180,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_kEeMXVnyMll_xhEBH1Aza4lEYO58yeQ0M", id: "RDCLAK5uy_kEeMXVnyMll_xhEBH1Aza4lEYO58yeQ0M",
@ -1208,7 +1199,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
], ],
similar_artists: [ similar_artists: [

View file

@ -64,7 +64,7 @@ MusicArtist(
name: "Evolve", name: "Evolve",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -96,7 +96,7 @@ MusicArtist(
name: "Mercury : Acts 1 & 2", name: "Mercury : Acts 1 & 2",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -140,7 +140,7 @@ MusicArtist(
name: "Mercury : Acts 1 & 2", name: "Mercury : Acts 1 & 2",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -172,7 +172,7 @@ MusicArtist(
name: "Evolve", name: "Evolve",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -204,7 +204,7 @@ MusicArtist(
name: "Night Visions", name: "Night Visions",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -233,7 +233,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(2100000), view_count: Some(2100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -262,7 +262,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(2400000000), view_count: Some(2400000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -291,7 +291,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(207000000), view_count: Some(207000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -320,7 +320,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(324000000), view_count: Some(324000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -349,7 +349,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(1900000000), view_count: Some(1900000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -378,7 +378,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(1000000000), view_count: Some(1000000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -407,7 +407,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(1400000000), view_count: Some(1400000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -431,7 +431,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(440000000), view_count: Some(440000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -460,7 +460,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(557000000), view_count: Some(557000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -489,7 +489,7 @@ MusicArtist(
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album: None, album: None,
view_count: Some(877000000), view_count: Some(877000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -517,7 +517,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -543,7 +543,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -569,7 +569,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -595,7 +595,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -621,7 +621,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -647,7 +647,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -673,7 +673,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -699,7 +699,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -725,7 +725,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -751,7 +751,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -777,7 +777,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2019), year: Some(2019),
by_va: false, by_va: false,
), ),
@ -803,7 +803,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2018), year: Some(2018),
by_va: false, by_va: false,
), ),
@ -829,7 +829,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2018), year: Some(2018),
by_va: false, by_va: false,
), ),
@ -855,7 +855,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2018), year: Some(2018),
by_va: false, by_va: false,
), ),
@ -881,7 +881,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2018), year: Some(2018),
by_va: false, by_va: false,
), ),
@ -907,7 +907,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -933,7 +933,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -959,7 +959,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -985,7 +985,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2016), year: Some(2016),
by_va: false, by_va: false,
), ),
@ -1011,7 +1011,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2016), year: Some(2016),
by_va: false, by_va: false,
), ),
@ -1037,7 +1037,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2015), year: Some(2015),
by_va: false, by_va: false,
), ),
@ -1063,7 +1063,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2015), year: Some(2015),
by_va: false, by_va: false,
), ),
@ -1089,7 +1089,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -1115,7 +1115,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: album, album_type: Album,
year: Some(2011), year: Some(2011),
by_va: false, by_va: false,
), ),
@ -1141,7 +1141,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: ep, album_type: Ep,
year: Some(2010), year: Some(2010),
by_va: false, by_va: false,
), ),
@ -1167,7 +1167,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: ep, album_type: Ep,
year: Some(2009), year: Some(2009),
by_va: false, by_va: false,
), ),
@ -1193,7 +1193,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: ep, album_type: Ep,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -1219,7 +1219,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -1245,7 +1245,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -1271,7 +1271,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -1297,7 +1297,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -1323,7 +1323,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2017), year: Some(2017),
by_va: false, by_va: false,
), ),
@ -1349,7 +1349,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2015), year: Some(2015),
by_va: false, by_va: false,
), ),
@ -1375,7 +1375,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2015), year: Some(2015),
by_va: false, by_va: false,
), ),
@ -1401,7 +1401,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2015), year: Some(2015),
by_va: false, by_va: false,
), ),
@ -1427,7 +1427,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -1453,7 +1453,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -1479,7 +1479,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2014), year: Some(2014),
by_va: false, by_va: false,
), ),
@ -1505,7 +1505,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"), artist_id: Some("UC0aXrjVxG5pZr99v77wZdPQ"),
album_type: single, album_type: Single,
year: Some(2013), year: Some(2013),
by_va: false, by_va: false,
), ),
@ -1529,7 +1529,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_mIpIa-YIJFJe0EAcNbcMPgg-3qCdK9qAk", id: "RDCLAK5uy_mIpIa-YIJFJe0EAcNbcMPgg-3qCdK9qAk",
@ -1549,7 +1548,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_nbzJVrwitbeDjlcHvjM7fgF7khtUOoHgU", id: "RDCLAK5uy_nbzJVrwitbeDjlcHvjM7fgF7khtUOoHgU",
@ -1569,7 +1567,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_nCs5nAmZrJ41ILrSyf36UvOwTBNyx0oEI", id: "RDCLAK5uy_nCs5nAmZrJ41ILrSyf36UvOwTBNyx0oEI",
@ -1589,7 +1586,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_nGXEmbtrmoUF9NG7m0WkxpF_qLKYR3YOU", id: "RDCLAK5uy_nGXEmbtrmoUF9NG7m0WkxpF_qLKYR3YOU",
@ -1609,7 +1605,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_mgHrXs_5F6wPwPFA47S8yrzCfjCi4AXDE", id: "RDCLAK5uy_mgHrXs_5F6wPwPFA47S8yrzCfjCi4AXDE",
@ -1629,7 +1624,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_l7u7RCjtiI_I3m5EgnI-V9yWAgx0RNy1E", id: "RDCLAK5uy_l7u7RCjtiI_I3m5EgnI-V9yWAgx0RNy1E",
@ -1649,7 +1643,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_k7h5535MeHE8xmgHsrZx7HOKH4lb5vAfY", id: "RDCLAK5uy_k7h5535MeHE8xmgHsrZx7HOKH4lb5vAfY",
@ -1669,7 +1662,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_mlCByo5eM1tLBhUdMyn2GphTXICCM_W1w", id: "RDCLAK5uy_mlCByo5eM1tLBhUdMyn2GphTXICCM_W1w",
@ -1689,7 +1681,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "RDCLAK5uy_ke0QH8jvXz6ynXEhn_mbCBy9m7fbnJ9NY", id: "RDCLAK5uy_ke0QH8jvXz6ynXEhn_mbCBy9m7fbnJ9NY",
@ -1709,7 +1700,6 @@ MusicArtist(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
], ],
similar_artists: [ similar_artists: [

View file

@ -64,7 +64,7 @@ MusicArtist(
name: "고블린 Goblin", name: "고블린 Goblin",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -96,7 +96,7 @@ MusicArtist(
name: "고블린 Goblin", name: "고블린 Goblin",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -128,7 +128,7 @@ MusicArtist(
name: "고블린 Goblin", name: "고블린 Goblin",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -157,7 +157,7 @@ MusicArtist(
artist_id: Some("UCfwCE5VhPMGxNPFxtVv7lRw"), artist_id: Some("UCfwCE5VhPMGxNPFxtVv7lRw"),
album: None, album: None,
view_count: Some(20000000), view_count: Some(20000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -186,7 +186,7 @@ MusicArtist(
artist_id: Some("UClGBYGUZmpzUaHgeb9gOBww"), artist_id: Some("UClGBYGUZmpzUaHgeb9gOBww"),
album: None, album: None,
view_count: Some(211000), view_count: Some(211000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -215,7 +215,7 @@ MusicArtist(
artist_id: Some("UCfaO3pZL5XOr8BvNZkrKeVA"), artist_id: Some("UCfaO3pZL5XOr8BvNZkrKeVA"),
album: None, album: None,
view_count: Some(10000), view_count: Some(10000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -244,7 +244,7 @@ MusicArtist(
artist_id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"), artist_id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
album: None, album: None,
view_count: Some(15000), view_count: Some(15000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -273,7 +273,7 @@ MusicArtist(
artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"), artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
album: None, album: None,
view_count: Some(1200), view_count: Some(1200),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -302,7 +302,7 @@ MusicArtist(
artist_id: Some("UCFFvwAcyQhpeQfuAgBN1XZw"), artist_id: Some("UCFFvwAcyQhpeQfuAgBN1XZw"),
album: None, album: None,
view_count: Some(12000), view_count: Some(12000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -331,7 +331,7 @@ MusicArtist(
artist_id: Some("UC_xEL8cbkItBH00KrGz9fbQ"), artist_id: Some("UC_xEL8cbkItBH00KrGz9fbQ"),
album: None, album: None,
view_count: Some(7400), view_count: Some(7400),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -360,7 +360,7 @@ MusicArtist(
artist_id: Some("UCaFqztcJss3HrXNurzQJyqQ"), artist_id: Some("UCaFqztcJss3HrXNurzQJyqQ"),
album: None, album: None,
view_count: Some(1400), view_count: Some(1400),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -389,7 +389,7 @@ MusicArtist(
artist_id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"), artist_id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"),
album: None, album: None,
view_count: Some(25000), view_count: Some(25000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -418,7 +418,7 @@ MusicArtist(
artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"), artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
album: None, album: None,
view_count: Some(3700), view_count: Some(3700),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -446,7 +446,7 @@ MusicArtist(
), ),
], ],
artist_id: Some("UCfwCE5VhPMGxNPFxtVv7lRw"), artist_id: Some("UCfwCE5VhPMGxNPFxtVv7lRw"),
album_type: single, album_type: Single,
year: Some(2019), year: Some(2019),
by_va: false, by_va: false,
), ),

View file

@ -33,7 +33,7 @@ MusicCharts(
artist_id: Some("UCiXhCjTprNP0nuQJ9UsLWeg"), artist_id: Some("UCiXhCjTprNP0nuQJ9UsLWeg"),
album: None, album: None,
view_count: Some(56000000), view_count: Some(56000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -62,7 +62,7 @@ MusicCharts(
artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"), artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"),
album: None, album: None,
view_count: Some(15000000), view_count: Some(15000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -91,7 +91,7 @@ MusicCharts(
artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"), artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"),
album: None, album: None,
view_count: Some(521000000), view_count: Some(521000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -120,7 +120,7 @@ MusicCharts(
artist_id: Some("UCWsDFcIhY2DBi3GB5uykGXA"), artist_id: Some("UCWsDFcIhY2DBi3GB5uykGXA"),
album: None, album: None,
view_count: Some(34000000), view_count: Some(34000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -149,7 +149,7 @@ MusicCharts(
artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"), artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"),
album: None, album: None,
view_count: Some(559000000), view_count: Some(559000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -178,7 +178,7 @@ MusicCharts(
artist_id: Some("UCMXDyVR2tclKWhbqNforSyA"), artist_id: Some("UCMXDyVR2tclKWhbqNforSyA"),
album: None, album: None,
view_count: Some(39000000), view_count: Some(39000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -207,7 +207,7 @@ MusicCharts(
artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"), artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"),
album: None, album: None,
view_count: Some(139000000), view_count: Some(139000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -236,7 +236,7 @@ MusicCharts(
artist_id: Some("UCKRnq8aBOCanYlffje7HyvA"), artist_id: Some("UCKRnq8aBOCanYlffje7HyvA"),
album: None, album: None,
view_count: Some(311000000), view_count: Some(311000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -265,7 +265,7 @@ MusicCharts(
artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"), artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"),
album: None, album: None,
view_count: Some(3800000), view_count: Some(3800000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -294,7 +294,7 @@ MusicCharts(
artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"), artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"),
album: None, album: None,
view_count: Some(46000000), view_count: Some(46000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -323,7 +323,7 @@ MusicCharts(
artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"), artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"),
album: None, album: None,
view_count: Some(73000000), view_count: Some(73000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -356,7 +356,7 @@ MusicCharts(
artist_id: Some("UCohgH17dyp4c_V7U9LoBLdA"), artist_id: Some("UCohgH17dyp4c_V7U9LoBLdA"),
album: None, album: None,
view_count: Some(77000000), view_count: Some(77000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -385,7 +385,7 @@ MusicCharts(
artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"), artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"),
album: None, album: None,
view_count: Some(2600000), view_count: Some(2600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -414,7 +414,7 @@ MusicCharts(
artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"), artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"),
album: None, album: None,
view_count: Some(17000000), view_count: Some(17000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -451,7 +451,7 @@ MusicCharts(
artist_id: Some("UCdPdi8UM25ZyvzhSJkk1uPw"), artist_id: Some("UCdPdi8UM25ZyvzhSJkk1uPw"),
album: None, album: None,
view_count: Some(8600000), view_count: Some(8600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -480,7 +480,7 @@ MusicCharts(
artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"), artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"),
album: None, album: None,
view_count: Some(15000000), view_count: Some(15000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -509,7 +509,7 @@ MusicCharts(
artist_id: Some("UCXT9NWRyDfHJq9Igm1pDQpQ"), artist_id: Some("UCXT9NWRyDfHJq9Igm1pDQpQ"),
album: None, album: None,
view_count: Some(31000000), view_count: Some(31000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -542,7 +542,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(202000000), view_count: Some(202000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -571,7 +571,7 @@ MusicCharts(
artist_id: Some("UCGJdT8Qip4XObbQZ98Z1CAA"), artist_id: Some("UCGJdT8Qip4XObbQZ98Z1CAA"),
album: None, album: None,
view_count: Some(4900000), view_count: Some(4900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -600,7 +600,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(545000000), view_count: Some(545000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -633,7 +633,7 @@ MusicCharts(
artist_id: Some("UC5IkSn-EFsUu3XANYklXc8g"), artist_id: Some("UC5IkSn-EFsUu3XANYklXc8g"),
album: None, album: None,
view_count: Some(20000000), view_count: Some(20000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -666,7 +666,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(36000000), view_count: Some(36000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -699,7 +699,7 @@ MusicCharts(
artist_id: Some("UCgpBsaDW2n_6ruzht3wvP0A"), artist_id: Some("UCgpBsaDW2n_6ruzht3wvP0A"),
album: None, album: None,
view_count: Some(66000000), view_count: Some(66000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -728,7 +728,7 @@ MusicCharts(
artist_id: Some("UCPC0L1d253x-KuMNwa05TpA"), artist_id: Some("UCPC0L1d253x-KuMNwa05TpA"),
album: None, album: None,
view_count: Some(68000000), view_count: Some(68000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -757,7 +757,7 @@ MusicCharts(
artist_id: Some("UCju-DqP7JNtCnMWFXhLgPHQ"), artist_id: Some("UCju-DqP7JNtCnMWFXhLgPHQ"),
album: None, album: None,
view_count: Some(46000000), view_count: Some(46000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -790,7 +790,7 @@ MusicCharts(
artist_id: Some("UC5IkSn-EFsUu3XANYklXc8g"), artist_id: Some("UC5IkSn-EFsUu3XANYklXc8g"),
album: None, album: None,
view_count: Some(43000000), view_count: Some(43000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -823,7 +823,7 @@ MusicCharts(
artist_id: Some("UCoC_a7lWbj2v7rt4ujp4n2A"), artist_id: Some("UCoC_a7lWbj2v7rt4ujp4n2A"),
album: None, album: None,
view_count: Some(7200000), view_count: Some(7200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -852,7 +852,7 @@ MusicCharts(
artist_id: Some("UCvUZUUxWhwtKLVQ9bVRjLEA"), artist_id: Some("UCvUZUUxWhwtKLVQ9bVRjLEA"),
album: None, album: None,
view_count: Some(4000000), view_count: Some(4000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -881,7 +881,7 @@ MusicCharts(
artist_id: Some("UCr_zAwkma5JAyHOWfVXaouA"), artist_id: Some("UCr_zAwkma5JAyHOWfVXaouA"),
album: None, album: None,
view_count: Some(2900000), view_count: Some(2900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -914,7 +914,7 @@ MusicCharts(
artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"), artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"),
album: None, album: None,
view_count: Some(10000000), view_count: Some(10000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -943,7 +943,7 @@ MusicCharts(
artist_id: Some("UCBabNBocAdKiN5sz8RBjIDg"), artist_id: Some("UCBabNBocAdKiN5sz8RBjIDg"),
album: None, album: None,
view_count: Some(15000000), view_count: Some(15000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -972,7 +972,7 @@ MusicCharts(
artist_id: Some("UC5xaQ6_dP7EGDmGLzVGZ1Ow"), artist_id: Some("UC5xaQ6_dP7EGDmGLzVGZ1Ow"),
album: None, album: None,
view_count: Some(16000000), view_count: Some(16000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1001,7 +1001,7 @@ MusicCharts(
artist_id: Some("UCiXhCjTprNP0nuQJ9UsLWeg"), artist_id: Some("UCiXhCjTprNP0nuQJ9UsLWeg"),
album: None, album: None,
view_count: Some(21000000), view_count: Some(21000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1030,7 +1030,7 @@ MusicCharts(
artist_id: Some("UC_VCJd8skzwcPktsMLqTz1g"), artist_id: Some("UC_VCJd8skzwcPktsMLqTz1g"),
album: None, album: None,
view_count: Some(35000000), view_count: Some(35000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1067,7 +1067,7 @@ MusicCharts(
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(30000000), view_count: Some(30000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1096,7 +1096,7 @@ MusicCharts(
artist_id: Some("UCq_Fb1zqNikdovyMJgRQjcw"), artist_id: Some("UCq_Fb1zqNikdovyMJgRQjcw"),
album: None, album: None,
view_count: Some(18000000), view_count: Some(18000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1125,7 +1125,7 @@ MusicCharts(
artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"), artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"),
album: None, album: None,
view_count: Some(5400000), view_count: Some(5400000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1154,7 +1154,7 @@ MusicCharts(
artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"), artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"),
album: None, album: None,
view_count: Some(312000000), view_count: Some(312000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1183,7 +1183,7 @@ MusicCharts(
artist_id: Some("UC0_1glf30IS53tFQWT8xpxw"), artist_id: Some("UC0_1glf30IS53tFQWT8xpxw"),
album: None, album: None,
view_count: Some(28000000), view_count: Some(28000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1212,7 +1212,7 @@ MusicCharts(
artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"), artist_id: Some("UC_z9AthnCGSAk_tZf-KqoFA"),
album: None, album: None,
view_count: Some(97000000), view_count: Some(97000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1238,7 +1238,7 @@ MusicCharts(
artist_id: Some("UCGexNm_Kw4rdQjLxmpb2EKw"), artist_id: Some("UCGexNm_Kw4rdQjLxmpb2EKw"),
album: None, album: None,
view_count: Some(6000000), view_count: Some(6000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1262,7 +1262,7 @@ MusicCharts(
artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"), artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"),
album: None, album: None,
view_count: Some(15000000), view_count: Some(15000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1286,7 +1286,7 @@ MusicCharts(
artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"), artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"),
album: None, album: None,
view_count: Some(10000000), view_count: Some(10000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1314,7 +1314,7 @@ MusicCharts(
artist_id: Some("UC_duTRnaqtLLTCDIlqjRTcQ"), artist_id: Some("UC_duTRnaqtLLTCDIlqjRTcQ"),
album: None, album: None,
view_count: Some(3600000), view_count: Some(3600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1338,7 +1338,7 @@ MusicCharts(
artist_id: Some("UCPoQYATXIYvN5WB0c4f6jfQ"), artist_id: Some("UCPoQYATXIYvN5WB0c4f6jfQ"),
album: None, album: None,
view_count: Some(524000), view_count: Some(524000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1362,7 +1362,7 @@ MusicCharts(
artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"), artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"),
album: None, album: None,
view_count: Some(3800000), view_count: Some(3800000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1386,7 +1386,7 @@ MusicCharts(
artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"), artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"),
album: None, album: None,
view_count: Some(46000000), view_count: Some(46000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1410,7 +1410,7 @@ MusicCharts(
artist_id: Some("UCEf_Bc-KVd7onSeifS3py9g"), artist_id: Some("UCEf_Bc-KVd7onSeifS3py9g"),
album: None, album: None,
view_count: Some(8300000), view_count: Some(8300000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1434,7 +1434,7 @@ MusicCharts(
artist_id: Some("UCVcAt8IIKIeubRSigcYXgtA"), artist_id: Some("UCVcAt8IIKIeubRSigcYXgtA"),
album: None, album: None,
view_count: Some(13000000), view_count: Some(13000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1458,7 +1458,7 @@ MusicCharts(
artist_id: Some("UC0_1glf30IS53tFQWT8xpxw"), artist_id: Some("UC0_1glf30IS53tFQWT8xpxw"),
album: None, album: None,
view_count: Some(365000), view_count: Some(365000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1482,7 +1482,7 @@ MusicCharts(
artist_id: Some("UC1_liDR4fRFJgH4HoJeV8cw"), artist_id: Some("UC1_liDR4fRFJgH4HoJeV8cw"),
album: None, album: None,
view_count: Some(754000), view_count: Some(754000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1506,7 +1506,7 @@ MusicCharts(
artist_id: Some("UCGJdT8Qip4XObbQZ98Z1CAA"), artist_id: Some("UCGJdT8Qip4XObbQZ98Z1CAA"),
album: None, album: None,
view_count: Some(4900000), view_count: Some(4900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1530,7 +1530,7 @@ MusicCharts(
artist_id: Some("UCr_zAwkma5JAyHOWfVXaouA"), artist_id: Some("UCr_zAwkma5JAyHOWfVXaouA"),
album: None, album: None,
view_count: Some(2900000), view_count: Some(2900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1554,7 +1554,7 @@ MusicCharts(
artist_id: Some("UCvUZUUxWhwtKLVQ9bVRjLEA"), artist_id: Some("UCvUZUUxWhwtKLVQ9bVRjLEA"),
album: None, album: None,
view_count: Some(4000000), view_count: Some(4000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1582,7 +1582,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(36000000), view_count: Some(36000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1606,7 +1606,7 @@ MusicCharts(
artist_id: Some("UCVcAt8IIKIeubRSigcYXgtA"), artist_id: Some("UCVcAt8IIKIeubRSigcYXgtA"),
album: None, album: None,
view_count: Some(2000000), view_count: Some(2000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1630,7 +1630,7 @@ MusicCharts(
artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"), artist_id: Some("UChWPNW87QHcXAsw2mzlsYNw"),
album: None, album: None,
view_count: Some(2600000), view_count: Some(2600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1662,7 +1662,7 @@ MusicCharts(
artist_id: Some("UC47k7qXysCBKeaYfc1zmkIA"), artist_id: Some("UC47k7qXysCBKeaYfc1zmkIA"),
album: None, album: None,
view_count: Some(3500000), view_count: Some(3500000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1686,7 +1686,7 @@ MusicCharts(
artist_id: Some("UCjfB7ooJY7C43vBAuuCub_A"), artist_id: Some("UCjfB7ooJY7C43vBAuuCub_A"),
album: None, album: None,
view_count: Some(367000), view_count: Some(367000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1710,7 +1710,7 @@ MusicCharts(
artist_id: Some("UC5xaQ6_dP7EGDmGLzVGZ1Ow"), artist_id: Some("UC5xaQ6_dP7EGDmGLzVGZ1Ow"),
album: None, album: None,
view_count: Some(1500000), view_count: Some(1500000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2416,7 +2416,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn4fmCoF1vKHLtivI0f9yHiF", id: "PL4fGSI1pDJn4fmCoF1vKHLtivI0f9yHiF",
@ -2436,7 +2435,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn5O8siDeZuI_4hbk6JWtTX1", id: "PL4fGSI1pDJn5O8siDeZuI_4hbk6JWtTX1",
@ -2456,7 +2454,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn4EBsWVeFpcSAVOFMfhyipg", id: "PL4fGSI1pDJn4EBsWVeFpcSAVOFMfhyipg",
@ -2476,7 +2473,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn5LOptOQixqnzXNGjNXAgYY", id: "PL4fGSI1pDJn5LOptOQixqnzXNGjNXAgYY",
@ -2496,7 +2492,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn4w4wTTgOmP_S80PoCtbGrL", id: "PL4fGSI1pDJn4w4wTTgOmP_S80PoCtbGrL",
@ -2516,7 +2511,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn7Wkr6Ll6ds1AhA42rT8uaU", id: "PL4fGSI1pDJn7Wkr6Ll6ds1AhA42rT8uaU",
@ -2536,7 +2530,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL4fGSI1pDJn4rBU0RHnR6-b1_uE20CzRH", id: "PL4fGSI1pDJn4rBU0RHnR6-b1_uE20CzRH",
@ -2556,7 +2549,6 @@ MusicCharts(
channel: None, channel: None,
track_count: None, track_count: None,
from_ytm: true, from_ytm: true,
is_podcast: false,
), ),
], ],
top_playlist_id: Some("PL4fGSI1pDJn69On1f-8NAvX_CYlx7QyZc"), top_playlist_id: Some("PL4fGSI1pDJn69On1f-8NAvX_CYlx7QyZc"),

View file

@ -29,7 +29,7 @@ MusicCharts(
artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"), artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"),
album: None, album: None,
view_count: Some(46000000), view_count: Some(46000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -62,7 +62,7 @@ MusicCharts(
artist_id: Some("UC9vrvNSL3xcWGSkV86REBSg"), artist_id: Some("UC9vrvNSL3xcWGSkV86REBSg"),
album: None, album: None,
view_count: Some(46000000), view_count: Some(46000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -91,7 +91,7 @@ MusicCharts(
artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"), artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"),
album: None, album: None,
view_count: Some(3300000000), view_count: Some(3300000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -124,7 +124,7 @@ MusicCharts(
artist_id: Some("UCONiUl5u7y2bMaVZJcuRDEQ"), artist_id: Some("UCONiUl5u7y2bMaVZJcuRDEQ"),
album: None, album: None,
view_count: Some(38000000), view_count: Some(38000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -157,7 +157,7 @@ MusicCharts(
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(57000000), view_count: Some(57000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -186,7 +186,7 @@ MusicCharts(
artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"), artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"),
album: None, album: None,
view_count: Some(521000000), view_count: Some(521000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -219,7 +219,7 @@ MusicCharts(
artist_id: Some("UC5p07Pr3hlfjXo3YGVCyOgg"), artist_id: Some("UC5p07Pr3hlfjXo3YGVCyOgg"),
album: None, album: None,
view_count: Some(76000000), view_count: Some(76000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -248,7 +248,7 @@ MusicCharts(
artist_id: Some("UCfh2j2Dq-aSeLhzuPOsnhVg"), artist_id: Some("UCfh2j2Dq-aSeLhzuPOsnhVg"),
album: None, album: None,
view_count: Some(276000000), view_count: Some(276000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -281,7 +281,7 @@ MusicCharts(
artist_id: Some("UCeBYRgPhy8kcRmIGQWKuqdQ"), artist_id: Some("UCeBYRgPhy8kcRmIGQWKuqdQ"),
album: None, album: None,
view_count: Some(136000000), view_count: Some(136000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -310,7 +310,7 @@ MusicCharts(
artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"), artist_id: Some("UCiY3z8HAGD6BlSNKVn2kSvQ"),
album: None, album: None,
view_count: Some(559000000), view_count: Some(559000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -339,7 +339,7 @@ MusicCharts(
artist_id: Some("UCDxKh1gFWeYsqePvgVzmPoQ"), artist_id: Some("UCDxKh1gFWeYsqePvgVzmPoQ"),
album: None, album: None,
view_count: Some(331000000), view_count: Some(331000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -368,7 +368,7 @@ MusicCharts(
artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"), artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"),
album: None, album: None,
view_count: Some(257000000), view_count: Some(257000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -401,7 +401,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(36000000), view_count: Some(36000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -442,7 +442,7 @@ MusicCharts(
artist_id: Some("UCKEFjh4JL-OyMI8z3h5Coaw"), artist_id: Some("UCKEFjh4JL-OyMI8z3h5Coaw"),
album: None, album: None,
view_count: Some(50000000), view_count: Some(50000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -475,7 +475,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(202000000), view_count: Some(202000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -504,7 +504,7 @@ MusicCharts(
artist_id: Some("UCKNGMXJHTiGFdZNSo_zs3fQ"), artist_id: Some("UCKNGMXJHTiGFdZNSo_zs3fQ"),
album: None, album: None,
view_count: Some(103000000), view_count: Some(103000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -533,7 +533,7 @@ MusicCharts(
artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"), artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"),
album: None, album: None,
view_count: Some(453000000), view_count: Some(453000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -562,7 +562,7 @@ MusicCharts(
artist_id: Some("UCUamzwxCTrUvpyAvAt4FEdg"), artist_id: Some("UCUamzwxCTrUvpyAvAt4FEdg"),
album: None, album: None,
view_count: Some(44000000), view_count: Some(44000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -591,7 +591,7 @@ MusicCharts(
artist_id: Some("UCKEFjh4JL-OyMI8z3h5Coaw"), artist_id: Some("UCKEFjh4JL-OyMI8z3h5Coaw"),
album: None, album: None,
view_count: Some(81000000), view_count: Some(81000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -620,7 +620,7 @@ MusicCharts(
artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"), artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"),
album: None, album: None,
view_count: Some(73000000), view_count: Some(73000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -649,7 +649,7 @@ MusicCharts(
artist_id: Some("UC6uMb9hMAziN9HZoXfTBAlg"), artist_id: Some("UC6uMb9hMAziN9HZoXfTBAlg"),
album: None, album: None,
view_count: Some(45000000), view_count: Some(45000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -678,7 +678,7 @@ MusicCharts(
artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"), artist_id: Some("UC7n3gWRN0vQzgiOKc51aZ4w"),
album: None, album: None,
view_count: Some(545000000), view_count: Some(545000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -707,7 +707,7 @@ MusicCharts(
artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"), artist_id: Some("UCJa2FF4TUB13Mm0GurZAqog"),
album: None, album: None,
view_count: Some(139000000), view_count: Some(139000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -736,7 +736,7 @@ MusicCharts(
artist_id: Some("UCeYz6rzUGhVwqxRM37FUo8w"), artist_id: Some("UCeYz6rzUGhVwqxRM37FUo8w"),
album: None, album: None,
view_count: Some(197000000), view_count: Some(197000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -769,7 +769,7 @@ MusicCharts(
artist_id: Some("UCy6qn2oxmoXA4_gBA5Q7zPw"), artist_id: Some("UCy6qn2oxmoXA4_gBA5Q7zPw"),
album: None, album: None,
view_count: Some(257000000), view_count: Some(257000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -798,7 +798,7 @@ MusicCharts(
artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"), artist_id: Some("UCybEdRVR5u_WFoV-BLTEBiA"),
album: None, album: None,
view_count: Some(15000000), view_count: Some(15000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -827,7 +827,7 @@ MusicCharts(
artist_id: Some("UCtGHTwNL20Y3fY9bumjHDOw"), artist_id: Some("UCtGHTwNL20Y3fY9bumjHDOw"),
album: None, album: None,
view_count: Some(55000000), view_count: Some(55000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -856,7 +856,7 @@ MusicCharts(
artist_id: Some("UCWsDFcIhY2DBi3GB5uykGXA"), artist_id: Some("UCWsDFcIhY2DBi3GB5uykGXA"),
album: None, album: None,
view_count: Some(34000000), view_count: Some(34000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -889,7 +889,7 @@ MusicCharts(
artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"), artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"),
album: None, album: None,
view_count: Some(123000000), view_count: Some(123000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -918,7 +918,7 @@ MusicCharts(
artist_id: Some("UCc3e8O2V5_7OA300ursDyFQ"), artist_id: Some("UCc3e8O2V5_7OA300ursDyFQ"),
album: None, album: None,
view_count: Some(109000000), view_count: Some(109000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -947,7 +947,7 @@ MusicCharts(
artist_id: Some("UC3QmG1Jn9cE5fTMt14DLuZw"), artist_id: Some("UC3QmG1Jn9cE5fTMt14DLuZw"),
album: None, album: None,
view_count: Some(5700000000), view_count: Some(5700000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -976,7 +976,7 @@ MusicCharts(
artist_id: Some("UC03jIQv4WXBSHdr1DlCLYDw"), artist_id: Some("UC03jIQv4WXBSHdr1DlCLYDw"),
album: None, album: None,
view_count: Some(872000000), view_count: Some(872000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1005,7 +1005,7 @@ MusicCharts(
artist_id: Some("UCSzWQmDsKG37iKN2vw1G-2Q"), artist_id: Some("UCSzWQmDsKG37iKN2vw1G-2Q"),
album: None, album: None,
view_count: Some(7900000), view_count: Some(7900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1034,7 +1034,7 @@ MusicCharts(
artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"), artist_id: Some("UCo6JijJGA3IvIiPsawDK3Ww"),
album: None, album: None,
view_count: Some(750000000), view_count: Some(750000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1063,7 +1063,7 @@ MusicCharts(
artist_id: Some("UCKRnq8aBOCanYlffje7HyvA"), artist_id: Some("UCKRnq8aBOCanYlffje7HyvA"),
album: None, album: None,
view_count: Some(311000000), view_count: Some(311000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1100,7 +1100,7 @@ MusicCharts(
artist_id: Some("UCQK0swJm0ceapSOtRKIWr0g"), artist_id: Some("UCQK0swJm0ceapSOtRKIWr0g"),
album: None, album: None,
view_count: Some(37000000), view_count: Some(37000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1133,7 +1133,7 @@ MusicCharts(
artist_id: Some("UC7PL9aor5qNRhvhWWVXyOqA"), artist_id: Some("UC7PL9aor5qNRhvhWWVXyOqA"),
album: None, album: None,
view_count: Some(377000000), view_count: Some(377000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1166,7 +1166,7 @@ MusicCharts(
artist_id: Some("UC2kPe8FB39lojsUDtyKcqOQ"), artist_id: Some("UC2kPe8FB39lojsUDtyKcqOQ"),
album: None, album: None,
view_count: Some(486000000), view_count: Some(486000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1195,7 +1195,7 @@ MusicCharts(
artist_id: Some("UCrP3Rfz32MT-OH9MZh_N9kA"), artist_id: Some("UCrP3Rfz32MT-OH9MZh_N9kA"),
album: None, album: None,
view_count: Some(570000000), view_count: Some(570000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1224,7 +1224,7 @@ MusicCharts(
artist_id: Some("UC0QVToeCjC9-1u-teWToPsg"), artist_id: Some("UC0QVToeCjC9-1u-teWToPsg"),
album: None, album: None,
view_count: Some(28000000), view_count: Some(28000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -33,7 +33,7 @@ TrackDetails(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(235000000), view_count: Some(235000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -51,7 +51,7 @@ TrackDetails(
name: "INVU - The 3rd Album", name: "INVU - The 3rd Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -35,7 +35,7 @@ Paginator(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(250000000), view_count: Some(250000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -69,7 +69,7 @@ Paginator(
artist_id: Some("UC_4Y1QqJr60C5Z7-eQWy-mw"), artist_id: Some("UC_4Y1QqJr60C5Z7-eQWy-mw"),
album: None, album: None,
view_count: Some(168000000), view_count: Some(168000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -103,7 +103,7 @@ Paginator(
artist_id: Some("UCAq0pFGa2w9SjxOq0ZxKVIw"), artist_id: Some("UCAq0pFGa2w9SjxOq0ZxKVIw"),
album: None, album: None,
view_count: Some(464000000), view_count: Some(464000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -137,7 +137,7 @@ Paginator(
artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"), artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"),
album: None, album: None,
view_count: Some(230000000), view_count: Some(230000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -171,7 +171,7 @@ Paginator(
artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"), artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"),
album: None, album: None,
view_count: Some(422000000), view_count: Some(422000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -205,7 +205,7 @@ Paginator(
artist_id: Some("UCHmZYTfdTyVKQEJicLiXEOg"), artist_id: Some("UCHmZYTfdTyVKQEJicLiXEOg"),
album: None, album: None,
view_count: Some(349000000), view_count: Some(349000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -239,7 +239,7 @@ Paginator(
artist_id: Some("UCuKdaTsJ9Jv94hVV_I9aRxQ"), artist_id: Some("UCuKdaTsJ9Jv94hVV_I9aRxQ"),
album: None, album: None,
view_count: Some(167000000), view_count: Some(167000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -273,7 +273,7 @@ Paginator(
artist_id: Some("UC-clMkTZa7k-FxmNgMjoCgQ"), artist_id: Some("UC-clMkTZa7k-FxmNgMjoCgQ"),
album: None, album: None,
view_count: Some(124000000), view_count: Some(124000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -307,7 +307,7 @@ Paginator(
artist_id: Some("UCEf_Bc-KVd7onSeifS3py9g"), artist_id: Some("UCEf_Bc-KVd7onSeifS3py9g"),
album: None, album: None,
view_count: Some(127000000), view_count: Some(127000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -336,7 +336,7 @@ Paginator(
artist_id: Some("UCqTaQGqjAI6fYkr84KZgZEg"), artist_id: Some("UCqTaQGqjAI6fYkr84KZgZEg"),
album: None, album: None,
view_count: Some(239000000), view_count: Some(239000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -370,7 +370,7 @@ Paginator(
artist_id: Some("UCAKvDuIX3m1AUdPpDSqV_3w"), artist_id: Some("UCAKvDuIX3m1AUdPpDSqV_3w"),
album: None, album: None,
view_count: Some(140000000), view_count: Some(140000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -404,7 +404,7 @@ Paginator(
artist_id: Some("UC_Cx288SDUD9liYn7CiJLAA"), artist_id: Some("UC_Cx288SDUD9liYn7CiJLAA"),
album: None, album: None,
view_count: Some(90000000), view_count: Some(90000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -438,7 +438,7 @@ Paginator(
artist_id: Some("UCDDpqmryjNunitS05bv7-8w"), artist_id: Some("UCDDpqmryjNunitS05bv7-8w"),
album: None, album: None,
view_count: Some(137000000), view_count: Some(137000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -472,7 +472,7 @@ Paginator(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(220000000), view_count: Some(220000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -506,7 +506,7 @@ Paginator(
artist_id: Some("UCwPKPUAWE8ah0lkOcvNh8_Q"), artist_id: Some("UCwPKPUAWE8ah0lkOcvNh8_Q"),
album: None, album: None,
view_count: Some(258000000), view_count: Some(258000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -540,7 +540,7 @@ Paginator(
artist_id: Some("UCWT2ZfW7d8YI-HinHEVhyCA"), artist_id: Some("UCWT2ZfW7d8YI-HinHEVhyCA"),
album: None, album: None,
view_count: Some(181000000), view_count: Some(181000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -574,7 +574,7 @@ Paginator(
artist_id: Some("UCjqYTQjO-JG-8vLlt6-4iyQ"), artist_id: Some("UCjqYTQjO-JG-8vLlt6-4iyQ"),
album: None, album: None,
view_count: Some(165000000), view_count: Some(165000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -608,7 +608,7 @@ Paginator(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(108000000), view_count: Some(108000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -642,7 +642,7 @@ Paginator(
artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"), artist_id: Some("UCkbbMCA40i18i7UdjayMPAg"),
album: None, album: None,
view_count: Some(222000000), view_count: Some(222000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -676,7 +676,7 @@ Paginator(
artist_id: Some("UCEUX9tUYqTFfPQdAgVNsKTA"), artist_id: Some("UCEUX9tUYqTFfPQdAgVNsKTA"),
album: None, album: None,
view_count: Some(540000000), view_count: Some(540000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -710,7 +710,7 @@ Paginator(
artist_id: Some("UCG81UKNsFg9Perf0uPQOsQw"), artist_id: Some("UCG81UKNsFg9Perf0uPQOsQw"),
album: None, album: None,
view_count: Some(90000000), view_count: Some(90000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -744,7 +744,7 @@ Paginator(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(90000000), view_count: Some(90000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -778,7 +778,7 @@ Paginator(
artist_id: Some("UCDdCbqagfKo_euzzCV9G2EQ"), artist_id: Some("UCDdCbqagfKo_euzzCV9G2EQ"),
album: None, album: None,
view_count: Some(71000000), view_count: Some(71000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -807,7 +807,7 @@ Paginator(
artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"), artist_id: Some("UCTP45_DE3fMLujU8sZ-MBzw"),
album: None, album: None,
view_count: Some(208000000), view_count: Some(208000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -841,7 +841,7 @@ Paginator(
artist_id: Some("UCDnYJA3OXXhRKYPe3jzLGeQ"), artist_id: Some("UCDnYJA3OXXhRKYPe3jzLGeQ"),
album: None, album: None,
view_count: Some(140000000), view_count: Some(140000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -53,7 +53,7 @@ Paginator(
name: "LOVE DIVE (LOVE DIVE)", name: "LOVE DIVE (LOVE DIVE)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -105,7 +105,7 @@ Paginator(
name: "My Voice - The 1st Album", name: "My Voice - The 1st Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -157,7 +157,7 @@ Paginator(
name: "FOREVER 1 - The 7th Album", name: "FOREVER 1 - The 7th Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -209,7 +209,7 @@ Paginator(
name: "\'The ReVe Festival 2022 - Feel My Rhythm\'", name: "\'The ReVe Festival 2022 - Feel My Rhythm\'",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -261,7 +261,7 @@ Paginator(
name: "NewJeans 1st EP \'New Jeans\'", name: "NewJeans 1st EP \'New Jeans\'",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -313,7 +313,7 @@ Paginator(
name: "IU 5th Album \'LILAC\' (IU 5th Album \'LILAC\')", name: "IU 5th Album \'LILAC\' (IU 5th Album \'LILAC\')",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -365,7 +365,7 @@ Paginator(
name: "2021 Winter SMTOWN : SMCU EXPRESS", name: "2021 Winter SMTOWN : SMCU EXPRESS",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -417,7 +417,7 @@ Paginator(
name: "Dear OHMYGIRL", name: "Dear OHMYGIRL",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -469,7 +469,7 @@ Paginator(
name: "I Love", name: "I Love",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -521,7 +521,7 @@ Paginator(
name: "BORN PINK", name: "BORN PINK",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -573,7 +573,7 @@ Paginator(
name: "YOUNG-LUV.COM (YOUNG-LUV.COM)", name: "YOUNG-LUV.COM (YOUNG-LUV.COM)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -625,7 +625,7 @@ Paginator(
name: "ELEVEN (ELEVEN)", name: "ELEVEN (ELEVEN)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -677,7 +677,7 @@ Paginator(
name: "Weekend", name: "Weekend",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -729,7 +729,7 @@ Paginator(
name: "Offset", name: "Offset",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -785,7 +785,7 @@ Paginator(
name: "Scared to Be Lonely", name: "Scared to Be Lonely",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -837,7 +837,7 @@ Paginator(
name: "After LIKE", name: "After LIKE",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -889,7 +889,7 @@ Paginator(
name: "Purpose - The 2nd Album", name: "Purpose - The 2nd Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -941,7 +941,7 @@ Paginator(
name: "Heart Burn (열이올라요 (Heart Burn))", name: "Heart Burn (열이올라요 (Heart Burn))",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -993,7 +993,7 @@ Paginator(
name: "Rollin\'", name: "Rollin\'",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1045,7 +1045,7 @@ Paginator(
name: "The ReVe Festival: Finale", name: "The ReVe Festival: Finale",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1097,7 +1097,7 @@ Paginator(
name: "Every letter I sent you.", name: "Every letter I sent you.",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1149,7 +1149,7 @@ Paginator(
name: "Stronger (Deluxe Version)", name: "Stronger (Deluxe Version)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1201,7 +1201,7 @@ Paginator(
name: "FATE NUMBER FOR", name: "FATE NUMBER FOR",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1253,7 +1253,7 @@ Paginator(
name: "ANTIFRAGILE", name: "ANTIFRAGILE",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1305,7 +1305,7 @@ Paginator(
name: "소녀시대 Girls\' Generation", name: "소녀시대 Girls\' Generation",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -32,7 +32,7 @@ MusicRelated(
name: "FOREVER 1 - The 7th Album", name: "FOREVER 1 - The 7th Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -64,7 +64,7 @@ MusicRelated(
name: "After LIKE", name: "After LIKE",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -96,7 +96,7 @@ MusicRelated(
name: "Windy", name: "Windy",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -128,7 +128,7 @@ MusicRelated(
name: "Girls - The 2nd Mini Album", name: "Girls - The 2nd Mini Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -160,7 +160,7 @@ MusicRelated(
name: "Weekend", name: "Weekend",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -192,7 +192,7 @@ MusicRelated(
name: "Hello", name: "Hello",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -224,7 +224,7 @@ MusicRelated(
name: "Girls - The 2nd Mini Album", name: "Girls - The 2nd Mini Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -256,7 +256,7 @@ MusicRelated(
name: "IT\'z Different", name: "IT\'z Different",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -288,7 +288,7 @@ MusicRelated(
name: "LOVE DIVE (LOVE DIVE)", name: "LOVE DIVE (LOVE DIVE)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -320,7 +320,7 @@ MusicRelated(
name: "Vanilla", name: "Vanilla",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -352,7 +352,7 @@ MusicRelated(
name: "Savage - The 1st Mini Album", name: "Savage - The 1st Mini Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -384,7 +384,7 @@ MusicRelated(
name: "\'The ReVe Festival 2022 - Feel My Rhythm\'", name: "\'The ReVe Festival 2022 - Feel My Rhythm\'",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -416,7 +416,7 @@ MusicRelated(
name: "I NEVER DIE", name: "I NEVER DIE",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -448,7 +448,7 @@ MusicRelated(
name: "INVU - The 3rd Album", name: "INVU - The 3rd Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -480,7 +480,7 @@ MusicRelated(
name: "CHECKMATE", name: "CHECKMATE",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -512,7 +512,7 @@ MusicRelated(
name: "Girls - The 2nd Mini Album", name: "Girls - The 2nd Mini Album",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -544,7 +544,7 @@ MusicRelated(
name: "Street Dance Girls Fighter (SGF) Special", name: "Street Dance Girls Fighter (SGF) Special",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -576,7 +576,7 @@ MusicRelated(
name: "Next Level", name: "Next Level",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -608,7 +608,7 @@ MusicRelated(
name: "IT\'z ICY", name: "IT\'z ICY",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -640,7 +640,7 @@ MusicRelated(
name: "1/6 (6분의1)", name: "1/6 (6분의1)",
)), )),
view_count: None, view_count: None,
track_type: track, is_video: false,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -666,7 +666,7 @@ MusicRelated(
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album: None, album: None,
view_count: Some(35000000), view_count: Some(35000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -690,7 +690,7 @@ MusicRelated(
artist_id: Some("UCx5Dw_5guQcKu_lMGCh-IuQ"), artist_id: Some("UCx5Dw_5guQcKu_lMGCh-IuQ"),
album: None, album: None,
view_count: Some(836000), view_count: Some(836000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -714,7 +714,7 @@ MusicRelated(
artist_id: Some("UCrGYENbzwtva2X16bAPhTbA"), artist_id: Some("UCrGYENbzwtva2X16bAPhTbA"),
album: None, album: None,
view_count: Some(1200000), view_count: Some(1200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -738,7 +738,7 @@ MusicRelated(
artist_id: Some("UCC3bq4PHj5W5y47jdRjOCPA"), artist_id: Some("UCC3bq4PHj5W5y47jdRjOCPA"),
album: None, album: None,
view_count: Some(987000), view_count: Some(987000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -766,7 +766,7 @@ MusicRelated(
), ),
], ],
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album_type: album, album_type: Album,
year: Some(2022), year: Some(2022),
by_va: false, by_va: false,
), ),
@ -792,7 +792,7 @@ MusicRelated(
), ),
], ],
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"), artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
album_type: ep, album_type: Ep,
year: Some(2021), year: Some(2021),
by_va: false, by_va: false,
), ),
@ -991,7 +991,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLXE743St3DmXcUceLu--0-1k2FP2EocOk", id: "PLXE743St3DmXcUceLu--0-1k2FP2EocOk",
@ -1014,7 +1013,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLrppmyF0pfrfcoUjEygOB3sJpLk7envYZ", id: "PLrppmyF0pfrfcoUjEygOB3sJpLk7envYZ",
@ -1037,7 +1035,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLpwgyaUVRzlLwAwXFWUCtIQJgbMS2k5fG", id: "PLpwgyaUVRzlLwAwXFWUCtIQJgbMS2k5fG",
@ -1060,7 +1057,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLPhP3bI_bdf1KY5-iN6trq-1XB4AQoZij", id: "PLPhP3bI_bdf1KY5-iN6trq-1XB4AQoZij",
@ -1083,7 +1079,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLjVRwhW9AxIDrdwuZqGfC_gjmFNfDfXqm", id: "PLjVRwhW9AxIDrdwuZqGfC_gjmFNfDfXqm",
@ -1106,7 +1101,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLhBJuM3nUmMEZSJaKFmjA7Y5z-PBzMO0o", id: "PLhBJuM3nUmMEZSJaKFmjA7Y5z-PBzMO0o",
@ -1129,7 +1123,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PL0Ne18oW010y_gRCR_57arzpFiP9gnVEi", id: "PL0Ne18oW010y_gRCR_57arzpFiP9gnVEi",
@ -1152,7 +1145,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLSNAUEM08rvKpvgkWSThc7PP7R9GJ8WdJ", id: "PLSNAUEM08rvKpvgkWSThc7PP7R9GJ8WdJ",
@ -1175,7 +1167,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
MusicPlaylistItem( MusicPlaylistItem(
id: "PLmOj3ylRt-xido1Feaf3O5HFXSKKeBuRR", id: "PLmOj3ylRt-xido1Feaf3O5HFXSKKeBuRR",
@ -1198,7 +1189,6 @@ MusicRelated(
)), )),
track_count: None, track_count: None,
from_ytm: false, from_ytm: false,
is_podcast: false,
), ),
], ],
) )

View file

@ -28,7 +28,7 @@ expression: map_res.c
artist_id: Some("UCs6GGpd9zvsYghuYe0VDFUQ"), artist_id: Some("UCs6GGpd9zvsYghuYe0VDFUQ"),
album: None, album: None,
view_count: Some(8600000), view_count: Some(8600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -57,7 +57,7 @@ expression: map_res.c
artist_id: Some("UCicJjripVxiTXbUfociVZwQ"), artist_id: Some("UCicJjripVxiTXbUfociVZwQ"),
album: None, album: None,
view_count: Some(244000), view_count: Some(244000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -86,7 +86,7 @@ expression: map_res.c
artist_id: Some("UCuO5r4J0jTrgRYnmfEOd3UQ"), artist_id: Some("UCuO5r4J0jTrgRYnmfEOd3UQ"),
album: None, album: None,
view_count: Some(314000), view_count: Some(314000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -115,7 +115,7 @@ expression: map_res.c
artist_id: Some("UC4gi504gkSyoXt9vfaGUr9A"), artist_id: Some("UC4gi504gkSyoXt9vfaGUr9A"),
album: None, album: None,
view_count: Some(265000), view_count: Some(265000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -144,7 +144,7 @@ expression: map_res.c
artist_id: Some("UCsN8M73DMWa8SPp5o_0IAQQ"), artist_id: Some("UCsN8M73DMWa8SPp5o_0IAQQ"),
album: None, album: None,
view_count: Some(47000), view_count: Some(47000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -173,7 +173,7 @@ expression: map_res.c
artist_id: Some("UCc_iRXEpehN-dDTkPLoBjZg"), artist_id: Some("UCc_iRXEpehN-dDTkPLoBjZg"),
album: None, album: None,
view_count: Some(225000), view_count: Some(225000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -210,7 +210,7 @@ expression: map_res.c
artist_id: Some("UCsPz48w0M3QUEGAiDP1x17w"), artist_id: Some("UCsPz48w0M3QUEGAiDP1x17w"),
album: None, album: None,
view_count: Some(124000), view_count: Some(124000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -239,7 +239,7 @@ expression: map_res.c
artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"), artist_id: Some("UCpcTrCXblq78GZrTUTLWeBw"),
album: None, album: None,
view_count: Some(34000000), view_count: Some(34000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -272,7 +272,7 @@ expression: map_res.c
artist_id: Some("UCeCsfhcY09c-UvX7DkQDcUw"), artist_id: Some("UCeCsfhcY09c-UvX7DkQDcUw"),
album: None, album: None,
view_count: Some(39000), view_count: Some(39000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -301,7 +301,7 @@ expression: map_res.c
artist_id: Some("UCa7FGSUsN2wNRUclibmicMg"), artist_id: Some("UCa7FGSUsN2wNRUclibmicMg"),
album: None, album: None,
view_count: Some(400000), view_count: Some(400000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -28,7 +28,7 @@ expression: map_res.c
artist_id: Some("UCl7jsog9v_M5SgdWeSEXpRA"), artist_id: Some("UCl7jsog9v_M5SgdWeSEXpRA"),
album: None, album: None,
view_count: Some(428000), view_count: Some(428000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -57,7 +57,7 @@ expression: map_res.c
artist_id: Some("UCtilVkO8eHizeFSs5s5vvCA"), artist_id: Some("UCtilVkO8eHizeFSs5s5vvCA"),
album: None, album: None,
view_count: Some(377000), view_count: Some(377000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -86,7 +86,7 @@ expression: map_res.c
artist_id: Some("UC3i0iDG8jrpTvxgoeDoskbg"), artist_id: Some("UC3i0iDG8jrpTvxgoeDoskbg"),
album: None, album: None,
view_count: Some(2200000), view_count: Some(2200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -115,7 +115,7 @@ expression: map_res.c
artist_id: Some("UCtNte05ndJf0BEN3hSmVEYQ"), artist_id: Some("UCtNte05ndJf0BEN3hSmVEYQ"),
album: None, album: None,
view_count: Some(123000), view_count: Some(123000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -144,7 +144,7 @@ expression: map_res.c
artist_id: Some("UCLLh_wXspYOzRoOUWUCyu8A"), artist_id: Some("UCLLh_wXspYOzRoOUWUCyu8A"),
album: None, album: None,
view_count: Some(1700000), view_count: Some(1700000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -173,7 +173,7 @@ expression: map_res.c
artist_id: Some("UCR5Q898Ou0J9eR5gQFFbExw"), artist_id: Some("UCR5Q898Ou0J9eR5gQFFbExw"),
album: None, album: None,
view_count: Some(1000000), view_count: Some(1000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -202,7 +202,7 @@ expression: map_res.c
artist_id: Some("UCAravTV5JMlA34hSiFeLphQ"), artist_id: Some("UCAravTV5JMlA34hSiFeLphQ"),
album: None, album: None,
view_count: Some(1100000), view_count: Some(1100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -231,7 +231,7 @@ expression: map_res.c
artist_id: Some("UCqfnM1n2W4KwqeLZjm70_5w"), artist_id: Some("UCqfnM1n2W4KwqeLZjm70_5w"),
album: None, album: None,
view_count: Some(574000), view_count: Some(574000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -264,7 +264,7 @@ expression: map_res.c
artist_id: Some("UC2ssqTYToKpthovpsXR_V9A"), artist_id: Some("UC2ssqTYToKpthovpsXR_V9A"),
album: None, album: None,
view_count: Some(531000), view_count: Some(531000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -293,7 +293,7 @@ expression: map_res.c
artist_id: Some("UC7eTn3T3sbweHz9TJJYxoqQ"), artist_id: Some("UC7eTn3T3sbweHz9TJJYxoqQ"),
album: None, album: None,
view_count: Some(888000), view_count: Some(888000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -322,7 +322,7 @@ expression: map_res.c
artist_id: Some("UCzt2dORSFzAiwRNUOpg1Gng"), artist_id: Some("UCzt2dORSFzAiwRNUOpg1Gng"),
album: None, album: None,
view_count: Some(1100000), view_count: Some(1100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -351,7 +351,7 @@ expression: map_res.c
artist_id: Some("UCS-F4K78yXD6b53lypQXVmA"), artist_id: Some("UCS-F4K78yXD6b53lypQXVmA"),
album: None, album: None,
view_count: Some(625000), view_count: Some(625000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -380,7 +380,7 @@ expression: map_res.c
artist_id: Some("UC4dqLAF7yT-_DqeYisQ001w"), artist_id: Some("UC4dqLAF7yT-_DqeYisQ001w"),
album: None, album: None,
view_count: Some(6400000), view_count: Some(6400000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -409,7 +409,7 @@ expression: map_res.c
artist_id: Some("UCICfl8cwTSkZ_kUZ7tLnfBw"), artist_id: Some("UCICfl8cwTSkZ_kUZ7tLnfBw"),
album: None, album: None,
view_count: Some(1700000), view_count: Some(1700000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -438,7 +438,7 @@ expression: map_res.c
artist_id: Some("UCg-EvXRjoIqX6IjDB1F09XA"), artist_id: Some("UCg-EvXRjoIqX6IjDB1F09XA"),
album: None, album: None,
view_count: Some(115000), view_count: Some(115000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -467,7 +467,7 @@ expression: map_res.c
artist_id: Some("UCb18v2rxFjfofBw68qhdJOg"), artist_id: Some("UCb18v2rxFjfofBw68qhdJOg"),
album: None, album: None,
view_count: Some(609000), view_count: Some(609000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -496,7 +496,7 @@ expression: map_res.c
artist_id: Some("UCkBzk1A-RnBs_1OArpSeAFw"), artist_id: Some("UCkBzk1A-RnBs_1OArpSeAFw"),
album: None, album: None,
view_count: Some(1600000), view_count: Some(1600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -525,7 +525,7 @@ expression: map_res.c
artist_id: Some("UC8DMv_5z36X8mpS-VPATVKA"), artist_id: Some("UC8DMv_5z36X8mpS-VPATVKA"),
album: None, album: None,
view_count: Some(13000), view_count: Some(13000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -554,7 +554,7 @@ expression: map_res.c
artist_id: Some("UCBNTNHPngOpC-yeC8VpAPJQ"), artist_id: Some("UCBNTNHPngOpC-yeC8VpAPJQ"),
album: None, album: None,
view_count: Some(2000000), view_count: Some(2000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -583,7 +583,7 @@ expression: map_res.c
artist_id: Some("UCX8lsVmQtu9m1l5n3KDniMw"), artist_id: Some("UCX8lsVmQtu9m1l5n3KDniMw"),
album: None, album: None,
view_count: Some(1000000), view_count: Some(1000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -612,7 +612,7 @@ expression: map_res.c
artist_id: Some("UC8RdjfDbLzA8CYRjsi6gvgw"), artist_id: Some("UC8RdjfDbLzA8CYRjsi6gvgw"),
album: None, album: None,
view_count: Some(865000), view_count: Some(865000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -641,7 +641,7 @@ expression: map_res.c
artist_id: Some("UCdKSviUrXNT9SjC8e-jEJQQ"), artist_id: Some("UCdKSviUrXNT9SjC8e-jEJQQ"),
album: None, album: None,
view_count: Some(14000), view_count: Some(14000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -670,7 +670,7 @@ expression: map_res.c
artist_id: Some("UCBI6li04V1aWOFrs9jjPomQ"), artist_id: Some("UCBI6li04V1aWOFrs9jjPomQ"),
album: None, album: None,
view_count: Some(1600000), view_count: Some(1600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -699,7 +699,7 @@ expression: map_res.c
artist_id: Some("UCCQBpFm05uyBgLNDMFgoBYQ"), artist_id: Some("UCCQBpFm05uyBgLNDMFgoBYQ"),
album: None, album: None,
view_count: Some(133000), view_count: Some(133000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -728,7 +728,7 @@ expression: map_res.c
artist_id: Some("UCH37fMa51kuvqaHjQyPUbQw"), artist_id: Some("UCH37fMa51kuvqaHjQyPUbQw"),
album: None, album: None,
view_count: Some(2000000), view_count: Some(2000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -757,7 +757,7 @@ expression: map_res.c
artist_id: Some("UCame-clC-0couNMsqr_U21w"), artist_id: Some("UCame-clC-0couNMsqr_U21w"),
album: None, album: None,
view_count: Some(1100000), view_count: Some(1100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -790,7 +790,7 @@ expression: map_res.c
artist_id: Some("UCJ2m-WpROlZCiZZID9r7NSQ"), artist_id: Some("UCJ2m-WpROlZCiZZID9r7NSQ"),
album: None, album: None,
view_count: Some(875000), view_count: Some(875000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -814,7 +814,7 @@ expression: map_res.c
artist_id: Some("UCUApRKc5Uc-rDP2QET6eieg"), artist_id: Some("UCUApRKc5Uc-rDP2QET6eieg"),
album: None, album: None,
view_count: Some(44000), view_count: Some(44000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -843,7 +843,7 @@ expression: map_res.c
artist_id: Some("UCwiGKkQcOlzMpXOc1xHFoeg"), artist_id: Some("UCwiGKkQcOlzMpXOc1xHFoeg"),
album: None, album: None,
view_count: Some(119000), view_count: Some(119000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -872,7 +872,7 @@ expression: map_res.c
artist_id: Some("UCSZUc2vHq3Z54Lcw7UgydYQ"), artist_id: Some("UCSZUc2vHq3Z54Lcw7UgydYQ"),
album: None, album: None,
view_count: Some(298000), view_count: Some(298000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -901,7 +901,7 @@ expression: map_res.c
artist_id: Some("UCVS6hZxkuygm-EzXHxWKbJQ"), artist_id: Some("UCVS6hZxkuygm-EzXHxWKbJQ"),
album: None, album: None,
view_count: Some(2600000), view_count: Some(2600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -930,7 +930,7 @@ expression: map_res.c
artist_id: Some("UCuxZXXFOL3K3kEFGiAnp-Kw"), artist_id: Some("UCuxZXXFOL3K3kEFGiAnp-Kw"),
album: None, album: None,
view_count: Some(120000), view_count: Some(120000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -959,7 +959,7 @@ expression: map_res.c
artist_id: Some("UCtNte05ndJf0BEN3hSmVEYQ"), artist_id: Some("UCtNte05ndJf0BEN3hSmVEYQ"),
album: None, album: None,
view_count: Some(100000), view_count: Some(100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -988,7 +988,7 @@ expression: map_res.c
artist_id: Some("UC0wV8tkoYYHxuEXgcQEQ7zA"), artist_id: Some("UC0wV8tkoYYHxuEXgcQEQ7zA"),
album: None, album: None,
view_count: Some(1000000), view_count: Some(1000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1021,7 +1021,7 @@ expression: map_res.c
artist_id: Some("UCEvEiysS7crVEnNAXIx77tw"), artist_id: Some("UCEvEiysS7crVEnNAXIx77tw"),
album: None, album: None,
view_count: Some(587000), view_count: Some(587000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1050,7 +1050,7 @@ expression: map_res.c
artist_id: Some("UCXHklVWUoqHEKrcaJ5JixKw"), artist_id: Some("UCXHklVWUoqHEKrcaJ5JixKw"),
album: None, album: None,
view_count: Some(137000), view_count: Some(137000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1079,7 +1079,7 @@ expression: map_res.c
artist_id: Some("UCn30hmDp52npfCIHHgULM7A"), artist_id: Some("UCn30hmDp52npfCIHHgULM7A"),
album: None, album: None,
view_count: Some(154000), view_count: Some(154000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1108,7 +1108,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(1000000), view_count: Some(1000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1137,7 +1137,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(913000), view_count: Some(913000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1166,7 +1166,7 @@ expression: map_res.c
artist_id: Some("UCoHMUugeU6PWB9ePTOV7WJw"), artist_id: Some("UCoHMUugeU6PWB9ePTOV7WJw"),
album: None, album: None,
view_count: Some(1900000), view_count: Some(1900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1199,7 +1199,7 @@ expression: map_res.c
artist_id: Some("UCLCR-FbVVq1GsETOtdGG-mw"), artist_id: Some("UCLCR-FbVVq1GsETOtdGG-mw"),
album: None, album: None,
view_count: Some(681000), view_count: Some(681000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1228,7 +1228,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(19000), view_count: Some(19000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1257,7 +1257,7 @@ expression: map_res.c
artist_id: Some("UCu3WJjXqlYozV9s8sGYPySA"), artist_id: Some("UCu3WJjXqlYozV9s8sGYPySA"),
album: None, album: None,
view_count: Some(1700000), view_count: Some(1700000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1286,7 +1286,7 @@ expression: map_res.c
artist_id: Some("UCz51ZodJbYUNfkdPHOjJKKw"), artist_id: Some("UCz51ZodJbYUNfkdPHOjJKKw"),
album: None, album: None,
view_count: Some(7000000), view_count: Some(7000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1323,7 +1323,7 @@ expression: map_res.c
artist_id: Some("UCSRPntvq2xLXQtZwE2fA__w"), artist_id: Some("UCSRPntvq2xLXQtZwE2fA__w"),
album: None, album: None,
view_count: Some(43000), view_count: Some(43000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1352,7 +1352,7 @@ expression: map_res.c
artist_id: Some("UCjikjZ91JLx_e-2i9nrhhSg"), artist_id: Some("UCjikjZ91JLx_e-2i9nrhhSg"),
album: None, album: None,
view_count: Some(21000000), view_count: Some(21000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1381,7 +1381,7 @@ expression: map_res.c
artist_id: Some("UCIHQbC5CEmgPkBKO9gOXyBQ"), artist_id: Some("UCIHQbC5CEmgPkBKO9gOXyBQ"),
album: None, album: None,
view_count: Some(138000), view_count: Some(138000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1410,7 +1410,7 @@ expression: map_res.c
artist_id: Some("UCC_ZUtrLVilXKVbOJB3wDVA"), artist_id: Some("UCC_ZUtrLVilXKVbOJB3wDVA"),
album: None, album: None,
view_count: Some(134000), view_count: Some(134000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1439,7 +1439,7 @@ expression: map_res.c
artist_id: Some("UCksBsDumncwulY5-MC6xWDA"), artist_id: Some("UCksBsDumncwulY5-MC6xWDA"),
album: None, album: None,
view_count: Some(612000), view_count: Some(612000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1468,7 +1468,7 @@ expression: map_res.c
artist_id: Some("UCrC-7fsdTCYeaRBpwA6j-Eg"), artist_id: Some("UCrC-7fsdTCYeaRBpwA6j-Eg"),
album: None, album: None,
view_count: None, view_count: None,
track_type: episode, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1497,7 +1497,7 @@ expression: map_res.c
artist_id: Some("UCYO-8CIkoBoUG2nOWz57Q9g"), artist_id: Some("UCYO-8CIkoBoUG2nOWz57Q9g"),
album: None, album: None,
view_count: Some(1700000), view_count: Some(1700000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1530,7 +1530,7 @@ expression: map_res.c
artist_id: Some("UC5-_N4l38iyasppZ5MPILXg"), artist_id: Some("UC5-_N4l38iyasppZ5MPILXg"),
album: None, album: None,
view_count: Some(988000), view_count: Some(988000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1559,7 +1559,7 @@ expression: map_res.c
artist_id: Some("UCLpm6-66iqUlOG28abww73w"), artist_id: Some("UCLpm6-66iqUlOG28abww73w"),
album: None, album: None,
view_count: Some(454000), view_count: Some(454000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1588,7 +1588,7 @@ expression: map_res.c
artist_id: Some("UC1rqUUukLEcgGBMChrH3y_w"), artist_id: Some("UC1rqUUukLEcgGBMChrH3y_w"),
album: None, album: None,
view_count: Some(738000), view_count: Some(738000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1617,7 +1617,7 @@ expression: map_res.c
artist_id: Some("UCMXADr_MS_MdbOouS7ZMc8w"), artist_id: Some("UCMXADr_MS_MdbOouS7ZMc8w"),
album: None, album: None,
view_count: Some(234000), view_count: Some(234000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1646,7 +1646,7 @@ expression: map_res.c
artist_id: Some("UCN3lpPbZIK3CgTWTtriwWMQ"), artist_id: Some("UCN3lpPbZIK3CgTWTtriwWMQ"),
album: None, album: None,
view_count: Some(113000), view_count: Some(113000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1675,7 +1675,7 @@ expression: map_res.c
artist_id: Some("UCiDD2aSYdgxSPE2YUm5lEjg"), artist_id: Some("UCiDD2aSYdgxSPE2YUm5lEjg"),
album: None, album: None,
view_count: Some(702000), view_count: Some(702000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1704,7 +1704,7 @@ expression: map_res.c
artist_id: Some("UCMg_DZ4fMJGCohPYnBTLtpA"), artist_id: Some("UCMg_DZ4fMJGCohPYnBTLtpA"),
album: None, album: None,
view_count: Some(72000), view_count: Some(72000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1733,7 +1733,7 @@ expression: map_res.c
artist_id: Some("UCn5SLnGaBcsusekOkSndmWg"), artist_id: Some("UCn5SLnGaBcsusekOkSndmWg"),
album: None, album: None,
view_count: Some(265000), view_count: Some(265000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1762,7 +1762,7 @@ expression: map_res.c
artist_id: Some("UCiC_vxasVKRDBvZBaMIEGdg"), artist_id: Some("UCiC_vxasVKRDBvZBaMIEGdg"),
album: None, album: None,
view_count: Some(49000), view_count: Some(49000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1791,7 +1791,7 @@ expression: map_res.c
artist_id: Some("UCCXCwvd49sm9x_gp1nuv7uA"), artist_id: Some("UCCXCwvd49sm9x_gp1nuv7uA"),
album: None, album: None,
view_count: Some(15000), view_count: Some(15000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1820,7 +1820,7 @@ expression: map_res.c
artist_id: Some("UCPC1pc0VjTVizL2FferDukw"), artist_id: Some("UCPC1pc0VjTVizL2FferDukw"),
album: None, album: None,
view_count: Some(332000), view_count: Some(332000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1849,7 +1849,7 @@ expression: map_res.c
artist_id: Some("UCylwv7H2IUI_JWiA_2Mt5oA"), artist_id: Some("UCylwv7H2IUI_JWiA_2Mt5oA"),
album: None, album: None,
view_count: Some(177000), view_count: Some(177000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1873,7 +1873,7 @@ expression: map_res.c
artist_id: Some("UCBvTykFO8_qxF0VtPm0ZjmA"), artist_id: Some("UCBvTykFO8_qxF0VtPm0ZjmA"),
album: None, album: None,
view_count: Some(362000), view_count: Some(362000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1902,7 +1902,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(103000), view_count: Some(103000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1931,7 +1931,7 @@ expression: map_res.c
artist_id: Some("UC7aCpfjAUTxRWslugOpsjUg"), artist_id: Some("UC7aCpfjAUTxRWslugOpsjUg"),
album: None, album: None,
view_count: Some(92000), view_count: Some(92000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1960,7 +1960,7 @@ expression: map_res.c
artist_id: Some("UC2-fS2PfXjiYYOhpmnCfDIw"), artist_id: Some("UC2-fS2PfXjiYYOhpmnCfDIw"),
album: None, album: None,
view_count: None, view_count: None,
track_type: episode, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -1989,7 +1989,7 @@ expression: map_res.c
artist_id: Some("UCzVb0SIXp9q9PeKCcFjsBtA"), artist_id: Some("UCzVb0SIXp9q9PeKCcFjsBtA"),
album: None, album: None,
view_count: Some(25000000), view_count: Some(25000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2018,7 +2018,7 @@ expression: map_res.c
artist_id: Some("UCkFIRlbak2lK--nCQXau_6g"), artist_id: Some("UCkFIRlbak2lK--nCQXau_6g"),
album: None, album: None,
view_count: Some(1200000), view_count: Some(1200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2047,7 +2047,7 @@ expression: map_res.c
artist_id: Some("UCOykHV9q0qb0vrBsxO_5fkQ"), artist_id: Some("UCOykHV9q0qb0vrBsxO_5fkQ"),
album: None, album: None,
view_count: Some(1100000), view_count: Some(1100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2076,7 +2076,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(2000000), view_count: Some(2000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2105,7 +2105,7 @@ expression: map_res.c
artist_id: Some("UCofCirUtHXO7IL8AjWHytZQ"), artist_id: Some("UCofCirUtHXO7IL8AjWHytZQ"),
album: None, album: None,
view_count: Some(24000), view_count: Some(24000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2134,7 +2134,7 @@ expression: map_res.c
artist_id: Some("UClWLQP-lNGvEV5qJpy7DLhw"), artist_id: Some("UClWLQP-lNGvEV5qJpy7DLhw"),
album: None, album: None,
view_count: Some(669000), view_count: Some(669000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2163,7 +2163,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(590000), view_count: Some(590000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2196,7 +2196,7 @@ expression: map_res.c
artist_id: Some("UC97hKW1a1rzrMnzkKwaNx_g"), artist_id: Some("UC97hKW1a1rzrMnzkKwaNx_g"),
album: None, album: None,
view_count: Some(2600000), view_count: Some(2600000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2225,7 +2225,7 @@ expression: map_res.c
artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"), artist_id: Some("UCR28YDxjDE3ogQROaNdnRbQ"),
album: None, album: None,
view_count: Some(1300000), view_count: Some(1300000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2254,7 +2254,7 @@ expression: map_res.c
artist_id: Some("UCqgwdYBhoUHmjrVxNhEUv4g"), artist_id: Some("UCqgwdYBhoUHmjrVxNhEUv4g"),
album: None, album: None,
view_count: Some(1400000), view_count: Some(1400000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2291,7 +2291,7 @@ expression: map_res.c
artist_id: Some("UCrmhl-Xsb9LW5WYtC2iHe6A"), artist_id: Some("UCrmhl-Xsb9LW5WYtC2iHe6A"),
album: None, album: None,
view_count: Some(12000), view_count: Some(12000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2324,7 +2324,7 @@ expression: map_res.c
artist_id: Some("UCzmabbKsmXlWnI9N2kKQ4lA"), artist_id: Some("UCzmabbKsmXlWnI9N2kKQ4lA"),
album: None, album: None,
view_count: Some(10000000), view_count: Some(10000000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2353,7 +2353,7 @@ expression: map_res.c
artist_id: Some("UC4yEk8HA1s1-OmMOJSCyg0A"), artist_id: Some("UC4yEk8HA1s1-OmMOJSCyg0A"),
album: None, album: None,
view_count: Some(488000), view_count: Some(488000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2382,7 +2382,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(6800000), view_count: Some(6800000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2411,7 +2411,7 @@ expression: map_res.c
artist_id: Some("UCf4l_B9IhzstPp8elVeHnTQ"), artist_id: Some("UCf4l_B9IhzstPp8elVeHnTQ"),
album: None, album: None,
view_count: Some(637000), view_count: Some(637000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2440,7 +2440,7 @@ expression: map_res.c
artist_id: Some("UCC0ydtpsVWZNHLvKSf4MnYw"), artist_id: Some("UCC0ydtpsVWZNHLvKSf4MnYw"),
album: None, album: None,
view_count: Some(3900000), view_count: Some(3900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2469,7 +2469,7 @@ expression: map_res.c
artist_id: Some("UC7a1tClTvIIttpI-r2PwiQA"), artist_id: Some("UC7a1tClTvIIttpI-r2PwiQA"),
album: None, album: None,
view_count: Some(1200000), view_count: Some(1200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2502,7 +2502,7 @@ expression: map_res.c
artist_id: Some("UCBvOwJ62CE_EdXLhrFceElg"), artist_id: Some("UCBvOwJ62CE_EdXLhrFceElg"),
album: None, album: None,
view_count: Some(272000), view_count: Some(272000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2531,7 +2531,7 @@ expression: map_res.c
artist_id: Some("UC3PFoXZPFJGy9F4PG84nNQQ"), artist_id: Some("UC3PFoXZPFJGy9F4PG84nNQQ"),
album: None, album: None,
view_count: Some(371000), view_count: Some(371000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2560,7 +2560,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(3200000), view_count: Some(3200000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2593,7 +2593,7 @@ expression: map_res.c
artist_id: Some("UCQd9dydn5gaib_uuVVkYZTQ"), artist_id: Some("UCQd9dydn5gaib_uuVVkYZTQ"),
album: None, album: None,
view_count: Some(42000), view_count: Some(42000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2622,7 +2622,7 @@ expression: map_res.c
artist_id: Some("UC1snMKQQ0kl280XfQmg9tpQ"), artist_id: Some("UC1snMKQQ0kl280XfQmg9tpQ"),
album: None, album: None,
view_count: Some(657000), view_count: Some(657000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2651,7 +2651,7 @@ expression: map_res.c
artist_id: Some("UCsIkF_bbTB9jjGIkT4j82rQ"), artist_id: Some("UCsIkF_bbTB9jjGIkT4j82rQ"),
album: None, album: None,
view_count: Some(49000), view_count: Some(49000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2680,7 +2680,7 @@ expression: map_res.c
artist_id: Some("UCrXRY_7SVVmc6TykwhRGUNQ"), artist_id: Some("UCrXRY_7SVVmc6TykwhRGUNQ"),
album: None, album: None,
view_count: Some(6900000), view_count: Some(6900000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2709,7 +2709,7 @@ expression: map_res.c
artist_id: Some("UCof4hiuvv9BPhVCh90QHErw"), artist_id: Some("UCof4hiuvv9BPhVCh90QHErw"),
album: None, album: None,
view_count: Some(489000), view_count: Some(489000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2738,7 +2738,7 @@ expression: map_res.c
artist_id: Some("UCtfmGsduggb9uGGsC9lhfKQ"), artist_id: Some("UCtfmGsduggb9uGGsC9lhfKQ"),
album: None, album: None,
view_count: Some(931000), view_count: Some(931000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2767,7 +2767,7 @@ expression: map_res.c
artist_id: Some("UCdmScpl5nFGLy3xCnfHjDsw"), artist_id: Some("UCdmScpl5nFGLy3xCnfHjDsw"),
album: None, album: None,
view_count: Some(2100000), view_count: Some(2100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2796,7 +2796,7 @@ expression: map_res.c
artist_id: Some("UCTfTV3COQIaxnvZPjNbXApw"), artist_id: Some("UCTfTV3COQIaxnvZPjNbXApw"),
album: None, album: None,
view_count: Some(476000), view_count: Some(476000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2825,7 +2825,7 @@ expression: map_res.c
artist_id: Some("UCHN9HRrB-BeOtSVKhVT7t_w"), artist_id: Some("UCHN9HRrB-BeOtSVKhVT7t_w"),
album: None, album: None,
view_count: Some(328000), view_count: Some(328000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2854,7 +2854,7 @@ expression: map_res.c
artist_id: Some("UChZSUPxvkA2ATGLY9zZrxnQ"), artist_id: Some("UChZSUPxvkA2ATGLY9zZrxnQ"),
album: None, album: None,
view_count: Some(483000), view_count: Some(483000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2883,7 +2883,7 @@ expression: map_res.c
artist_id: Some("UCzj_b294hukUPPkbwgiMmLQ"), artist_id: Some("UCzj_b294hukUPPkbwgiMmLQ"),
album: None, album: None,
view_count: Some(1100000), view_count: Some(1100000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2912,7 +2912,7 @@ expression: map_res.c
artist_id: Some("UCoS1Y8yS22Z1r3wVmHllaLA"), artist_id: Some("UCoS1Y8yS22Z1r3wVmHllaLA"),
album: None, album: None,
view_count: Some(683000), view_count: Some(683000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),
@ -2941,7 +2941,7 @@ expression: map_res.c
artist_id: None, artist_id: None,
album: None, album: None,
view_count: Some(11000), view_count: Some(11000),
track_type: video, is_video: true,
track_nr: None, track_nr: None,
by_va: false, by_va: false,
), ),

View file

@ -40,7 +40,7 @@ MusicAlbum(
], ],
artist_id: Some("UCXGYZ-OhdOpPBamHX3K9YRg"), artist_id: Some("UCXGYZ-OhdOpPBamHX3K9YRg"),
description: None, description: None,
album_type: single, album_type: Single,
year: Some(2020), year: Some(2020),
by_va: false, by_va: false,
tracks: [ tracks: [
@ -65,7 +65,7 @@ MusicAlbum(
name: "Der Himmel reißt auf", name: "Der Himmel reißt auf",
)), )),
view_count: Some(12000000), view_count: Some(12000000),
track_type: video, is_video: true,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
), ),

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