Giter Site home page Giter Site logo

hoshinova's Introduction

hoshinova

Monitor YouTube channels and automatically run ytarchive when the channel goes live.

⚠️ Unstable Software: This program is under heavy development. It works, but will still undergo a lot of breaking changes. Upgrade with caution.

Discord GitHub release

Screenshot of Web UI

Features

  • Monitor and automatically record live streams from multiple YouTube channels
  • View and manage recording status from a web interface
  • Receive notifications whenever a stream goes live, or has finished being recorded

Install

You can get hoshinova using either one of the following methods.

Get the latest release

You can download the latest binaries from the releases page.

Windows x86-64 Linux x86-64 Linux aarch64

Make sure you have ytarchive and ffmpeg installed and executable in your PATH (guide).

Run from source

# Clone the repository
git clone https://github.com/HoloArchivists/hoshinova

# Generate TypeScript bindings
cd hoshinova
cargo test

# Build the web UI
cd web
yarn install && yarn build

# Build and run hoshinova
cd ..
cargo run

Get the docker image

docker pull ghcr.io/holoarchivists/hoshinova:main

Run with Docker:

docker run -d \
  -p 1104:1104 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  -v $(pwd)/videos:/app/videos \
  -v $(pwd)/temp:/app/temp \
  ghcr.io/holoarchivists/hoshinova:main

** Note **: When running in Docker, the service runs as UID 1000, so bind-mount permissions need to be set correctly: chown -R 1000:1000 ./videos ./temp (adjust paths to whatever locations your mounting to the Docker container volumes).

Or with docker-compose:

version: "3"
services:
  hoshinova:
    image: ghcr.io/holoarchivists/hoshinova:main
    restart: unless-stopped
    ports:
      - 127.0.0.1:1104:1104
    volumes:
      - ./config.toml:/app/config.toml
      - ./temp/:/app/temp
      - ./videos:/app/videos

** Note **: When using docker-compose, restarting the service will not update it. To update, use docker-compose pull and then docker-compose up -d.

Configure

Copy the config.example.toml file to config.toml and edit the file as needed.

ytarchive configuration

[ytarchive]
executable_path = "ytarchive"
working_directory = "temp"
args = [
  "--vp9", "--thumbnail", "--add-metadata", "--threads", "4",
  "--retry-stream", "30",
  "--output", "%(upload_date)s %(title)s [%(channel)s] (%(id)s)"
]
quality = "best"
delay_start = "1s"

The default configuration should work for most cases. If you don't have ytarchive in your PATH, you can specify absolute path in the executable_path section (for example, /home/user/bin/ytarchive).

You can also set a different working_directory. This is the place where ytarchive will download videos to while it's live. After it's done, the files will be moved to the output_directory configured in each channel (see below).

By default, the --wait flag is added automatically. You can add more flags too, if you need to use cookies, change the number of threads, etc. Just note that each argument needs to be a separate item in the list (for example, ["--threads", "4"] instead of ["--threads 4"]).

The delay_start parameter can also be adjusted if you are starting a lot of downloads simultaneously. The parameter add some delay between launching ytarchive instances.

scrapers and notifiers

[scraper.rss]
poll_interval = "30s"
ignore_older_than = "24h"

Right now there's only an RSS scraper. More may be added in the future. You can change the poll_interval, which specifies how long to wait between checking the RSS feeds of each channel.

You can use the ignore_older_than parameter to skip checking videos that are older than the specified duration. This is useful if your filters match a lot of videos and don't want to hit rate limits during startup.

[notifier.discord]
webhook_url = "https://discordapp.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz"
notify_on = ["waiting", "recording", "done", "failed"]

This part is optional. You can remove this section if you don't want any notifications.

Right now you can only send notifications to Discord. You can get the webhook_url by following these instructions. The notify_on setting lets you specify which events you want to be notified about. Right now there are only 4 events:

Event Description
waiting The stream waiting room is available but it's not live yet
recording The stream has just started and is being recorded
done The stream is over
failed Something went wrong while recording the stream

webserver

A webserver is available for you to view and monitor your tasks. If you don't want this, you can remove this section.

[webserver]
bind_address = "0.0.0.0:1104"

bind_address is the address the webserver will listen to. Setting the address to 0.0.0.0:1104 will let anyone access the web interface. If you only want to access it from the computer you're running hoshinova from, set the address to 127.0.0.1:1104.

Feel free to adjust the port :1104 to any number up to 65535.

Note that if you're running in Docker, you most likely want to set the bind address to 0.0.0.0.

channel configuration

[[channel]]
id = "UCP0BspO_AMEe3aQqqpo89Dg"
name = "Moona Hoshinova"
filters = ["(?i)MoonUtau|Karaoke|Archive"]
outpath = "./videos/moona"

This part can be copy-pasted multiple times to monitor and record multiple channels. The id field is the channel ID. It's the ending part of e.g. https://www.youtube.com/channel/UCP0BspO_AMEe3aQqqpo89Dg.

