auto-detect listings, player skip
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
.env
|
||||
node_modules
|
||||
static_assets
|
||||
host_info.json
|
||||
favourites_info.json
|
||||
*.js
|
||||
package-lock.json
|
||||
build
|
||||
@@ -30,7 +30,9 @@ Make sure to set a master password! See `.env.example`. Enter in the master pass
|
||||
# Tips
|
||||
|
||||
## Adding media
|
||||
Add it to the relevant static directory (`/static_assets/anime_assets`, `/static_assets/manga_assets`, or `/static_assets/music_assets`), and create an entry for it in `host_info.json`. See `host_info.json.example` for an example.
|
||||
Add it to the relevant static directory (`/static_assets/anime_assets`, `/static_assets/manga_assets`, or `/static_assets/music_assets`), and just rerun `npm run build`.
|
||||
|
||||
To mark it as a favourite, create an entry for it in `favourites_info.json`. See `favourites_info.json.example` for an example.
|
||||
|
||||
## Hosting Multiple TOR Hidden Services
|
||||
If you are running multiple TOR hidden services, you will need to modify the [.torrc file](https://stackoverflow.com/questions/14321214/how-to-run-multiple-tor-processes-at-once-with-different-exit-ips#18895491).
|
||||
|
||||
48
build.ts
48
build.ts
@@ -2,17 +2,20 @@ import * as path from 'path';
|
||||
import { readdirSync } from 'fs';
|
||||
import { Renderer } from './ryuji.js';
|
||||
import { Builder } from './saki.js';
|
||||
import _host_info from './host_info.json';
|
||||
import _favourites_info from './favourites_info.json';
|
||||
|
||||
//todo: music?
|
||||
|
||||
interface Listing {
|
||||
name: string;
|
||||
type: "anime" | "manga" | "music";
|
||||
interface FavouritesInfo {
|
||||
favourites: {
|
||||
listing: boolean; //whether to mark entire listing as favourite
|
||||
chapters: string[]; //favourite chapters
|
||||
}; //marked as not optional here, but in the actual host_info.json file, it is optional
|
||||
};
|
||||
}
|
||||
|
||||
interface Listing extends FavouritesInfo {
|
||||
name: string;
|
||||
type: "anime" | "manga" | "music";
|
||||
}
|
||||
|
||||
interface DirectoryVars {
|
||||
@@ -37,20 +40,27 @@ interface MangaVars {
|
||||
prev_chapter?: string | boolean;
|
||||
}
|
||||
|
||||
const listings: Listing[] = _host_info.listings.map(
|
||||
(listing: any) =>
|
||||
//add empty "favourites" if not present in the json
|
||||
listing.favourites ? listing : {
|
||||
...listing,
|
||||
favourites: {
|
||||
listing: false,
|
||||
chapters: []
|
||||
}
|
||||
}
|
||||
).filter(
|
||||
(listing: any): listing is Listing =>
|
||||
typeof listing.name === "string" && (listing?.type === "anime" || listing?.type === "manga" || listing?.type === "music")
|
||||
);
|
||||
const favourites_info: Record<string, FavouritesInfo> = _favourites_info;
|
||||
|
||||
let listings: Listing[] = [];
|
||||
|
||||
//add listings from static_assets/anime_assets
|
||||
for (let listing_type of ["anime", "manga", "music"]) {
|
||||
listings.push(...readdirSync(path.join(__dirname, `/static_assets/${listing_type}_assets`)).map(
|
||||
(listing_name: string) =>
|
||||
({
|
||||
name: listing_name,
|
||||
type: listing_type,
|
||||
favourites: {
|
||||
listing: favourites_info[listing_name] ? favourites_info[listing_name].favourites.listing : false,
|
||||
chapters: favourites_info[listing_name] ? favourites_info[listing_name].favourites.chapters : [],
|
||||
}
|
||||
})
|
||||
).filter(
|
||||
(listing: any): listing is Listing =>
|
||||
typeof listing.name === "string" && (listing?.type === "anime" || listing?.type === "manga" || listing?.type === "music")
|
||||
));
|
||||
}
|
||||
|
||||
let renderer: Renderer = new Renderer("templates", "components");
|
||||
let builder: Builder = new Builder("/build");
|
||||
|
||||
31
favourites_info.json.example
Normal file
31
favourites_info.json.example
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"senpai-wa-otokonoko": {
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": [
|
||||
"c001",
|
||||
"c099"
|
||||
]
|
||||
}
|
||||
}
|
||||
"bocchi-the-boulder": {
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": []
|
||||
}
|
||||
},
|
||||
"not-buna": {
|
||||
"favourites": {
|
||||
"listing": false,
|
||||
"chapters": [
|
||||
"aira"
|
||||
]
|
||||
}
|
||||
},
|
||||
"arizona-philips": {
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"listings": [
|
||||
{
|
||||
"name": "senpai-wa-otokonoko",
|
||||
"type": "manga",
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": ["c001"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "shiitake-simulation",
|
||||
"type": "manga"
|
||||
},
|
||||
{
|
||||
"name": "bocchi-the-boulder",
|
||||
"type": "anime",
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "yoroshiku",
|
||||
"type": "music"
|
||||
},
|
||||
{
|
||||
"name": "not-buna",
|
||||
"type": "music",
|
||||
"favourites": {
|
||||
"listing": false,
|
||||
"chapters": ["aira"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arizona-philips",
|
||||
"type": "music",
|
||||
"favourites": {
|
||||
"listing": true,
|
||||
"chapters": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
8
ryuji.ts
8
ryuji.ts
@@ -89,10 +89,10 @@ export class Renderer {
|
||||
if (recursion_layer > 5) throw Error("Components more than 5 layers deep, components may be referencing each other in infinite loop.");
|
||||
if (typeof exp_parts[1] !== "string") throw Error("`component:` statement missing component file name afterwards");
|
||||
let file_name: string = exp_parts[1];
|
||||
if (!file_name.includes(".")) {
|
||||
file_name += this.file_extension;
|
||||
}
|
||||
rendered += this.render_template(Renderer.concat_path(this.components_dir, file_name), vars, recursion_layer+1);
|
||||
if (!file_name.includes(".")) {
|
||||
file_name += this.file_extension;
|
||||
}
|
||||
rendered += this.render_template(Renderer.concat_path(this.components_dir, file_name), vars, recursion_layer+1);
|
||||
} else if (exp_parts[0] === "for") {
|
||||
if (for_loops[for_loops.length-1]?.index === index) {
|
||||
//for loop already exists, just continue and do nothing
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -34,6 +33,8 @@
|
||||
<br>
|
||||
<audio id="audio" type="audio/mpeg" controls>
|
||||
</audio>
|
||||
<br>
|
||||
<input type="button" value="Skip Song" onclick="next_song()"/>
|
||||
</div>
|
||||
<!--I would use <details> here, but maybe too abusive-->
|
||||
<div>
|
||||
@@ -74,7 +75,7 @@
|
||||
played.push(played);
|
||||
audio_ele.src = `/music_assets/${random_song_start}.mp3`;
|
||||
//"ended" event
|
||||
audio_ele.addEventListener("ended", () => {
|
||||
function next_song() {
|
||||
const not_played = playable_songs.filter((song) => !played.includes(song));
|
||||
if (not_played.length === 0) return;
|
||||
let random_song_next = not_played[Math.floor(Math.random() * not_played.length)];
|
||||
@@ -82,7 +83,8 @@
|
||||
played.push(random_song_next);
|
||||
audio_ele.src = `/music_assets/${random_song_next}.mp3`;
|
||||
audio_ele.play();
|
||||
});
|
||||
}
|
||||
audio_ele.addEventListener("ended", next_song);
|
||||
//show filters toggle
|
||||
const show_filters = document.getElementById("show-filters");
|
||||
function filter_check() {
|
||||
|
||||
Reference in New Issue
Block a user