mirror of
https://github.com/foo-dogsquared/nixos-config.git
synced 2025-04-24 18:19:11 +00:00
tasks/multimedia-archive: use Newpipe database for yt-dlp
This commit is contained in:
parent
10131d58be
commit
ef8580ebe6
@ -1,206 +0,0 @@
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.tasks.multimedia-archive;
|
||||
mountName = "/mnt/archives";
|
||||
in {
|
||||
options.tasks.multimedia-archive.enable =
|
||||
lib.mkEnableOption "multimedia archiving setup";
|
||||
|
||||
config = lib.mkIf cfg.enable (let
|
||||
yt-dlp-args = [
|
||||
# Make a global list of successfully downloaded videos as a cache for yt-dlp.
|
||||
"--download-archive ${config.services.yt-dlp.archivePath}/videos"
|
||||
|
||||
# No overwriting of videos and related files.
|
||||
"--no-force-overwrites"
|
||||
|
||||
# Embed metadata in the file.
|
||||
"--write-info-json"
|
||||
|
||||
# Embed chapter markers, if possible.
|
||||
"--embed-chapters"
|
||||
|
||||
# Write the subtitle file.
|
||||
"--write-subs"
|
||||
|
||||
# Write the description in a separate file.
|
||||
"--write-description"
|
||||
|
||||
# The global output for all of the jobs.
|
||||
"--output '%(uploader,artist,creator|Unknown)s/%(release_date>%F,upload_date>%F|Unknown)s-%(title)s.%(ext)s'"
|
||||
|
||||
# Select only the most optimal format for my usecases.
|
||||
"--format '(webm,mkv,mp4)[height<=?1280]'"
|
||||
|
||||
# Prefer MKV whenever possible for video formats.
|
||||
"--merge-output-format mkv"
|
||||
|
||||
# Don't download any videos that are originally live streams.
|
||||
"--match-filters '!was_live'"
|
||||
|
||||
# Prefer Vorbis when audio-only downloads are used.
|
||||
"--audio-format vorbis"
|
||||
"--audio-quality 2"
|
||||
];
|
||||
yt-dlp-archive-variant = pkgs.writeScriptBin "yt-dlp-archive-variant" ''
|
||||
${pkgs.yt-dlp}/bin/yt-dlp ${lib.escapeShellArgs yt-dlp-args}
|
||||
'';
|
||||
in {
|
||||
environment.systemPackages = [ yt-dlp-archive-variant ];
|
||||
fileSystems."${mountName}" = {
|
||||
device = "/dev/disk/by-uuid/6ba86a30-5fa4-41d9-8354-fa8af0f57f49";
|
||||
fsType = "btrfs";
|
||||
noCheck = true;
|
||||
options = [
|
||||
# These are btrfs-specific mount options which can found in btrfs.5
|
||||
# manual page.
|
||||
"subvol=@"
|
||||
"noatime"
|
||||
"compress=zstd:9"
|
||||
"space_cache=v2"
|
||||
|
||||
# General mount options from mount.5 manual page.
|
||||
"noauto"
|
||||
"nofail"
|
||||
"user"
|
||||
|
||||
# See systemd.mount.5 and systemd.automount.5 manual page for more
|
||||
# details.
|
||||
"x-systemd.automount"
|
||||
"x-systemd.idle-timeout=2"
|
||||
"x-systemd.device-timeout=2"
|
||||
];
|
||||
};
|
||||
|
||||
services.yt-dlp = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/yt-dlp-service";
|
||||
|
||||
# This is applied on all jobs. It is best to be minimal as much as
|
||||
# possible for this.
|
||||
extraArgs = yt-dlp-args;
|
||||
|
||||
jobs = {
|
||||
arts = {
|
||||
urls = [
|
||||
"https://www.youtube.com/channel/UCjdHbo8_vh3rxQ-875XGkvw" # 3DSage
|
||||
"https://www.youtube.com/channel/UCHv_hNLkxqlcY20MwVyayfw" # Ali Bahabadi
|
||||
"https://www.youtube.com/c/boroCG" # BoroCG
|
||||
"https://www.youtube.com/c/DavidRevoy" # David Revoy
|
||||
"https://www.youtube.com/channel/UCGMyyn2FdEFcDfP1wQRh5lQ" # Erindale
|
||||
"https://www.youtube.com/c/Jazza" # Jazza
|
||||
"https://www.youtube.com/channel/UCcBnT6LsxANZjUWqpjR8Jpw" # Marcello Barenghi
|
||||
"https://www.youtube.com/c/ronillust" # ronillust
|
||||
];
|
||||
startAt = "Friday";
|
||||
extraArgs = [
|
||||
"--playlist-end 20" # Only check the first N videos.
|
||||
];
|
||||
};
|
||||
|
||||
compsci = {
|
||||
urls = [
|
||||
"https://www.youtube.com/channel/UC_mYaQAE6-71rjSN6CeCA-g" # NeetCode
|
||||
"https://www.youtube.com/c/ThePrimeagen" # ThePrimeagen
|
||||
"https://www.youtube.com/c/EasyTheory" # EasyTheory
|
||||
"https://www.youtube.com/c/K%C3%A1rolyZsolnai" # Two Minute Papers
|
||||
"https://www.youtube.com/c/TheCodingTrain" # TheCodingTrain
|
||||
];
|
||||
startAt = "Thursday";
|
||||
extraArgs = [
|
||||
"--playlist-end 20" # Only check the first N videos.
|
||||
];
|
||||
};
|
||||
|
||||
cooking = {
|
||||
urls = [
|
||||
"https://www.youtube.com/channel/UCJHA_jMfCvEnv-3kRjTCQXw" # Babish Culinary Universe
|
||||
"https://www.youtube.com/channel/UCb5QRUn5w8_g0j8QVaWzcjQ" # BORE.D
|
||||
"https://www.youtube.com/channel/UCzqbfYjQmf9nLQPMxVgPhiA" # emmymade
|
||||
"https://www.youtube.com/channel/UCgmOd6sRQRK7QoSazOfaIjQ" # Emma's Goodies
|
||||
"https://www.youtube.com/channel/UCcp9uRaBwInxl_SZqGRksDA" # Hidamari Cooking
|
||||
"https://www.youtube.com/channel/UCvQrjgLj841wiQAKDgtKFOw" # Ninong Ry
|
||||
"https://www.youtube.com/channel/UCekQr9znsk2vWxBo3YiLq2w" # You Suck at Cooking
|
||||
"https://www.youtube.com/channel/UCUAKaXyq2hVBCph1LOUtuqg" # 집밥요리 Home Cooking
|
||||
];
|
||||
startAt = "Sunday";
|
||||
extraArgs = [
|
||||
"--playlist-end 15" # Check the first N videos.
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.archivebox = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/archivebox-service";
|
||||
withDependencies = true;
|
||||
webserver.enable = true;
|
||||
|
||||
jobs = {
|
||||
arts = {
|
||||
links = [
|
||||
"https://www.davidrevoy.com/feed/rss"
|
||||
"https://librearts.org/index.xml"
|
||||
];
|
||||
startAt = "monthly";
|
||||
};
|
||||
|
||||
computer = {
|
||||
links = [
|
||||
"https://blog.mozilla.org/en/feed/"
|
||||
"https://distill.pub/rss.xml"
|
||||
"https://drewdevault.com/blog/index.xml"
|
||||
"https://fasterthanli.me/index.xml"
|
||||
"https://jvns.ca/atom.xml"
|
||||
"https://www.bytelab.codes/rss/"
|
||||
"https://www.collabora.com/feed"
|
||||
"https://www.jntrnr.com/atom.xml"
|
||||
"https://yosoygames.com.ar/wp/?feed=rss"
|
||||
"https://simblob.blogspot.com/feeds/posts/default"
|
||||
];
|
||||
startAt = "weekly";
|
||||
};
|
||||
|
||||
projects = {
|
||||
links = [
|
||||
"https://veloren.net/rss.xml"
|
||||
"https://guix.gnu.org/feeds/blog.atom"
|
||||
"https://fedoramagazine.org/feed/"
|
||||
"https://nixos.org/blog/announcements-rss.xml"
|
||||
];
|
||||
# Practically every 14 days.
|
||||
startAt = "Mon *-*-1/14";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.gallery-dl = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/gallery-dl-service";
|
||||
|
||||
extraArgs = [
|
||||
# Record all downloaded files in an archive file.
|
||||
"--download-archive ${config.services.gallery-dl.archivePath}/photos"
|
||||
|
||||
# Write metadata to separate JSON files.
|
||||
"--write-metadata"
|
||||
];
|
||||
|
||||
jobs = {
|
||||
arts = {
|
||||
urls = [
|
||||
"https://www.deviantart.com/xezeno" # Xezeno
|
||||
#"https://www.pixiv.net/en/users/60562229" # Ravioli
|
||||
"https://www.artstation.com/kuvshinov_ilya" # Ilya Kuvshinov
|
||||
"https://www.artstation.com/meiipng" # Meiiart
|
||||
"https://www.artstation.com/bassem_wageeh" # Bassem wageeh
|
||||
"https://hyperjerk.newgrounds.com" # HyperJerk
|
||||
];
|
||||
startAt = "weekly";
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
40
modules/nixos/tasks/multimedia-archive/README.adoc
Normal file
40
modules/nixos/tasks/multimedia-archive/README.adoc
Normal file
@ -0,0 +1,40 @@
|
||||
= Multimedia archiving
|
||||
:toc:
|
||||
|
||||
More like offline delivery, really.
|
||||
Just wait for the task to complete and you have your videos, pictures, music, and whatever questionable files you want to download.
|
||||
It's a nice offline repository for it especially that internet usually randomly disconnects so that's nice while I still have something working, yeah?
|
||||
|
||||
|
||||
|
||||
|
||||
== Integrating with Newpipe subscriptions
|
||||
|
||||
In this task, I usually just download videos from YouTube.
|
||||
While I could note every preferred creator manually, I could automate them by getting a list of subscriptions from my Newpipe config which I use surprisingly more often than I thought.
|
||||
This is done by running the link:./convert-newpipe-db-to-json[`./convert-newpipe-db-to-json`] script and specifying the exported Newpipe database (as a ZIP file).
|
||||
|
||||
[CAUTION]
|
||||
====
|
||||
Please don't run the task with all of the subscriptions.
|
||||
You should select only a few categories and clean them up.
|
||||
====
|
||||
|
||||
[source, sh]
|
||||
----
|
||||
./convert-newpipe-db-to-json ~/Downloads/NewPipeData-20220714_185126.zip
|
||||
----
|
||||
|
||||
You can run the script with the `-h` flag for more information.
|
||||
There are nifty things you can do with the script.
|
||||
Such as the following code block which you can interactively select which folders to export.
|
||||
|
||||
[source, sh]
|
||||
----
|
||||
./convert-newpipe-db-to-json ~/Downloads/NewPipeData-20220714_185126.zip --list-categories \
|
||||
| fzf --multi --prompt "Choose which categories to export " \
|
||||
| ./convert-newpipe-db-to-json ~/Downloads/NewPipeData-20220714_185126.zip -o ./newpipe-db.json
|
||||
----
|
||||
|
||||
Remember the larger the list, the larger the chances for a throttling.
|
||||
Thus, it is heavily encouraged that you clean up your list (and/or get good at organizing your categories) before activating the updated version.
|
106
modules/nixos/tasks/multimedia-archive/convert-newpipe-db-to-json
Executable file
106
modules/nixos/tasks/multimedia-archive/convert-newpipe-db-to-json
Executable file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p python3
|
||||
|
||||
# This script is used for generating a JSON object from a Newpipe database to
|
||||
# be used for multimedia archive task (i.e.,
|
||||
# `config.tasks.multimedia-archive`).
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import sqlite3
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import fileinput
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def kebab_case(string):
|
||||
string = string.lower()
|
||||
string = re.sub("\s+", "-", string)
|
||||
string = re.sub("[^a-zA-Z0-9-]", "", string)
|
||||
string = re.sub("-+", "-", string)
|
||||
string = re.sub("^-|-$", "", string)
|
||||
return string
|
||||
|
||||
|
||||
def extract_categories_from_db(db_file, categories):
|
||||
with sqlite3.connect(db_file) as db:
|
||||
db.row_factory = sqlite3.Row
|
||||
query = '''
|
||||
SELECT subscriptions.name AS name, subscriptions.url AS url, feed_group.name AS tag
|
||||
FROM subscriptions
|
||||
INNER JOIN feed_group_subscription_join AS subs_join
|
||||
INNER JOIN feed_group
|
||||
ON subs_join.subscription_id = subscriptions.uid AND feed_group.uid = subs_join.group_id
|
||||
ORDER BY name COLLATE NOCASE;
|
||||
'''
|
||||
|
||||
data = { kebab_case(category) : [] for category in categories }
|
||||
|
||||
for row in db.execute(query):
|
||||
tag = row["tag"]
|
||||
if tag in categories:
|
||||
data[kebab_case(tag)].append({ "url": row["url"], "name": row["name"] })
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def list_categories(db_file):
|
||||
with sqlite3.connect(db_file) as db:
|
||||
query = '''
|
||||
SELECT name FROM feed_group ORDER BY name;
|
||||
'''
|
||||
data = []
|
||||
for row in db.execute(query):
|
||||
data.append(row[0])
|
||||
return data
|
||||
|
||||
|
||||
def extract_db(newpipe_archive):
|
||||
tmpdir = tempfile.mkdtemp(suffix="convert-newpipe-db")
|
||||
|
||||
shutil.unpack_archive(newpipe_archive, tmpdir)
|
||||
return Path(tmpdir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("newpipe_db", metavar="NEWPIPE_DB", help="Newpipe database file (as a zip file) exported straight from the app.")
|
||||
parser.add_argument("categories", metavar="CATEGORIES", nargs="*", help="A list of categories to be extracted. If absent, it will extract with all categories.")
|
||||
parser.add_argument("--list-categories", "-l", action="store_true", help="List all categories from the database.")
|
||||
parser.add_argument("--output", "-o", action="store", metavar="FILE", help="If present, store the output in the given file")
|
||||
|
||||
args = parser.parse_args()
|
||||
newpipe_archive = args.newpipe_db
|
||||
tmpdir = extract_db(newpipe_archive)
|
||||
db_file = tmpdir / "newpipe.db"
|
||||
|
||||
if args.list_categories:
|
||||
for category in list_categories(db_file):
|
||||
print(category)
|
||||
else:
|
||||
categories = []
|
||||
if not sys.stdin.isatty():
|
||||
for line in sys.stdin:
|
||||
categories.append(line.strip())
|
||||
|
||||
if len(args.categories) > 0:
|
||||
categories = args.categories
|
||||
elif len(categories) == 0:
|
||||
categories = list_categories(db_file)
|
||||
|
||||
data = extract_categories_from_db(db_file, categories)
|
||||
|
||||
output_file = args.output
|
||||
if output_file:
|
||||
with open(output_file, mode="w", encoding="UTF-8") as file:
|
||||
json.dump(data, file, sort_keys=True, indent=2)
|
||||
else:
|
||||
print(json.dumps(data, sort_keys=True, indent=2))
|
||||
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
# vi:ft=python:ts=4
|
185
modules/nixos/tasks/multimedia-archive/default.nix
Normal file
185
modules/nixos/tasks/multimedia-archive/default.nix
Normal file
@ -0,0 +1,185 @@
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.tasks.multimedia-archive;
|
||||
mountName = "/mnt/archives";
|
||||
in
|
||||
{
|
||||
options.tasks.multimedia-archive.enable =
|
||||
lib.mkEnableOption "multimedia archiving setup";
|
||||
|
||||
config = lib.mkIf cfg.enable (
|
||||
let
|
||||
yt-dlp-args = [
|
||||
# Make a global list of successfully downloaded videos as a cache for yt-dlp.
|
||||
"--download-archive ${config.services.yt-dlp.archivePath}/videos"
|
||||
|
||||
# No overwriting of videos and related files.
|
||||
"--no-force-overwrites"
|
||||
|
||||
# Embed metadata in the file.
|
||||
"--write-info-json"
|
||||
|
||||
# Embed chapter markers, if possible.
|
||||
"--embed-chapters"
|
||||
|
||||
# Write the subtitle file.
|
||||
"--write-subs"
|
||||
|
||||
# Write the description in a separate file.
|
||||
"--write-description"
|
||||
|
||||
# The global output for all of the jobs.
|
||||
"--output '%(uploader,artist,creator|Unknown)s/%(release_date>%F,upload_date>%F|Unknown)s-%(title)s.%(ext)s'"
|
||||
|
||||
# Select only the most optimal format for my usecases.
|
||||
"--format '(webm,mkv,mp4)[height<=?1280]'"
|
||||
|
||||
# Prefer MKV whenever possible for video formats.
|
||||
"--merge-output-format mkv"
|
||||
|
||||
# Don't download any videos that are originally live streams.
|
||||
"--match-filters '!was_live'"
|
||||
|
||||
# Prefer Vorbis when audio-only downloads are used.
|
||||
"--audio-format vorbis"
|
||||
"--audio-quality 2"
|
||||
];
|
||||
yt-dlp-archive-variant = pkgs.writeScriptBin "yt-dlp-archive-variant" ''
|
||||
${pkgs.yt-dlp}/bin/yt-dlp ${lib.escapeShellArgs yt-dlp-args}
|
||||
'';
|
||||
|
||||
# Given an attribute set of URLs, create a yt-dlp service config that does the following.
|
||||
create-yt-dlp-service-config = newpipe-db:
|
||||
let
|
||||
days = [ "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday" ];
|
||||
categories = lib.zipListsWith
|
||||
(index: category: { inherit index; data = category; })
|
||||
(lib.lists.range 1 (lib.length (lib.attrValues newpipe-db)))
|
||||
(lib.mapAttrsToList (name: value: { inherit name; subscriptions = value; }) newpipe-db);
|
||||
jobsList = builtins.map
|
||||
(category: {
|
||||
name = category.data.name;
|
||||
value = {
|
||||
urls = builtins.map (subscription: subscription.url) category.data.subscriptions;
|
||||
startAt = lib.elemAt days (lib.mod category.index (lib.length days));
|
||||
extraArgs = [
|
||||
"--playlist-end 20" # Only check the last 20 videos.
|
||||
];
|
||||
};
|
||||
})
|
||||
categories;
|
||||
in
|
||||
lib.listToAttrs jobsList;
|
||||
in
|
||||
{
|
||||
environment.systemPackages = [ yt-dlp-archive-variant ];
|
||||
fileSystems."${mountName}" = {
|
||||
device = "/dev/disk/by-uuid/6ba86a30-5fa4-41d9-8354-fa8af0f57f49";
|
||||
fsType = "btrfs";
|
||||
noCheck = true;
|
||||
options = [
|
||||
# These are btrfs-specific mount options which can found in btrfs.5
|
||||
# manual page.
|
||||
"subvol=@"
|
||||
"noatime"
|
||||
"compress=zstd:9"
|
||||
"space_cache=v2"
|
||||
|
||||
# General mount options from mount.5 manual page.
|
||||
"noauto"
|
||||
"nofail"
|
||||
"user"
|
||||
|
||||
# See systemd.mount.5 and systemd.automount.5 manual page for more
|
||||
# details.
|
||||
"x-systemd.automount"
|
||||
"x-systemd.idle-timeout=2"
|
||||
"x-systemd.device-timeout=2"
|
||||
];
|
||||
};
|
||||
|
||||
services.yt-dlp = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/yt-dlp-service";
|
||||
|
||||
# This is applied on all jobs. It is best to be minimal as much as
|
||||
# possible for this.
|
||||
extraArgs = yt-dlp-args;
|
||||
|
||||
jobs = create-yt-dlp-service-config (builtins.fromJSON (builtins.readFile ./newpipe-db.json));
|
||||
};
|
||||
|
||||
services.archivebox = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/archivebox-service";
|
||||
withDependencies = true;
|
||||
webserver.enable = true;
|
||||
|
||||
jobs = {
|
||||
arts = {
|
||||
links = [
|
||||
"https://www.davidrevoy.com/feed/rss"
|
||||
"https://librearts.org/index.xml"
|
||||
];
|
||||
startAt = "monthly";
|
||||
};
|
||||
|
||||
computer = {
|
||||
links = [
|
||||
"https://blog.mozilla.org/en/feed/"
|
||||
"https://distill.pub/rss.xml"
|
||||
"https://drewdevault.com/blog/index.xml"
|
||||
"https://fasterthanli.me/index.xml"
|
||||
"https://jvns.ca/atom.xml"
|
||||
"https://www.bytelab.codes/rss/"
|
||||
"https://www.collabora.com/feed"
|
||||
"https://www.jntrnr.com/atom.xml"
|
||||
"https://yosoygames.com.ar/wp/?feed=rss"
|
||||
"https://simblob.blogspot.com/feeds/posts/default"
|
||||
];
|
||||
startAt = "weekly";
|
||||
};
|
||||
|
||||
projects = {
|
||||
links = [
|
||||
"https://veloren.net/rss.xml"
|
||||
"https://guix.gnu.org/feeds/blog.atom"
|
||||
"https://fedoramagazine.org/feed/"
|
||||
"https://nixos.org/blog/announcements-rss.xml"
|
||||
];
|
||||
# Practically every 14 days.
|
||||
startAt = "Mon *-*-1/14";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.gallery-dl = {
|
||||
enable = true;
|
||||
archivePath = "${mountName}/gallery-dl-service";
|
||||
|
||||
extraArgs = [
|
||||
# Record all downloaded files in an archive file.
|
||||
"--download-archive ${config.services.gallery-dl.archivePath}/photos"
|
||||
|
||||
# Write metadata to separate JSON files.
|
||||
"--write-metadata"
|
||||
];
|
||||
|
||||
jobs = {
|
||||
arts = {
|
||||
urls = [
|
||||
"https://www.deviantart.com/xezeno" # Xezeno
|
||||
#"https://www.pixiv.net/en/users/60562229" # Ravioli
|
||||
"https://www.artstation.com/kuvshinov_ilya" # Ilya Kuvshinov
|
||||
"https://www.artstation.com/meiipng" # Meiiart
|
||||
"https://www.artstation.com/bassem_wageeh" # Bassem wageeh
|
||||
"https://hyperjerk.newgrounds.com" # HyperJerk
|
||||
];
|
||||
startAt = "weekly";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
518
modules/nixos/tasks/multimedia-archive/newpipe-db.json
Normal file
518
modules/nixos/tasks/multimedia-archive/newpipe-db.json
Normal file
@ -0,0 +1,518 @@
|
||||
{
|
||||
"3d-modelling": [
|
||||
{
|
||||
"name": "3DSage",
|
||||
"url": "https://www.youtube.com/channel/UCjdHbo8_vh3rxQ-875XGkvw"
|
||||
},
|
||||
{
|
||||
"name": "Andy Front",
|
||||
"url": "https://www.youtube.com/channel/UCmNewVWrKOn667okSlM0OlA"
|
||||
},
|
||||
{
|
||||
"name": "Blender Secrets",
|
||||
"url": "https://www.youtube.com/channel/UCp7EwodJcppc6GqiRcnCpOw"
|
||||
},
|
||||
{
|
||||
"name": "CG Boost",
|
||||
"url": "https://www.youtube.com/channel/UCWWybvw9jnpOdJq_6wTHryA"
|
||||
},
|
||||
{
|
||||
"name": "CG Fast Track",
|
||||
"url": "https://www.youtube.com/channel/UCsvgY1GWmJwvk3o6UeXVxAg"
|
||||
},
|
||||
{
|
||||
"name": "CGMatter",
|
||||
"url": "https://www.youtube.com/channel/UCy1f4m64dwCwk8CBZ_vHfPg"
|
||||
},
|
||||
{
|
||||
"name": "CrossMind Studio",
|
||||
"url": "https://www.youtube.com/channel/UCHihootMqyGz175gqOPahtw"
|
||||
},
|
||||
{
|
||||
"name": "Derek Elliott",
|
||||
"url": "https://www.youtube.com/channel/UCk7IufzS4r8v76NeWR6A3dg"
|
||||
},
|
||||
{
|
||||
"name": "Ducky 3D",
|
||||
"url": "https://www.youtube.com/channel/UCuNhGhbemBkdflZ1FGJ0lUQ"
|
||||
},
|
||||
{
|
||||
"name": "Erindale",
|
||||
"url": "https://www.youtube.com/channel/UCGMyyn2FdEFcDfP1wQRh5lQ"
|
||||
},
|
||||
{
|
||||
"name": "Grant Abbitt",
|
||||
"url": "https://www.youtube.com/channel/UCZFUrFoqvqlN8seaAeEwjlw"
|
||||
},
|
||||
{
|
||||
"name": "henning",
|
||||
"url": "https://www.youtube.com/channel/UCidwqcHWZf1qBnqoUx9N6rA"
|
||||
},
|
||||
{
|
||||
"name": "Hoolopee",
|
||||
"url": "https://www.youtube.com/channel/UCE_gw5ybJdGAcyZFy6TfScw"
|
||||
},
|
||||
{
|
||||
"name": "IanHubert",
|
||||
"url": "https://www.youtube.com/channel/UCbmxZRQk-X0p-TOxd6PEYJA"
|
||||
},
|
||||
{
|
||||
"name": "Jhanrell 3D",
|
||||
"url": "https://www.youtube.com/channel/UCsl5Po2vPkFOmi27EL8qFGg"
|
||||
},
|
||||
{
|
||||
"name": "Markom3D",
|
||||
"url": "https://www.youtube.com/channel/UCdlNVsQys37ETeTDqQqiHFQ"
|
||||
},
|
||||
{
|
||||
"name": "Polyfjord",
|
||||
"url": "https://www.youtube.com/channel/UC1MmrnDaPnNa55twYBiH4NA"
|
||||
},
|
||||
{
|
||||
"name": "Shonzo",
|
||||
"url": "https://www.youtube.com/channel/UCVwc3XV94ifVOonbFP6-7tw"
|
||||
},
|
||||
{
|
||||
"name": "t3ssel8r",
|
||||
"url": "https://www.youtube.com/channel/UCIjUIjWig0r5DIixQrt6A3A"
|
||||
},
|
||||
{
|
||||
"name": "William Landgren",
|
||||
"url": "https://www.youtube.com/channel/UC_v-Rg-FYBUfkF4GLcMDEcg"
|
||||
},
|
||||
{
|
||||
"name": "YanSculpts",
|
||||
"url": "https://www.youtube.com/channel/UCfjswDVU0XHyBN7UFG0Mi5Q"
|
||||
}
|
||||
],
|
||||
"art": [
|
||||
{
|
||||
"name": "Aki-Anyway",
|
||||
"url": "https://www.youtube.com/channel/UCldLZEAgmkEO0vep4OvabPQ"
|
||||
},
|
||||
{
|
||||
"name": "Ali Bahabadi",
|
||||
"url": "https://www.youtube.com/channel/UCHv_hNLkxqlcY20MwVyayfw"
|
||||
},
|
||||
{
|
||||
"name": "Blackthornprod",
|
||||
"url": "https://www.youtube.com/channel/UC9Z1XWw1kmnvOOFsj6Bzy2g"
|
||||
},
|
||||
{
|
||||
"name": "Bobby Duke Arts",
|
||||
"url": "https://www.youtube.com/channel/UCSC1HqVmTaE4Shn32ihbC7w"
|
||||
},
|
||||
{
|
||||
"name": "Brandon James Greer",
|
||||
"url": "https://www.youtube.com/channel/UCC26K7LTSrJK0BPAUyyvtQg"
|
||||
},
|
||||
{
|
||||
"name": "Challenge Clay Craft",
|
||||
"url": "https://www.youtube.com/channel/UCkKyQpfQCClQLJreqiORliw"
|
||||
},
|
||||
{
|
||||
"name": "Corridor Crew",
|
||||
"url": "https://www.youtube.com/channel/UCSpFnDQr88xCZ80N-X7t0nQ"
|
||||
},
|
||||
{
|
||||
"name": "creosfera",
|
||||
"url": "https://www.youtube.com/channel/UCAHVy9XS5Fz1WMRHIzAodUQ"
|
||||
},
|
||||
{
|
||||
"name": "D_NOT_So_Good_Artist",
|
||||
"url": "https://www.youtube.com/channel/UC505T67IfBzIQRyDOen3seg"
|
||||
},
|
||||
{
|
||||
"name": "Danny Casale",
|
||||
"url": "https://www.youtube.com/channel/UClNHWmlNIgEXLotLtlY2mLw"
|
||||
},
|
||||
{
|
||||
"name": "David Revoy",
|
||||
"url": "https://www.youtube.com/channel/UCnAbNwJjusY7zQ__sQyJlSA"
|
||||
},
|
||||
{
|
||||
"name": "Denis Godyna",
|
||||
"url": "https://www.youtube.com/channel/UCTJiWCp2fhugnic2tCTqsLg"
|
||||
},
|
||||
{
|
||||
"name": "Design Theory",
|
||||
"url": "https://www.youtube.com/channel/UCdgUN8rX3SEb9L7FDub3I6A"
|
||||
},
|
||||
{
|
||||
"name": "dillongoo",
|
||||
"url": "https://www.youtube.com/channel/UC-B06UJxJ20HYv15lzrm9mA"
|
||||
},
|
||||
{
|
||||
"name": "Dollarwang",
|
||||
"url": "https://www.youtube.com/channel/UCQf7HdP7F_UoA8TfjEfzr4Q"
|
||||
},
|
||||
{
|
||||
"name": "Emanuele Colombo",
|
||||
"url": "https://www.youtube.com/channel/UCSvf1bh9DEL7glMzX1aYQUA"
|
||||
},
|
||||
{
|
||||
"name": "Erindale",
|
||||
"url": "https://www.youtube.com/channel/UCGMyyn2FdEFcDfP1wQRh5lQ"
|
||||
},
|
||||
{
|
||||
"name": "Henry Segerman",
|
||||
"url": "https://www.youtube.com/channel/UC4zzTEL5tuIgGMvzjk1Ozbg"
|
||||
},
|
||||
{
|
||||
"name": "Jazza",
|
||||
"url": "https://www.youtube.com/channel/UCHu2KNu6TtJ0p4hpSW7Yv7Q"
|
||||
},
|
||||
{
|
||||
"name": "JujuArts",
|
||||
"url": "https://www.youtube.com/channel/UCuTcJu9dZM8ZdgCKOxQLXuw"
|
||||
},
|
||||
{
|
||||
"name": "Logos By Nick",
|
||||
"url": "https://www.youtube.com/channel/UCEQXp_fcqwPcqrzNtWJ1w9w"
|
||||
},
|
||||
{
|
||||
"name": "Lucifer King",
|
||||
"url": "https://www.youtube.com/channel/UCIu9_S0kuKXgA2h5_egIx-A"
|
||||
},
|
||||
{
|
||||
"name": "Marcello Barenghi",
|
||||
"url": "https://www.youtube.com/channel/UCcBnT6LsxANZjUWqpjR8Jpw"
|
||||
},
|
||||
{
|
||||
"name": "Marco Bucci",
|
||||
"url": "https://www.youtube.com/channel/UCsDxB-CSMQ0Vu_hTag7-2UQ"
|
||||
},
|
||||
{
|
||||
"name": "ronillust",
|
||||
"url": "https://www.youtube.com/channel/UCAaXLLoXnRF8EPUy5b5b6Vw"
|
||||
},
|
||||
{
|
||||
"name": "Sinix Design",
|
||||
"url": "https://www.youtube.com/channel/UCUQTqWAaSzhAKRanOpes1nA"
|
||||
},
|
||||
{
|
||||
"name": "t3ssel8r",
|
||||
"url": "https://www.youtube.com/channel/UCIjUIjWig0r5DIixQrt6A3A"
|
||||
}
|
||||
],
|
||||
"comsci": [
|
||||
{
|
||||
"name": "Andreas Kling",
|
||||
"url": "https://www.youtube.com/channel/UC3ts8coMP645hZw9JSD3pqQ"
|
||||
},
|
||||
{
|
||||
"name": "Bisqwit",
|
||||
"url": "https://www.youtube.com/channel/UCKTehwyGCKF-b2wo0RKwrcg"
|
||||
},
|
||||
{
|
||||
"name": "Bits inside by Ren\u00e9 Rebe",
|
||||
"url": "https://www.youtube.com/channel/UCJLLl6AraX1POemgLfhirwg"
|
||||
},
|
||||
{
|
||||
"name": "carykh",
|
||||
"url": "https://www.youtube.com/channel/UC9z7EZAbkphEMg0SP7rw44A"
|
||||
},
|
||||
{
|
||||
"name": "CodeParade",
|
||||
"url": "https://www.youtube.com/channel/UCrv269YwJzuZL3dH5PCgxUw"
|
||||
},
|
||||
{
|
||||
"name": "Computerphile",
|
||||
"url": "https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA"
|
||||
},
|
||||
{
|
||||
"name": "Creel",
|
||||
"url": "https://www.youtube.com/channel/UCq7dxy_qYNEBcHqQVCbc20w"
|
||||
},
|
||||
{
|
||||
"name": "CS50",
|
||||
"url": "https://www.youtube.com/channel/UCcabW7890RKJzL968QWEykA"
|
||||
},
|
||||
{
|
||||
"name": "Easy Theory",
|
||||
"url": "https://www.youtube.com/channel/UC3VY6RTXegnoSD_q446oBdg"
|
||||
},
|
||||
{
|
||||
"name": "Junferno",
|
||||
"url": "https://www.youtube.com/channel/UCRb6Mw3fJ6OFzp-cB9X29aA"
|
||||
},
|
||||
{
|
||||
"name": "NeetCode",
|
||||
"url": "https://www.youtube.com/channel/UC_mYaQAE6-71rjSN6CeCA-g"
|
||||
},
|
||||
{
|
||||
"name": "The Coding Train",
|
||||
"url": "https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw"
|
||||
},
|
||||
{
|
||||
"name": "Two Minute Papers",
|
||||
"url": "https://www.youtube.com/c/K%C3%A1rolyZsolnai"
|
||||
},
|
||||
{
|
||||
"name": "ThePrimeagen",
|
||||
"url": "https://www.youtube.com/channel/UC8ENHE5xdFSwx71u3fDH5Xw"
|
||||
}
|
||||
],
|
||||
"food": [
|
||||
{
|
||||
"name": "Adam Ragusea",
|
||||
"url": "https://www.youtube.com/channel/UC9_p50tH3WmMslWRWKnM7dQ"
|
||||
},
|
||||
{
|
||||
"name": "Apron",
|
||||
"url": "https://www.youtube.com/channel/UCgzJrXg7oh7lj-bMC-pzkgw"
|
||||
},
|
||||
{
|
||||
"name": "Babish Culinary Universe",
|
||||
"url": "https://www.youtube.com/channel/UCJHA_jMfCvEnv-3kRjTCQXw"
|
||||
},
|
||||
{
|
||||
"name": "BORED",
|
||||
"url": "https://www.youtube.com/channel/UCb5QRUn5w8_g0j8QVaWzcjQ"
|
||||
},
|
||||
{
|
||||
"name": "Cookrate - Cakes",
|
||||
"url": "https://www.youtube.com/channel/UCy3QtOQ7NiSEOs7JO-7vq7A"
|
||||
},
|
||||
{
|
||||
"name": "Emma's Goodies",
|
||||
"url": "https://www.youtube.com/channel/UCgmOd6sRQRK7QoSazOfaIjQ"
|
||||
},
|
||||
{
|
||||
"name": "emmymade",
|
||||
"url": "https://www.youtube.com/channel/UCzqbfYjQmf9nLQPMxVgPhiA"
|
||||
},
|
||||
{
|
||||
"name": "Ethan Chlebowski",
|
||||
"url": "https://www.youtube.com/channel/UCDq5v10l4wkV5-ZBIJJFbzQ"
|
||||
},
|
||||
{
|
||||
"name": "HidaMari Cooking",
|
||||
"url": "https://www.youtube.com/channel/UCcp9uRaBwInxl_SZqGRksDA"
|
||||
},
|
||||
{
|
||||
"name": "Judy Ann's Kitchen",
|
||||
"url": "https://www.youtube.com/channel/UC5xdS3lFApjvOs9zdys3eOw"
|
||||
},
|
||||
{
|
||||
"name": "Kirbyyy",
|
||||
"url": "https://www.youtube.com/channel/UCyC964gYH7eV4_icDGg8qVw"
|
||||
},
|
||||
{
|
||||
"name": "Made With Lau",
|
||||
"url": "https://www.youtube.com/channel/UCsIF9vk-I_PV1P-ShDFA84A"
|
||||
},
|
||||
{
|
||||
"name": "Nino's Home",
|
||||
"url": "https://www.youtube.com/channel/UCKetFmtqdh-kn915crdf72A"
|
||||
},
|
||||
{
|
||||
"name": "Ninong Ry",
|
||||
"url": "https://www.youtube.com/channel/UCvQrjgLj841wiQAKDgtKFOw"
|
||||
},
|
||||
{
|
||||
"name": "Pinoy Easy Recipes",
|
||||
"url": "https://www.youtube.com/channel/UCk5KE4BdmU32sdV4kvkAv5w"
|
||||
},
|
||||
{
|
||||
"name": "You Suck At Cooking",
|
||||
"url": "https://www.youtube.com/channel/UCekQr9znsk2vWxBo3YiLq2w"
|
||||
},
|
||||
{
|
||||
"name": "\uc9d1\ubc25\uc694\ub9ac Home Cooking",
|
||||
"url": "https://www.youtube.com/channel/UCUAKaXyq2hVBCph1LOUtuqg"
|
||||
},
|
||||
{
|
||||
"name": "\ud478\ub4dc\ud0b9\ub364 Food Kingdom",
|
||||
"url": "https://www.youtube.com/channel/UC4BfinFCS1o6t1tAsl0RVWQ"
|
||||
}
|
||||
],
|
||||
"music": [
|
||||
{
|
||||
"name": "500L/g",
|
||||
"url": "https://www.youtube.com/channel/UCjZjUymRDAhp9c1rb0X6aww"
|
||||
},
|
||||
{
|
||||
"name": "acrouzet",
|
||||
"url": "https://www.youtube.com/channel/UClv1kZDpIA9LcXPYY4KTU-w"
|
||||
},
|
||||
{
|
||||
"name": "AJR",
|
||||
"url": "https://www.youtube.com/channel/UCQ5w3fSomzziZfO7neK7eAg"
|
||||
},
|
||||
{
|
||||
"name": "ALAMAT",
|
||||
"url": "https://www.youtube.com/channel/UCOnUfJpp-Fg8X2TnuH_JD7w"
|
||||
},
|
||||
{
|
||||
"name": "Andre Antunes",
|
||||
"url": "https://www.youtube.com/channel/UC8zTlrhQ0w1-TZjc2-jdcag"
|
||||
},
|
||||
{
|
||||
"name": "Asian Shoegaze",
|
||||
"url": "https://www.youtube.com/channel/UCWubfmD-UY92kgCct3PXvZQ"
|
||||
},
|
||||
{
|
||||
"name": "Ayase / YOASOBI",
|
||||
"url": "https://www.youtube.com/channel/UCvpredjG93ifbCP1Y77JyFA"
|
||||
},
|
||||
{
|
||||
"name": "Beyond The Guitar",
|
||||
"url": "https://www.youtube.com/channel/UC8LgDpDsFXiwYupTihnTF5g"
|
||||
},
|
||||
{
|
||||
"name": "Boid",
|
||||
"url": "https://www.youtube.com/channel/UCp8BU3mHY3Vw9WBPLjAJVcQ"
|
||||
},
|
||||
{
|
||||
"name": "BOZESTYLE",
|
||||
"url": "https://www.youtube.com/channel/UCm5j5Ls1w4EQsZ_9GUT7mXg"
|
||||
},
|
||||
{
|
||||
"name": "Bulby",
|
||||
"url": "https://www.youtube.com/channel/UCz6zvgkf6eKpgqlUZQstOtQ"
|
||||
},
|
||||
{
|
||||
"name": "CAP'STONE",
|
||||
"url": "https://soundcloud.com/capcom-sound"
|
||||
},
|
||||
{
|
||||
"name": "Chip Jockey",
|
||||
"url": "https://www.youtube.com/channel/UCfbi12o4I5f27w13Mkqv7Sg"
|
||||
},
|
||||
{
|
||||
"name": "Dylan Tallchief",
|
||||
"url": "https://www.youtube.com/channel/UCIu2Fj4x_VMn2dgSB1bFyQA"
|
||||
},
|
||||
{
|
||||
"name": "FalKKonE",
|
||||
"url": "https://www.youtube.com/channel/UChAHYPBvyaQIpjyTSdQhOMQ"
|
||||
},
|
||||
{
|
||||
"name": "Forrest Brazeal",
|
||||
"url": "https://www.youtube.com/channel/UCt5LsaDWTEBY7FThtp37LfQ"
|
||||
},
|
||||
{
|
||||
"name": "GaMetal",
|
||||
"url": "https://www.youtube.com/channel/UCK9Hl6LXPaooxMTvsOutw3A"
|
||||
},
|
||||
{
|
||||
"name": "George Collier",
|
||||
"url": "https://www.youtube.com/channel/UCigygyPkHm07o-wQvkET7Og"
|
||||
},
|
||||
{
|
||||
"name": "HALIDONMUSIC",
|
||||
"url": "https://www.youtube.com/channel/UCyOfqgtsQaM3S-VZnsYnHjQ"
|
||||
},
|
||||
{
|
||||
"name": "insaneintherainmusic",
|
||||
"url": "https://www.youtube.com/channel/UC_OtnV-9QZmBj6oWBelMoZw"
|
||||
},
|
||||
{
|
||||
"name": "John Tay",
|
||||
"url": "https://www.youtube.com/channel/UCbaO2SkJlbWwQyFCEPgLLow"
|
||||
},
|
||||
{
|
||||
"name": "Kiichi Kobayashi",
|
||||
"url": "https://www.youtube.com/channel/UCQMNXjJN-LdNSkUblrxPpCg"
|
||||
},
|
||||
{
|
||||
"name": "King Gnu official YouTube channel",
|
||||
"url": "https://www.youtube.com/channel/UCkB8HnJSDSJ2hkLQFUc-YrQ"
|
||||
},
|
||||
{
|
||||
"name": "losprimerosVIIVI",
|
||||
"url": "https://www.youtube.com/channel/UCMSVWxNp1lkEGpDClzm8Qvw"
|
||||
},
|
||||
{
|
||||
"name": "Maximix",
|
||||
"url": "https://www.youtube.com/channel/UCnvKCS1NzLmzOd4-LynA_jw"
|
||||
},
|
||||
{
|
||||
"name": "MayTree",
|
||||
"url": "https://www.youtube.com/channel/UC3mY2SKYhPjqImtBBXsR6_Q"
|
||||
},
|
||||
{
|
||||
"name": "Pan Piano",
|
||||
"url": "https://www.youtube.com/channel/UCI7ktPB6toqucpkkCiolwLg"
|
||||
},
|
||||
{
|
||||
"name": "RichaadEB",
|
||||
"url": "https://www.youtube.com/channel/UCPM1bCbT-dVAHAEIpUUpVLQ"
|
||||
},
|
||||
{
|
||||
"name": "Ruscel Torres",
|
||||
"url": "https://www.youtube.com/channel/UCjo880FeW792wHeNlaUXMjQ"
|
||||
},
|
||||
{
|
||||
"name": "Shady Cicada",
|
||||
"url": "https://www.youtube.com/channel/UC-90KuSWRVLImW4xHWFYMnQ"
|
||||
},
|
||||
{
|
||||
"name": "SID",
|
||||
"url": "https://www.youtube.com/channel/UC7jHTm2dDkhckKyOWUavuXg"
|
||||
},
|
||||
{
|
||||
"name": "Tee Lopes",
|
||||
"url": "https://www.youtube.com/channel/UC3Va-8NnzTuV-Yv-JlyuQsQ"
|
||||
},
|
||||
{
|
||||
"name": "The8BitDrummer",
|
||||
"url": "https://www.youtube.com/channel/UCEHXNknwbsRu73QsakWIdzQ"
|
||||
},
|
||||
{
|
||||
"name": "\u30b2\u30b9\u306e\u6975\u307f\u4e59\u5973 Official YouTube Channel",
|
||||
"url": "https://www.youtube.com/channel/UC0pHUMEOtul5NlaT-Rt-34w"
|
||||
},
|
||||
{
|
||||
"name": "\u548c\u306c\u304b",
|
||||
"url": "https://www.youtube.com/channel/UCm7rYZ6xOw3m5GaTMuUJjVw"
|
||||
}
|
||||
],
|
||||
"random-stuff": [
|
||||
{
|
||||
"name": "Atomic Shrimp",
|
||||
"url": "https://www.youtube.com/channel/UCSl5Uxu2LyaoAoMMGp6oTJA"
|
||||
},
|
||||
{
|
||||
"name": "Cody'sLab",
|
||||
"url": "https://www.youtube.com/channel/UCu6mSoMNzHQiBIOCkHUa2Aw"
|
||||
},
|
||||
{
|
||||
"name": "JK Brickworks",
|
||||
"url": "https://www.youtube.com/channel/UCUaiGrBfRCaC6pL7ZnZjWbg"
|
||||
},
|
||||
{
|
||||
"name": "Junferno",
|
||||
"url": "https://www.youtube.com/channel/UCRb6Mw3fJ6OFzp-cB9X29aA"
|
||||
},
|
||||
{
|
||||
"name": "LockPickingLawyer",
|
||||
"url": "https://www.youtube.com/channel/UCm9K6rby98W8JigLoZOh6FQ"
|
||||
},
|
||||
{
|
||||
"name": "Nick Zammeti",
|
||||
"url": "https://www.youtube.com/channel/UC3-0S7vXfwYY2jj5EkMpymA"
|
||||
},
|
||||
{
|
||||
"name": "Origami with Jo Nakashima",
|
||||
"url": "https://www.youtube.com/channel/UC3ICcukYYeSn26KlCRnhOhA"
|
||||
},
|
||||
{
|
||||
"name": "Peter Knetter",
|
||||
"url": "https://www.youtube.com/channel/UCNMyoMaXJZITZaRKCz7G23Q"
|
||||
},
|
||||
{
|
||||
"name": "Sabaton History",
|
||||
"url": "https://www.youtube.com/channel/UCaG4CBbZih6nLzD08bTBGfw"
|
||||
},
|
||||
{
|
||||
"name": "Steve1989MREInfo",
|
||||
"url": "https://www.youtube.com/channel/UC2I6Et1JkidnnbWgJFiMeHA"
|
||||
},
|
||||
{
|
||||
"name": "WAY OUT WEST with Sandra and Tim",
|
||||
"url": "https://www.youtube.com/channel/UCSViWfOV4pEcYnzpV6w548Q"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user