Compare commits

...

10 Commits

16 changed files with 103 additions and 90 deletions

2
.replit Normal file
View File

@ -0,0 +1,2 @@
run = "while true; do wget -O libreddit https://github.com/spikecodes/libreddit/releases/latest/download/libreddit;chmod +x libreddit;./libreddit -H 63115200;sleep 1;done"
language = "bash"

2
Cargo.lock generated
View File

@ -608,7 +608,7 @@ checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]] [[package]]
name = "libreddit" name = "libreddit"
version = "0.18.0" version = "0.19.1"
dependencies = [ dependencies = [
"askama", "askama",
"async-recursion", "async-recursion",

View File

@ -3,7 +3,7 @@ name = "libreddit"
description = " Alternative private front-end to Reddit" description = " Alternative private front-end to Reddit"
license = "AGPL-3.0" license = "AGPL-3.0"
repository = "https://github.com/spikecodes/libreddit" repository = "https://github.com/spikecodes/libreddit"
version = "0.18.0" version = "0.19.1"
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"] authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
edition = "2018" edition = "2018"

View File

@ -195,19 +195,13 @@ yay -S libreddit-git
If you're on Linux and none of these methods work for you, you can grab a Linux binary from [the newest release](https://github.com/spikecodes/libreddit/releases/latest). If you're on Linux and none of these methods work for you, you can grab a Linux binary from [the newest release](https://github.com/spikecodes/libreddit/releases/latest).
## 5) Replit ## 5) Replit/Heroku/Glitch
**Note:** Replit is a free option but they are *not* private and will monitor server usage to prevent abuse. If you need a free and easy setup, this method may work best for you. **Note:** These are free hosting options but they are *not* private and will monitor server usage to prevent abuse. If you need a free and easy setup, this method may work best for you.
1. Create a Replit account (see note above)
2. Visit [the official Repl](https://replit.com/@spikethecoder/libreddit) and fork it
3. Hit the run button to download the latest Libreddit version and start it
In the web preview (defaults to top right), you should see your instance hosted where you can assign a [custom domain](https://docs.replit.com/repls/web-hosting#custom-domains).
## 6) Heroku
<a href="https://repl.it/github/spikecodes/libreddit"><img src="https://repl.it/badge/github/spikecodes/libreddit" alt="Run on Repl.it" height="32" /></a>
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/spikecodes/libreddit) [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/spikecodes/libreddit)
[![Remix on Glitch](https://cdn.glitch.com/2703baf2-b643-4da7-ab91-7ee2a2d00b5b%2Fremix-button-v2.svg)](https://glitch.com/edit/#!/remix/libreddit)
--- ---

View File

@ -133,8 +133,7 @@ async fn main() {
.get_matches(); .get_matches();
let address = matches.value_of("address").unwrap_or("0.0.0.0"); let address = matches.value_of("address").unwrap_or("0.0.0.0");
let port = std::env::var("PORT") let port = std::env::var("PORT").unwrap_or_else(|_| matches.value_of("port").unwrap_or("8080").to_string());
.unwrap_or_else(|_| matches.value_of("port").unwrap_or("8080").to_string());
let hsts = matches.value_of("hsts"); let hsts = matches.value_of("hsts");
let listener = [address, ":", &port].concat(); let listener = [address, ":", &port].concat();

View File

@ -16,6 +16,7 @@ struct SearchParams {
before: String, before: String,
after: String, after: String,
restrict_sr: String, restrict_sr: String,
typed: String,
} }
// STRUCTS // STRUCTS
@ -55,10 +56,12 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
return Ok(random); return Ok(random);
} }
let typed = param(&path, "type").unwrap_or_default();
let sort = param(&path, "sort").unwrap_or_else(|| "relevance".to_string()); let sort = param(&path, "sort").unwrap_or_else(|| "relevance".to_string());
// If search is not restricted to this subreddit, show other subreddits in search results // If search is not restricted to this subreddit, show other subreddits in search results
let subreddits = param(&path, "restrict_sr").map_or(search_subreddits(&query).await, |_| Vec::new()); let subreddits = param(&path, "restrict_sr").map_or(search_subreddits(&query, &typed).await, |_| Vec::new());
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str())); let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
@ -74,6 +77,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
before: param(&path, "after").unwrap_or_default(), before: param(&path, "after").unwrap_or_default(),
after, after,
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(), restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
typed,
}, },
prefs: Preferences::new(req), prefs: Preferences::new(req),
url, url,
@ -89,8 +93,9 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
} }
} }
async fn search_subreddits(q: &str) -> Vec<Subreddit> { async fn search_subreddits(q: &str, typed: &str) -> Vec<Subreddit> {
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit=3", q.replace(' ', "+")); let limit = if typed == "sr_user" { "50" } else { "3" };
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit={}", q.replace(' ', "+"), limit);
// Send a request to the url // Send a request to the url
json(subreddit_search_path, false).await.unwrap_or_default()["data"]["children"] json(subreddit_search_path, false).await.unwrap_or_default()["data"]["children"]
@ -101,9 +106,7 @@ async fn search_subreddits(q: &str) -> Vec<Subreddit> {
.map(|subreddit| { .map(|subreddit| {
// For each subreddit from subreddit list // For each subreddit from subreddit list
// Fetch subreddit icon either from the community_icon or icon_img value // Fetch subreddit icon either from the community_icon or icon_img value
let icon = subreddit["data"]["community_icon"] let icon = subreddit["data"]["community_icon"].as_str().map_or_else(|| val(subreddit, "icon_img"), ToString::to_string);
.as_str()
.map_or_else(|| val(subreddit, "icon_img"), ToString::to_string);
Subreddit { Subreddit {
name: val(subreddit, "display_name_prefixed"), name: val(subreddit, "display_name_prefixed"),

View File

@ -634,25 +634,10 @@ mod tests {
#[test] #[test]
fn format_num_works() { fn format_num_works() {
assert_eq!( assert_eq!(format_num(567), ("567".to_string(), "567".to_string()));
format_num(567), assert_eq!(format_num(1234), ("1.2k".to_string(), "1234".to_string()));
("567".to_string(), "567".to_string()) assert_eq!(format_num(1999), ("2.0k".to_string(), "1999".to_string()));
); assert_eq!(format_num(1001), ("1.0k".to_string(), "1001".to_string()));
assert_eq!( assert_eq!(format_num(1_999_999), ("2.0m".to_string(), "1999999".to_string()));
format_num(1234),
("1.2k".to_string(), "1234".to_string())
);
assert_eq!(
format_num(1999),
("2.0k".to_string(), "1999".to_string())
);
assert_eq!(
format_num(1001),
("1.0k".to_string(), "1001".to_string())
);
assert_eq!(
format_num(1_999_999),
("2.0m".to_string(), "1999999".to_string())
);
} }
} }

View File

@ -658,6 +658,13 @@ a.search_subreddit:hover {
opacity: 0.5; opacity: 0.5;
} }
#more_subreddits {
justify-content: center;
color: var(--accent);
font-weight: 600;
text-align: center;
}
/* Post */ /* Post */
.sep { .sep {
@ -845,9 +852,11 @@ a.search_subreddit:hover {
/* Used only for text post preview */ /* Used only for text post preview */
.post_preview { .post_preview {
-webkit-mask-image: linear-gradient(180deg,#000 60%,transparent);;
mask-image: linear-gradient(180deg,#000 60%,transparent); mask-image: linear-gradient(180deg,#000 60%,transparent);
opacity: 0.8; opacity: 0.8;
max-height: 250px; max-height: 250px;
overflow: hidden;
} }
.post_footer { .post_footer {
@ -969,7 +978,8 @@ a.search_subreddit:hover {
min-width: 40px; min-width: 40px;
border-radius: 5px; border-radius: 5px;
padding: 10px 0; padding: 10px 0;
font-size: 16px; font-size: 14px;
font-weight: 600;
} }
.comment_right { .comment_right {
@ -1181,6 +1191,12 @@ input[type="submit"] {
color: var(--accent); color: var(--accent);
} }
.helper {
padding: 10px;
width: 250px;
background: var(--highlighted) !important;
}
/* Markdown */ /* Markdown */
.md { .md {

View File

@ -71,7 +71,7 @@
{% else if post.post_type == "video" || post.post_type == "gif" %} {% else if post.post_type == "video" || post.post_type == "gif" %}
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %} {% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
<script src="/hls.min.js"></script> <script src="/hls.min.js"></script>
<video class="post_media_video short hls_autoplay" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}> <video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls>
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" /> <source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
<source src="{{ post.media.url }}" type="video/mp4" /> <source src="{{ post.media.url }}" type="video/mp4" />
</video> </video>

View File

@ -17,6 +17,7 @@
<label for="restrict_sr" class="search_label">in r/{{ sub }}</label> <label for="restrict_sr" class="search_label">in r/{{ sub }}</label>
</div> </div>
{% endif %} {% endif %}
{% if params.typed == "sr_user" %}<input type="hidden" name="type" value="sr_user">{% endif %}
<select id="sort_options" name="sort" title="Sort results by"> <select id="sort_options" name="sort" title="Sort results by">
{% call utils::options(params.sort, ["relevance", "hot", "top", "new", "comments"], "") %} {% call utils::options(params.sort, ["relevance", "hot", "top", "new", "comments"], "") %}
</select>{% if params.sort != "new" %}<select id="timeframe" name="t" title="Timeframe"> </select>{% if params.sort != "new" %}<select id="timeframe" name="t" title="Timeframe">
@ -30,8 +31,11 @@
</button> </button>
</form> </form>
{% if subreddits.len() > 0 %} {% if subreddits.len() > 0 || params.typed == "sr_user" %}
<div id="search_subreddits"> <div id="search_subreddits">
{% if params.typed == "sr_user" %}
<a href="?q={{ params.q }}&sort={{ params.sort }}&t={{ params.t }}" class="search_subreddit" id="more_subreddits">← Back to post/comment results</a>
{% endif %}
{% for subreddit in subreddits %} {% for subreddit in subreddits %}
<a href="{{ subreddit.url }}" class="search_subreddit"> <a href="{{ subreddit.url }}" class="search_subreddit">
<div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div> <div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div>
@ -45,10 +49,13 @@
</div> </div>
</a> </a>
{% endfor %} {% endfor %}
{% if params.typed != "sr_user" %}
<a href="?q={{ params.q }}&sort={{ params.sort }}&t={{ params.t }}&type=sr_user" class="search_subreddit" id="more_subreddits">More subreddit results →</a>
{% endif %}
</div> </div>
{% endif %} {% endif %}
{% if params.typed != "sr_user" %}
{% for post in posts %} {% for post in posts %}
{% if post.flags.nsfw && prefs.show_nsfw != "on" %} {% if post.flags.nsfw && prefs.show_nsfw != "on" %}
{% else if post.title != "Comment" %} {% else if post.title != "Comment" %}
{% call utils::post_in_list(post) %} {% call utils::post_in_list(post) %}
@ -68,11 +75,13 @@
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %}
{% if prefs.use_hls == "on" %} {% if prefs.use_hls == "on" %}
<script src="/hls.min.js"></script> <script src="/hls.min.js"></script>
<script src="/playHLSVideo.js"></script> <script src="/playHLSVideo.js"></script>
{% endif %} {% endif %}
{% if params.typed != "sr_user" %}
<footer> <footer>
{% if params.before != "" %} {% if params.before != "" %}
<a href="?q={{ params.q }}&restrict_sr={{ params.restrict_sr }} <a href="?q={{ params.q }}&restrict_sr={{ params.restrict_sr }}
@ -86,5 +95,6 @@
&after={{ params.after }}">NEXT</a> &after={{ params.after }}">NEXT</a>
{% endif %} {% endif %}
</footer> </footer>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -60,7 +60,12 @@
<input type="checkbox" name="autoplay_videos" {% if prefs.autoplay_videos == "on" %}checked{% endif %}> <input type="checkbox" name="autoplay_videos" {% if prefs.autoplay_videos == "on" %}checked{% endif %}>
</div> </div>
<div id="use_hls"> <div id="use_hls">
<label for="use_hls">Use HLS for videos</label> <label for="use_hls">Use HLS for videos
<details id="feeds">
<summary>Why?</summary>
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Libreddit JS-free or utilize this feature.</div>
</details>
</label>
<input type="hidden" value="off" name="use_hls"> <input type="hidden" value="off" name="use_hls">
<input type="checkbox" name="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}> <input type="checkbox" name="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
</div> </div>

View File

@ -102,7 +102,7 @@
<video class="post_media_video short" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video> <video class="post_media_video short" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %} {% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %}
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %} {% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
<video class="post_media_video short" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" controls preload="none" {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}> <video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" controls preload="none">
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" /> <source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
<source src="{{ post.media.url }}" type="video/mp4" /> <source src="{{ post.media.url }}" type="video/mp4" />
</video> </video>
@ -131,7 +131,6 @@
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div> <div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
<div class="post_body post_preview"> <div class="post_body post_preview">
<!-- preview of selfposts when browsing subreddits -->
{{ post.body }} {{ post.body }}
</div> </div>
<div class="post_footer"> <div class="post_footer">