If you have a https://www.youtube.com/c/SomeName URL you can use this bookmarklet to convert it to a /channel/ URL:

javascript:window.location=ytInitialData.metadata.channelMetadataRenderer.channelUrl

The name can be anything, it's just to help you identify the channel in the config file.

filters is a list of regular expressions to match on video titles. You can check the syntax here.

outpath is the output folder where you want the resulting videos to be moved to.

Creating release builds

Use the helper script build.sh to generate optimized release binaries for multiple targets. It uses cross-rs, which uses Docker, to automatically set up the build environment for cross-compilation.

If you run into any linking issues, run cargo clean and try again.

Debug logging

Run with the environment variable RUST_LOG=debug or RUST_LOG=trace to enable verbose logging.

RUST_LOG=debug cargo run

Note that you will likely also get debug logs from libraries that hoshinova depends on. To only get debug logs for hoshinova, use RUST_LOG=hoshinova=debug. For more information, see env_logger's documentation.

Support

Discord

This is very early in development. New features will be added, and existing features may be changed or removed without notice. We do not make any guarantees on the software's stability.

That being said, we're open to accepting input, bug reports, and contributions. If you run into any issues, feel free to hop on our Discord, or file an issue.

hoshinova's People

Contributors

1toldyou avatar bdruth avatar hizkifw avatar ryu1845 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

hoshinova's Issues

moving a completed download fails with "invalid cross-device link"

https://de.catbox.moe/j3kkw0.png
in my config, the uploader module was configured as such

uploaders:

  • name: D partition
    type: local
    config:
    path: /mnt/d/hoshinova
    base_url: 'https://***.de/d/hoshinova'

