Compare commits

...

6 Commits

Author SHA1 Message Date
Brock H Caldwell
c4b3fb215c feat: status indicators in header for tmdb & torrentio
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after -1m38s
2026-03-07 11:11:33 -06:00
Brock H Caldwell
2fc6d792bc chore: renames form field
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after -1m33s
2026-03-03 22:07:06 -06:00
Brock H Caldwell
c9f1a2d93a chore: updates readme
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after 13s
2026-03-03 22:04:22 -06:00
Brock H Caldwell
bbdd11d1b5 fix: rewords form question
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after -1m32s
2026-03-03 21:59:14 -06:00
Brock H Caldwell
1827908936 chore: updates readme
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after -1m32s
2026-03-03 20:16:49 -06:00
Brock H Caldwell
8b99a744e2 chore: updates readme
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after -1m32s
2026-03-03 20:12:00 -06:00
7 changed files with 88 additions and 5 deletions

View File

@@ -20,6 +20,9 @@ comparison to Stremio? That's because Torsearch uses the same source for media f
## Getting Started
For all pieces to work, you will need to serve the application over HTTPS. Running behind an SSL terminating
reverse proxy is the recommended approach.
1. Create a `compose.yml` file
```yaml
services:
@@ -116,7 +119,7 @@ TZ=America/Chicago
###################
# Symfony #
###################
# The external URL of the application where it can be reached by a browser.
# The external URL of the application where it can be reached by a browser. Should start with https://
APP_URL="<enter url>"
# Requried by Symfony Framework. Feel free to change.
APP_SECRET="70169beadfbc8101c393cbfbba27a313"
@@ -154,7 +157,7 @@ DATABASE_URL="mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:3306/${MYSQL
###################
# Real Debrid #
###################
# Enter your Real Debrid API key is passed to Torrentio to retrieve download options
# Enter your Real Debrid API key which is passed to Torrentio to retrieve download options
REAL_DEBRID_KEY="<enter real debrid api key>"
@@ -185,10 +188,29 @@ AUTH_METHOD=form_login
```
3. Run `docker compose up -d`
3. Enter the `APP_URL` in the .env file
4. Enter the `REAL_DEBRID_KEY` in the .env file
5. Enter a new `WEB_PORT` if the default doesn't work for you
4. Run `docker compose up -d`
4. Visit the app in the browser
5. Create your first user
5. Create a user
6. Visit the Preferences page to set your filter. This filter is used whenever you don't choose a specific file to
download (e.g. downloading via Monitor or clicking the "Download Season", "Download Selected", or "Download Episode" buttons).
7. Start downloading media!
## Having issues?
Submit an issue in the repo, and I'll try to address it as soon as possible. I do have a full-time job and family, so my
time is limited, but I'll do my best!
## Notes
This is my first contribution to open-source, the community that's given me so much over the years!
This project has been my personal hobby project for the last 1.5 years. I've written and re-written it several times.
It's been my testing ground for trying new things, so if the code looks like shit, my bad. I'm a PHP developer by day and
tinkerer by night - this was my first go with Symfony/Twig components, tailwind, the Symfony RICH bundle, and a lot more.
At some point, I'll put together a contribution guide, so others can hack on it too.
No AI was used for development (only to generate a list of countries with their flag emojis). If the code is bad, it's my fault.
# Disclaimer
Torsearch does not host any media; it only combines API results from multiple sources to make browsing them easier.
Torsearch is not affiliated with Real Debrid, Torrentio, or TMDB.

2
assets/bootstrap.js vendored
View File

@@ -5,6 +5,7 @@ import DownloadOptionTr from './components/download-option-tr.js';
import DownloadListRow from './components/download-list-row.js';
import MonitorListRow from './components/monitor-list-row.js';
import MovieContainer from "./components/movie-container.js";
import StatusCheckerSpan from "./components/status-checker-span.js";
import { startStimulusApp } from '@symfony/stimulus-bundle';
import Popover from '@stimulus-components/popover';
@@ -24,3 +25,4 @@ customElements.define('movie-container', MovieContainer);
customElements.define('dl-tr', DownloadOptionTr, {extends: 'tr'});
customElements.define('download-list-row', DownloadListRow, {extends: 'tr'});
customElements.define('monitor-list-row', MonitorListRow, {extends: 'tr'});
customElements.define('status-checker-span', StatusCheckerSpan, {extends: 'span'});

View File

@@ -0,0 +1,40 @@
export default class PreviewContentDialog extends HTMLSpanElement {
#url;
#service;
#status;
#statusTexts = {
200: 'up',
204: 'up'
}
#statusColors = {
200: 'bg-green-500',
204: 'bg-green-500'
}
constructor() {
super();
this.#url = this.getAttribute('url');
this.#service = this.getAttribute('service');
(async () => await this.checkStatus())();
setInterval(async () => await this.checkStatus(), 1000 * 60 * 10);
}
async checkStatus() {
const response = await fetch(this.#url);
this.#status = response.status;
this.setAttribute('title', this.getTitle());
this.classList.remove('bg-red-500', 'bg-green-500');
this.classList.add(this.getColor());
}
getTitle() {
return `${this.#service} is ${this.#statusTexts[this.#status] ?? 'down'}`
}
getColor() {
return this.#statusColors[this.#status] ?? 'bg-red-500';
}
}

View File

@@ -20,6 +20,7 @@ class RegistrationFormType extends AbstractType
->add('plainPassword', PasswordType::class, [
// instead of being set onto the object directly,
// this is read and encoded in the controller
'label' => 'Password',
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [

View File

@@ -18,9 +18,13 @@ module.exports = {
"bg-green-400",
"bg-purple-400",
"bg-orange-400",
"bg-red-500",
"bg-green-500",
"bg-blue-600",
"bg-rose-600",
"bg-black/20",
"text-red-500",
"text-green-500",
"alert-success",
"alert-warning",
"font-bold",

View File

@@ -6,6 +6,20 @@
<div class="md:flex md:items-center md:gap-12">
<nav aria-label="Global" class="md:block">
<ul class="ml-4 flex items-end md:items-center md:gap-6 text-sm">
<li>
<div class="flex flex-row justify-center items-start gap-2 p-2 w-10 mt-1 h-6 rounded-lg border border-orange-500 text-orange-500">
<status-checker-span
class="h-2 w-2 rounded-full text-green-600 bg-green-600"
url="https://torrentio.strem.fun"
service="Torrentio">
</status-checker-span>
<status-checker-span
class="h-2 w-2 rounded-full text-green-600 bg-green-600"
url="https://api.themoviedb.org/3"
service="TMDB">
</status-checker-span>
</div>
</li>
<li>
<a href="{{ path('app.monitor.upcoming-episodes') }}" data-turbo="false" title="View upcoming episodes of the shows you're subscribed to.">
<twig:ux:icon name="solar:calendar-linear" width="25px" class="text-orange-500" />

View File

@@ -30,7 +30,7 @@
<div class="flex flex-row gap-2 mb-2">
<input type="hidden" name="movie_folder" id="movie_folder_hidden" value="0" />
<input type="checkbox" name="movie_folder" id="movie_folder" value="1" {{ downloadPreferences['movie_folder'].getPreferenceValue() == true ? 'checked' }} />
<label class="text-gray-50" for="movie_folder">Store movies in a new directory?</label>
<label class="text-gray-50" for="movie_folder">Create a new directory for each movie?</label>
</div>
<button class="px-1.5 py-1 max-w-20 rounded-md bg-green-600 text-white" type="submit">Submit</button>
</form>