The path is pointing to a mounted NTFS partition. Since source and destination are on different partitions/FS, the current implementation in uploader/local.go using os.Rename fails. I replaced invoking os.Rename with a call to this function from SO (https://stackoverflow.com/a/50741908) which allowed the upload to complete.
https://de.catbox.moe/k8vyxg.png

This should make for more robust file moving overall.

[Architecture Request] `arm64` docker image

For the same reason as outlined in HoloArchivists/fc2-live-dl#45
But adding platforms: linux/amd64, linux/arm64 to the GitHub Actions workflow is insufficient.

I noticed numerous x86_64 targeted build processes within the Dockerfile and failed on local build.

cargo build --locked --release --target x86_64-unknown-linux-musl; \

cargo build --locked --release --target x86_64-unknown-linux-musl

/src/target/x86_64-unknown-linux-musl/release/hoshinova \

🥺

Edit: I forked the repo, replaced all x86_64 with aarch64 and successfully built it in own aarch64 server. But I have no idea how should the whole CI be modified (and seemed to be very slow per build).

Suggestion: Allows custom args for each [[channel]]

Sometimes I want to customize "--output" in the args of each anchor, because the channels name is too long, I want to make a short abbreviation myself, I need to customize "--output" to customize the name of each channel

For example, I want to custom: "--output", "%(upload_date)s-Vestia_Zeta-%(title)s"

Implement monitoring /live endpoint as alternative to parsing rss feed

There are three scenarios to account for

  • Request is forwarded to live stream -> start yta
  • Request is forwarded to channel page -> do nothing
  • Request is forwarded to waiting room -> do nothing
    Waiting rooms can sometimes be scheduled frames for free chat or weekly schedules, which will likely never go live and could spawn long-waiting yta processes which also add to rate limit margin, especially when starting up

Some <entry> elements from YT rss feed don't parse

Some entries from YT rss feed seem to fail parsing or are discarded. The videos can be added manually and are recorded as expected (based on similar, past cases for which I didn't open an issue). I checked trace-level logs but the two video ID never appeared. The filter I'm applying should match everything. Running 0.2.2 in Docker v20.10.21 on Windows

[2023-01-07T06:32:50Z INFO  hoshinova] hoshinova v0.2.2 (23435f3)
[2023-01-07T06:32:50Z DEBUG hoshinova] Git hash: 23435f3334ac948707543763c6b973b39cb8d904
[2023-01-07T06:32:50Z DEBUG hoshinova] Built on: 2022-11-05T04:06:50.280737947+00:00

Config

[ytarchive]
executable_path = "ytarchive"
working_directory = "temp"
args = ["--vp9", "--thumbnail", "--add-metadata", "--threads", "4", "--output", "%(upload_date)s %(title)s [%(channel)s] (%(id)s)", "--cookies", "/app/youtube.com_cookies.txt", "--retry-stream", "120"]
quality = "best"
delay_start = "3s"

[scraper.rss]
poll_interval = "30s"

[notifier.discord]
webhook_url = "hook url here"
notify_on = ["recording", "done", "failed"]

# Coming soon, a web interface to view and manage tasks.
# Optional, remove this section to disable.
[webserver]
bind_address = "0.0.0.0:1104"

[[channel]]
id = "UCu1INzefw3R7M9-3QEHYmMQ"
name = "Nene Amano"
filters = [".*"]
outpath = "/app/kawaii/Nene Amano"

Exerpt from https://www.youtube.com/feeds/videos.xml?channel_id=UCu1INzefw3R7M9-3QEHYmMQ

<entry>
<id>yt:video:zjUlh5lVFDs</id>
<yt:videoId>zjUlh5lVFDs</yt:videoId>
<yt:channelId>UCu1INzefw3R7M9-3QEHYmMQ</yt:channelId>
<title>【ASMR/KU100】WIFE Becomes Your Bunny Servant [Onee-san Voice/$10,000 Mic]</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=zjUlh5lVFDs"/>
<author>
<name>Nene Amano Ch. 天野寧々【kawaii】</name>
<uri>https://www.youtube.com/channel/UCu1INzefw3R7M9-3QEHYmMQ</uri>
</author>
<published>2023-01-05T02:07:53+00:00</published>
<updated>2023-01-05T22:13:45+00:00</updated>
<media:group>
<media:title>【ASMR/KU100】WIFE Becomes Your Bunny Servant [Onee-san Voice/$10,000 Mic]</media:title>
<media:content url="https://www.youtube.com/v/zjUlh5lVFDs?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
<media:thumbnail url="https://i3.ytimg.com/vi/zjUlh5lVFDs/hqdefault.jpg" width="480" height="360"/>
<media:description>#VTuber #ENVTuber #nenetan Using over $10,000 of audio equipment. The KU100 ASMR mic! (Whispering/Oil Massage/Heartbeat/Ear Cleaning) ☁️Become a memeber and support Tenshi!!☁️ https://www.youtube.com/channel/UCu1INzefw3R7M9-3QEHYmMQ/join ☁️Donation Link!☁️ https://streamlabs.com/Neneamano ☁️1st Anniversary Celebration Merch☁️ https://productionkawaii.booth.pm/items/3816918 ☁️Twitter☁️ https://twitter.com/amanene_kawaii Thumbnail by https://twitter.com/nakanomaru925 ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ ଘ(❁•̀ 3 •́)━☆*✲⋆[Chat Rules]☆*✲⋆ Let’s make this a fun and enjoyable place for everyone! ε(*´・ω・)з If rules are not followed, your comment may get deleted/muted. 1. No spoilers! They are my worst enemy! 2. Please be nice to other people watching. Don’t spam or troll. 3. Please understand that I try to respond to every comment, but it’s not possible for me to ​engage with every comment. Don’t take it personally if I don’t read your comment out loud! 4. If you see spam or trolling, don’t engage with it. Just block, report, and ignore it. Tenshi will take care of getting rid of them. ଘ(˵╹-╹)━☆ 5. Please keep the conversation relevant to the stream content. Asking me questions is ok, just make sure they are appropriate! 6. Please don’t cross the line with overly inappropriate/sensitive/sexual comments 7. I will only listen to backseating when I need help, and will ask for help if needed. Please keep in mind I may not listen to advice because I want to try for myself. 8. Please don’t talk about other streamers unless I engage the conversation myself. 9. I don’t do shout outs/saying name requests outside of superchats/donations. ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ kawaii members: 【Reina Sun 🍣 レイナ・サン】 https://www.youtube.com/channel/UCKJexadCeNo3lu0U20skmNg Twitter: https://twitter.com/reina_kawaiii 【Charlotte Suzu 🩸 シャーロット・スズ】 https://www.youtube.com/channel/UCoAw3SML_09dF-7yhQU8wFQ Twitter: https://twitter.com/charsuzu_kawaii 【Isla Coleman 👸 アイラ・コールマン】 https://www.youtube.com/channel/UCuaDidGk4HNLOnQE9wzMMeQ Twitter: https://twitter.com/isla_kawaii Kawaii Gen 2 Members: 【Lua Asuka Ch. 飛鳥瑠藍】 【Shee Icho Ch. 銀杏しい】 【Namiji Freesia Ch. ナミジ・フリージア】 Kawaii Gen 3 Members: 【Oceane Otoishi Ch. 音衣詩おしあん 】 【Aletta Sky Ch. アレッタ・スカイ】 【Miryu Kotofuji Ch. 琴藤みりゅう】 【Peony Aeria Ch. ピオニー・エリア】 【Sava Safari Ch. サヴァ・サファリ】 ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ Contact /お問い合わせ 【プロダクション「kawaii」HP】 https://production-kawaii.com/en/home-2/ 【「kawaii」公式Twitter】 https://twitter.com/kawaiiOfficial5​✧┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✧ I'm your angel, you're my angel! It's Nene! VTuber from production kawaii (Japan) who does chatting, gaming, and ASMR! I use a 3dio mic and a KU100 to do binaural ASMR. I can guarantee you will enjoy my waifu/wife ASMR. I can speak Tenshi, English, and Japanese! I also love to sing and rap, and have a 3D model coming in January!! Idol Tenshi coming soon! I enjoy games like Elden Ring, Dragon Age, FGO, Fate/Stay Night, Umineko, and more! I'm a big anime/manga fan who likes series such as Vinland Saga, JJBA, Berserk, Steins;Gate, chainsawman, FMA fullmetal alchemist, and more. I'm a big fan of VTubers such as Hololive. HololiveEN and HoliveJP are inspirations. I was inspired by Shirogane Noel, Mori Calliope, Gawr Gura, Ceres Fauna, Ouro Kronii, and more. VShojo and Nijisanji inspired me as well through art, digitalart, singing, game review, let's play, gaming and more. I quit my job to become a virtual animegirl and virtualyoutuber and will continue to fulfill my dream and lots of livestreaming to bring you joy. I usually stream every day! (and post lots of meme anime meme, animememe, anime funny).</media:description>
<media:community>
<media:starRating count="102" average="5.00" min="1" max="5"/>
<media:statistics views="0"/>
</media:community>
</media:group>
</entry>
<entry>
<id>yt:video:XHIrqwyMD6Q</id>
<yt:videoId>XHIrqwyMD6Q</yt:videoId>
<yt:channelId>UCu1INzefw3R7M9-3QEHYmMQ</yt:channelId>
<title>I’m Going to Destroy Amelia’s Entire Family [Bloodborne]</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=XHIrqwyMD6Q"/>
<author>
<name>Nene Amano Ch. 天野寧々【kawaii】</name>
<uri>https://www.youtube.com/channel/UCu1INzefw3R7M9-3QEHYmMQ</uri>
</author>
<published>2023-01-05T01:37:18+00:00</published>
<updated>2023-01-05T09:51:47+00:00</updated>
<media:group>
<media:title>I’m Going to Destroy Amelia’s Entire Family [Bloodborne]</media:title>
<media:content url="https://www.youtube.com/v/XHIrqwyMD6Q?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
<media:thumbnail url="https://i1.ytimg.com/vi/XHIrqwyMD6Q/hqdefault.jpg" width="480" height="360"/>
<media:description>#VTuber #ENVTuber #nenetan ☁️Donation Link!☁️ https://streamlabs.com/Neneamano Special alert sounds for $1, $10, $39, $50, $60, $100, $104, and $1000 donations! ☁️Become a memeber and support Tenshi!!☁️ https://www.youtube.com/channel/UCu1INzefw3R7M9-3QEHYmMQ/join ☁️Oppai Mousepad, Acrylic Stand, and more! Official Merch!☁️ https://booth.pm/en/search/Nene%20Amano ☁️Twitter☁️ https://twitter.com/amanene_kawaii Thumbnail is created with novelai. It is an ai generated image. ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ ଘ(❁•̀ 3 •́)━☆*✲⋆[Chat Rules]☆*✲⋆ Let’s make this a fun and enjoyable place for everyone! ε(*´・ω・)з If rules are not followed, your comment may get deleted/muted. 1. No spoilers! They are my worst enemy! 2. Please be nice to other people watching. Don’t spam or troll. 3. Please understand that I try to respond to every comment, but it’s not possible for me to ​engage with every comment. Don’t take it personally if I don’t read your comment out loud! 4. If you see spam or trolling, don’t engage with it. Just block, report, and ignore it. Tenshi will take care of getting rid of them. ଘ(˵╹-╹)━☆ 5. Please keep the conversation relevant to the stream content. Asking me questions is ok, just make sure they are appropriate! 6. Please don’t cross the line with overly inappropriate/sensitive/sexual comments 7. I will only listen to backseating when I need help, and will ask for help if needed. Please keep in mind I may not listen to advice because I want to try for myself. 8. Please don’t talk about other streamers unless I engage the conversation myself. 9. I don’t do shout outs/saying name requests outside of superchats/donations. ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ kawaii members: 【Reina Sun 🍣 レイナ・サン】 https://www.youtube.com/channel/UCKJexadCeNo3lu0U20skmNg Twitter: https://twitter.com/reina_kawaiii 【Charlotte Suzu 🩸 シャーロット・スズ】 https://www.youtube.com/channel/UCoAw3SML_09dF-7yhQU8wFQ Twitter: https://twitter.com/charsuzu_kawaii 【Isla Coleman 👸 アイラ・コールマン】 https://www.youtube.com/channel/UCuaDidGk4HNLOnQE9wzMMeQ Twitter: https://twitter.com/isla_kawaii ✰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✰ Contact /お問い合わせ 【プロダクション「kawaii」HP】 https://production-kawaii.com/en/home-2/ 【「kawaii」公式Twitter】 https://twitter.com/kawaiiOfficial5​✧┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈✧</media:description>
<media:community>
<media:starRating count="54" average="5.00" min="1" max="5"/>
<media:statistics views="0"/>
</media:community>
</media:group>
</entry>

Integrade apprise to allow for more notification options

Integrate apprise to enable sending notification to a huge array of platforms (discord, slack, telegram, email, etc.), with minimal configurations needed.

an example config would look like:

[notifier.apprise]
# array of apprise urls 
# ex. send notification to discord, telegram, pushover, and email
# reference apprise documentation for more options
apprise_urls = [
	"discord://webhook_id/webhook_token",
	"tgram://bottoken/ChatID", 
	"pover://user@token",
	"mailtos://userid:[email protected]"]
	
notify_on = ["waiting", "recording", "done", "failed"]

the apprise call would look like:

apprise --title 'Hoshinova' --body 'Recording x' \
   'discord://webhook_id/webhook_token' \
   'tgram://bottoken/ChatID' \
   'pover://user@token' \
   'mailtos://userid:[email protected]'

Relevant documentations
URL formats (point users to this)
Installation
CLI usage

v0.2.4

Re-adding streams that errored out

My yta runs with --retry-stream option, for reasons, and occasionally there is a period where the cally to YT made by yta will encounter some error. This temporary issue causes yta to close and hoshinova to list those streams as error on the web interface. I believe there are similar temporary issues with different unknown yta output that can also occur.

Once more advanced yta output handling is implemented, I'd love an option to have hoshinova re-add a queued job that errored out. For now, the workaround is to copy the URL and re-add it manually.

[2023-02-20T10:52:32Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:32 WARNING: Failed to retrieve data from https://youtu.be/Whc_01aacJY: Get "https://youtu.be/Whc_01aacJY": dial tcp: i/o timeout
[2023-02-20T10:52:33Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:33 WARNING: Failed to retrieve data from https://youtu.be/p7peFaDfDhY: Get "https://youtu.be/p7peFaDfDhY": dial tcp: i/o timeout
[2023-02-20T10:52:45Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:45 WARNING: Failed to retrieve data from https://youtu.be/wxlIFhpsRXU: Get "https://youtu.be/wxlIFhpsRXU": dial tcp: i/o timeout
[2023-02-20T10:52:45Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:45 WARNING: Failed to retrieve data from https://youtu.be/bBNWNfckUi4: Get "https://youtu.be/bBNWNfckUi4": dial tcp: i/o timeout
[2023-02-20T10:52:50Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:50 WARNING: Failed to retrieve data from https://youtu.be/TlnRKz5D1CU: Get "https://youtu.be/TlnRKz5D1CU": dial tcp: i/o timeout
[2023-02-20T10:52:59Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:59 WARNING: Failed to retrieve data from https://youtu.be/Jm3oo8dYxIM: Get "https://youtu.be/Jm3oo8dYxIM": dial tcp: i/o timeout
[2023-02-20T10:52:59Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:52:59 WARNING: Failed to retrieve data from https://youtu.be/mwvzktlABrk: Get "https://youtu.be/mwvzktlABrk": dial tcp: i/o timeout
[2023-02-20T10:53:03Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:53:03 WARNING: Failed to retrieve data from https://youtu.be/OZAWyFf_kak: Get "https://youtu.be/OZAWyFf_kak": dial tcp: i/o timeout
[2023-02-20T10:53:05Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/02/20 11:53:05 WARNING: Failed to retrieve data from https://youtu.be/t0en1SAPLL8: Get "https://youtu.be/t0en1SAPLL8": dial tcp: i/o timeout

Unschedule tasks

At the moment, the API only allows to schedule tasks, but doesn't allow cancelling them. This is pretty essential since if you e.g. schedule the wrong stream, you have to restart the entire app, and lose all of the existing tasks.

Suggestion: Add option to dump a video’s info.json

Once videos have been downloaded I move them to my Jellyfin server where metadata is used to help organize the videos. While ytarchive does embed some metadata into the final file there is a lot more information that is only present in the videos json dump.

Adding this may not be super straight forward as ytarchive does not support dumping the json. However yt-dlp does and can download just the json using a single command.

yt-dlp --write-info-json --skip-download < Video URL >

Direct file URI in web UI

It would be great if there was a direct URI to the downloaded video file for finished recordings in the web UI, so that you could directly open it. Or for starters, present the path to it, since it is otherwise impossible to know where it was saved to.

Implement module to limit requests counting towards rate limit

When starting up with several channels to monitor, a lot of yta instances can spawn at once (many of which will be discarded based on yta output) which can quickly exceed rate limit. By creating a module that governs all requests that count towards rate limit, the actions can be spaced out and dispatched from a queue one after another. I'm not sure if a safe rate is known (in which case, force that) but I envision a user setting to define how many seconds of wait should be between requests, to space them out equally.

Stream is re-added to downloading immediately after finishing

After a stream ends and the video is considered downloaded, hoshinova restarts the download.
The first capture is never moved to the correct folder.

This is using the latest build of hoshinova, and a master build of ytarchive from a couple of weeks ago.

Here is the relevant log output:

[2023-05-14T08:57:19Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=    1 fps=0.0 q=-1.0 size=     256kB time=00:00:00.00 bitrate=2097152.0kbits/s speed=12.2x
[2023-05-14T08:57:19Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=44811 fps=0.0 q=-1.0 size=  166144kB time=00:24:55.16 bitrate= 910.3kbits/s speed=2.99e+03x
[2023-05-14T08:57:20Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=83440 fps=83420 q=-1.0 size=  357632kB time=00:46:24.09 bitrate=1052.3kbits/s speed=2.78e+03x
[2023-05-14T08:57:20Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=124799 fps=83176 q=-1.0 size=  534528kB time=01:09:24.10 bitrate=1051.6kbits/s speed=2.78e+03x
[2023-05-14T08:57:21Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=167537 fps=83741 q=-1.0 size=  707584kB time=01:33:10.13 bitrate=1036.9kbits/s speed=2.79e+03x
[2023-05-14T08:57:21Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=208933 fps=83551 q=-1.0 size=  884736kB time=01:56:11.38 bitrate=1039.6kbits/s speed=2.79e+03x
[2023-05-14T08:57:22Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=255193 fps=85046 q=-1.0 size= 1043456kB time=02:21:54.93 bitrate=1003.9kbits/s speed=2.84e+03x
[2023-05-14T08:57:22Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=294508 fps=84129 q=-1.0 size= 1232128kB time=02:43:46.75 bitrate=1027.2kbits/s speed=2.81e+03x
[2023-05-14T08:57:23Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=340354 fps=85075 q=-1.0 size= 1391360kB time=03:09:16.48 bitrate=1003.7kbits/s speed=2.84e+03x
[2023-05-14T08:57:23Z WARN  hoshinova::module::recorder] Unknown ytarchive output: frame=343162 fps=84978 q=-1.0 Lsize= 1405123kB time=03:10:50.17 bitrate=1005.3kbits/s speed=2.84e+03x
[2023-05-14T08:57:23Z WARN  hoshinova::module::recorder] Unknown ytarchive output: 2023/05/14 08:57:23
[2023-05-14T08:57:23Z INFO  hoshinova::module::recorder] [P8gMi0mtJGw][ぶいすぽっ!【公式】][ぶ
いすぽっ!VALORANT紅白戦 Presented by GALLERIA] Recording finished
[2023-05-14T08:57:23Z INFO  hoshinova::module::recorder] [P8gMi0mtJGw][ぶいすぽっ!【公式】][ぶ
いすぽっ!VALORANT紅白戦 Presented by GALLERIA] Recording started

I suspect it is happening because:

  • For some reason that causes ytarchive to output a result like this when muxing to mkv fps=28668 q=-1.0 size= 8109824kB time=04:12:12.00 bitrate=4390.4kbits/frame=909692 fps=28278 q=-1.0 size= 8125952kB time=04:12:41.61 bitrate=4390.5kbits/frame=914073 fps=27955 q=-1.0 size= 8166656kB time=04:13:54.63 bitrate=4391.4kbits/frame=920200 fps=27711 q=-1.0 size= 8223744kB time=04:15:36.74 bitrate=4392.6kbits/frame=926620 fps=27489 q=-1.0 size= 8283648kB time=04:17:23.74 bitrate=4394.0kbits/frame=938175 fps=27422 q=-1.0 size= 8390912kB time=04:20:36.34 bitrate=4396.1kbits/frame=944924
  • That isn't captured by hoshinova, so when the file finishes muxing the download restarts. I tried suppressing this with --quiet but that didn't show any started downloads with the webui.

Possibly related is that video fragments often don't catch up with audio (by the end of a long stream the video fragments have been 10x fewer than audio). Before this started happening, I never got that output from ytarchive.

Perform permissions check on startup

hoshinova should ensure that the temp and output directories are writeable, and exit with an error if they're not. It's better to crash early than later when the recordings are needed.

Scheduled streams show as "Idle" instead of "Waiting"

Here's my ytarchive command and output:

D:\Downloads\youtube-dl\record>ytarchive -c "D:\Downloads\youtube-dl\cookies.txt" -t -w -r 300 -o "E:\ytarchive\[%(upload_date)s] %(title)s (%(id)s)" --write-thumbnail --add-metadata --debug --merge --threads 2 https://www.youtube.com/channel/UCswvd6_YWmd6riRk-8oT-sA/live best
ytarchive 0.3.1-8d48052
2022/08/29 00:04:06 INFO: Loaded cookie file D:\Downloads\youtube-dl\cookies.txt
2022/08/29 00:04:06 net/http: invalid byte '"' in Cookie.Value; dropping invalid bytes

Waiting for stream, retrying every 300 seconds...

Suggestion: Ad-hoc add to download list from webpage

Sometimes there are videos that I would like to download that don't match any of my filters, but stopping the app, editing the config, and restarting feels like a very clunky process to record a one off video.

It would be great if there were a way to ad-hoc add videos to the queue via a text box on the webpage.

I made a quick mock up to try and illustrate this.

2022-08-10_18-21

possible uncaught error in rss parsing, stopping main thread until interrupt event is sent manually

Log from such a recent event I encountered below. Unfortunately I have no clue what the response body looked like but I suppose a general catch to dump the response into the log could do the trick to nail it down over some time. It's better for the parsing to verbosely fail, rather than to block the thread silently.

[2022-10-07T14:00:11Z DEBUG hoshinova::module::scraper] Fetching RSS for Vesper
[2022-10-07T14:00:11Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:11Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(211), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:11Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(211), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:11Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(211), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:11Z DEBUG h2::codec::framed_read] received frame=Ping { ack: false, payload: [0, 0, 0, 0, 0, 0, 0, 88] }
[2022-10-07T14:00:11Z DEBUG h2::codec::framed_write] send frame=Ping { ack: true, payload: [0, 0, 0, 0, 0, 0, 0, 88] }
[2022-10-07T14:00:11Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UCDRWSO281bIHYVi-OV3iFYA
[2022-10-07T14:00:41Z DEBUG hoshinova::module::scraper] Fetching RSS for Terumi
[2022-10-07T14:00:41Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:41Z DEBUG hoshinova::module::scraper] Fetching RSS for Yuuki
[2022-10-07T14:00:41Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:41Z DEBUG hoshinova::module::scraper] Fetching RSS for Pippa
[2022-10-07T14:00:41Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:41Z DEBUG hoshinova::module::scraper] Fetching RSS for Lisa
[2022-10-07T14:00:41Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(213), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(215), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(217), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(219), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(215), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(215), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Ping { ack: false, payload: [0, 0, 0, 0, 0, 0, 0, 90] }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Ping { ack: true, payload: [0, 0, 0, 0, 0, 0, 0, 90] }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(213), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(213), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:41Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UC0G2Y7_TXXJKL1OwTdTsWBg
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(217), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(217), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:41Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UCSmWTq8TP7zejNS6qbJLHUw
[2022-10-07T14:00:41Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UCJ46YTYBQVXsfsp8-HryoUA
[2022-10-07T14:00:41Z DEBUG hoshinova::module::scraper] Fetching RSS for Vesper
[2022-10-07T14:00:41Z DEBUG hyper::client::pool] reuse idle connection for ("https", www.youtube.com)
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(219), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(219), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Headers { stream_id: StreamId(221), flags: (0x5: END_HEADERS | END_STREAM) }
[2022-10-07T14:00:41Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UCdureux7B_V-xhM7tYImASQ
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Headers { stream_id: StreamId(221), flags: (0x4: END_HEADERS) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Data { stream_id: StreamId(221), flags: (0x1: END_STREAM) }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_read] received frame=Ping { ack: false, payload: [0, 0, 0, 0, 0, 0, 0, 92] }
[2022-10-07T14:00:41Z DEBUG h2::codec::framed_write] send frame=Ping { ack: true, payload: [0, 0, 0, 0, 0, 0, 0, 92] }
[2022-10-07T14:00:41Z DEBUG reqwest::async_impl::client] response '200 OK' for https://www.youtube.com/feeds/videos.xml?channel_id=UCDRWSO281bIHYVi-OV3iFYA
[2022-10-07T14:01:11Z DEBUG hoshinova::module::scraper] Fetching RSS for Terumi

Sent interrupt here

[2022-10-08T02:10:59Z DEBUG h2::codec::framed_read] received frame=GoAway { error_code: NO_ERROR, last_stream_id: StreamId(2147483647), debug_data: b"session_timed_out" }
[2022-10-08T02:10:59Z INFO  hoshinova::module::recorder] [vNodJC74mQU][Terumi Koizumi Ch.][【CYBERPUNK】Did you guess it was going to be Cyberpunk?! Let's go do Flatline some Cyberpsychos!] Recording started
[2022-10-08T02:10:59Z INFO  hoshinova::module::recorder] [4aPlZMEsCUo][Lisa Ch. 近藤リサ 【Tsunderia】][Stellar Stellar (Midwest Emo ver.) / Covered by Chikafuji Lisa] Recording started
[2022-10-08T02:10:59Z INFO  hoshinova::module::recorder] [mxTtycZdRqg][Pipkin Pippa Ch.【Phase Connect】][Talking to Famous Vtubers. . . and myself] Recording started
[2022-10-08T02:10:59Z DEBUG hoshinova::module::recorder] [4aPlZMEsCUo][Lisa Ch. 近藤リサ 【Tsunderia】][Stellar Stellar (Midwest Emo ver.) / Covered by Chikafuji Lisa] Process exited with Ok(ExitStatus(unix_wait_status(0)))
[2022-10-08T02:11:09Z DEBUG hoshinova::module::recorder] [vNodJC74mQU][Terumi Koizumi Ch.][【CYBERPUNK】Did you guess it was going to be Cyberpunk?! Let's go do Flatline some Cyberpsychos!] Process exited with Ok(ExitStatus(unix_wait_status(0)))
[2022-10-09T09:23:57Z DEBUG reqwest::connect] starting new connection: https://www.youtube.com/
[2022-10-09T09:23:57Z DEBUG h2::codec::framed_read] received frame=Ping { ack: false, payload: [0, 0, 0, 0, 0, 0, 0, 94] }
[2022-10-09T09:23:57Z DEBUG reqwest::connect] starting new connection: https://discord.com/
[2022-10-09T09:23:57Z DEBUG hoshinova::module::scraper] Fetching RSS for Yuuki
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::dns] resolving host="www.youtube.com"
[2022-10-09T09:23:57Z DEBUG h2::codec::framed_write] send frame=Ping { ack: true, payload: [0, 0, 0, 0, 0, 0, 0, 94] }
[2022-10-09T09:23:57Z DEBUG h2::codec::framed_read] received frame=GoAway { error_code: NO_ERROR, last_stream_id: StreamId(221), debug_data: b"session_timed_out" }
[2022-10-09T09:23:57Z DEBUG reqwest::connect] starting new connection: https://www.youtube.com/
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::dns] resolving host="discord.com"
[2022-10-09T09:23:57Z DEBUG h2::proto::connection] Connection::poll; IO error error=UnexpectedEof
[2022-10-09T09:23:57Z DEBUG hoshinova::module::scraper] Fetching RSS for Pippa
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::dns] resolving host="www.youtube.com"
[2022-10-09T09:23:57Z DEBUG hyper::proto::h2::client] connection error: unexpected end of file
[2022-10-09T09:23:57Z DEBUG reqwest::connect] starting new connection: https://www.youtube.com/
[2022-10-09T09:23:57Z DEBUG hoshinova::module::scraper] Fetching RSS for Lisa
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::dns] resolving host="www.youtube.com"
[2022-10-09T09:23:57Z DEBUG reqwest::connect] starting new connection: https://www.youtube.com/
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::dns] resolving host="www.youtube.com"
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connecting to 142.251.36.174:443
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connecting to 172.217.16.174:443
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connecting to 142.251.37.14:443
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connecting to 142.251.36.174:443
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connected to 142.251.36.174:443
[2022-10-09T09:23:57Z DEBUG rustls::client::hs] Resuming session
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connected to 172.217.16.174:443
[2022-10-09T09:23:57Z DEBUG rustls::client::hs] Resuming session
[2022-10-09T09:23:57Z DEBUG hyper::client::connect::http] connected to 142.251.37.14:443

Uncaught yta outputs

There are some ytarchive outputs that are currently unhandled by hoshinova. First one are network issues with the following output:

[2022-11-06T13:47:17Z WARN hoshinova::module::recorder] Unknown ytarchive output: 2022/11/06 13:47:17 ERROR: Error retrieving player response: unable to retrieve player response object from watch page

Can be fixed by readding the vod but you currently wont know if the issue occured in the first place.

Second issue is file name length errors. Ytarchive on windows can handle them and truncates the title accordingly but hoshinova just seems to break entirely at that point.

[2022-11-09T12:10:49Z WARN hoshinova::module::recorder] Unknown ytarchive output: 2022/11/09 12:10:49 WARNING: Formatted filename is too long. Truncating the title to try and fix.

Bundle the example configuration into the binary

Ideally, users who downloaded the binary releases should not have to refer to GitHub to grab an example configuration file. The example config should be embedded with the include_str! macro, and be accessible with a command-line flag, something like this:

./hoshinova --example-config > config.toml

Detect when ytarchive crashes

Right now hoshinova doesn't check if ytarchive is still running or has crashed, and can cause videos to get stuck.

Improve documentation on Docker

There are some things to note when managing deployments using Docker, that isn't well-documented right now:

  • The service runs as UID 1000, so bind-mount permissions need to be set correctly: chown -R 1000:1000 ./videos ./temp
  • When using docker-compose, restarting the service will not update it. To update, use docker-compose pull and then docker-compose up -d

Also move thumbnail when ytarchiver downloads it

When you supply the --write-thumbnail parameter to ytarchiver, the downloaded thumbnail isn't moved along with the video file. It stays inside the temp folder.

Example config:

[ytarchive]
executable_path = "ytarchive"
working_directory = "temp"
args = [
  "--vp9", "--thumbnail", "--add-metadata", "--threads", "4",
  "--retry-stream", "30",
  "--no-frag-files", "--thumbnail", "--write-thumbnail",
  "--output", "%(upload_date)s %(title)s [%(channel)s] (%(id)s)"
]
quality = "best"
# Delay between starting ytarchive processes. Increase this number if you get
# rate limited by YouTube.
delay_start = "1s"

Add description keyword filtering

Would be great to add keyword filtering for the description. This would help with collabs and unarchived streams that are not mentioned in the title all the time, but have a note in the description that would not be picked up by title filtering.

Thanks

WebServer can't be disabled

Disabling(Comment) or erasing webserver settings in config file cause program crash

Config file

#[webserver]
#bind_address = "0.0.0.0:25566"

After run program output:

[2022-08-28T11:03:00Z INFO  hoshinova] hoshinova v0.2.1
[2022-08-28T11:03:01Z ERROR hoshinova::msgbus] Failed to send message: channel closed 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.