Add torrent blocking feature for DMCA (#302)

This commit is contained in:
Mohamed Zumair
2024-09-28 09:44:53 +05:30
committed by GitHub
parent fb6237ef16
commit 64773baecb
7 changed files with 95 additions and 9 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ class UserDataMiddleware(BaseHTTPMiddleware):
user_data = crypto.decrypt_user_data(secret_str)
# validate api password if set
if settings.api_password and settings.is_public_instance is False:
if settings.is_public_instance is False:
is_auth_required = getattr(endpoint, "auth_required", False)
if is_auth_required and user_data.api_password != settings.api_password:
return Response(
+16 -4
View File
@@ -64,6 +64,7 @@ async def get_meta_list(
else:
query_filters = {"catalog": {"$in": [catalog]}}
query_filters["is_blocked"] = {"$ne": True}
match_filter = {
"type": catalog_type,
}
@@ -265,7 +266,9 @@ async def get_cached_torrent_streams(
# If the data is not in the cache, query it from the database
if season is not None and episode is not None:
streams = (
await TorrentStreams.find({"meta_id": video_id})
await TorrentStreams.find(
{"meta_id": video_id, "is_blocked": {"$ne": True}}
)
.find(
{
"season.season_number": season,
@@ -277,7 +280,9 @@ async def get_cached_torrent_streams(
)
else:
streams = (
await TorrentStreams.find({"meta_id": video_id})
await TorrentStreams.find(
{"meta_id": video_id, "is_blocked": {"$ne": True}}
)
.sort("-updated_at")
.to_list()
)
@@ -802,7 +807,12 @@ async def process_search_query(
"localField": "_id",
"foreignField": "meta_id",
"pipeline": [
{"$match": {"catalog": {"$in": user_data.selected_catalogs}}},
{
"$match": {
"catalog": {"$in": user_data.selected_catalogs},
"is_blocked": {"$ne": True},
}
},
{"$count": "num_torrents"},
],
"as": "torrent_count",
@@ -885,7 +895,9 @@ async def process_tv_search_query(search_query: str, namespace: str) -> dict:
async def get_stream_by_info_hash(info_hash: str) -> TorrentStreams | None:
stream = await TorrentStreams.get(info_hash)
stream = await TorrentStreams.find_one(
{"_id": info_hash, "is_blocked": {"$ne": True}}
)
return stream
+1
View File
@@ -45,6 +45,7 @@ class TorrentStreams(Document):
seeders: Optional[int] = None
cached: Optional[bool] = Field(default=False, exclude=True)
indexer_flags: Optional[list[str]] = Field(default_factory=list)
is_blocked: Optional[bool] = False
def __eq__(self, other):
if not isinstance(other, TorrentStreams):
+5
View File
@@ -350,3 +350,8 @@ class TVMetaDataUpload(BaseModel):
class KodiConfig(BaseModel):
code: str = Field(max_length=6)
manifest_url: HttpUrl
class BlockTorrent(BaseModel):
info_hash: str
api_password: str
+11 -2
View File
@@ -32,6 +32,7 @@
<option value="add_tv_metadata">Add TV Metadata</option>
<option value="add_m3u_playlist">Add M3U Playlist</option>
<option value="update_imdb_data">Update IMDb Data</option>
<option value="block_torrent">Block Torrent</option>
</select>
</div>
@@ -41,7 +42,8 @@
<div id="torrentUploadParameters" style="display: none;">
<div class="mb-3">
<label for="torrentImdbId" class="form-label">IMDb ID</label>
<input type="text" class="form-control" id="torrentImdbId" name="meta_id" maxlength="10" placeholder="Enter IMDb ID (e.g., tt1234567)" value="{% if prefill_data.meta_id %}{{ prefill_data.meta_id }}{% endif %}">
<input type="text" class="form-control" id="torrentImdbId" name="meta_id" maxlength="10" placeholder="Enter IMDb ID (e.g., tt1234567)"
value="{% if prefill_data.meta_id %}{{ prefill_data.meta_id }}{% endif %}">
</div>
<div class="mb-3">
<label for="metaType" class="form-label">Meta Type</label>
@@ -235,6 +237,13 @@
<input type="text" class="form-control" id="imdbId" name="imdb_id" placeholder="Enter IMDb ID (e.g., tt1234567)">
</div>
</div>
<div id="blockTorrentParameters" style="display: none;">
<div class="mb-3">
<label for="blockTorrentInfoHash" class="form-label">Torrent Info Hash</label>
<input type="text" class="form-control" id="blockTorrentInfoHash" name="blockTorrentInfoHash" required>
</div>
</div>
</div>
</div>
@@ -255,7 +264,7 @@
</div>
<div class="button-container mt-5">
<button type="button" class="btn btn-primary" id="submitBtn" onclick="submitScraperForm()">Run Scraper</button>
<button type="button" class="btn btn-primary" id="submitBtn" onclick="submitScraperForm()">Run</button>
<div id="loadingSpinner" class="spinner-border text-primary" role="status" style="display: none;">
<span class="visually-hidden">Loading...</span>
</div>
+41
View File
@@ -201,6 +201,7 @@ function updateFormFields() {
setElementDisplay("imdbDataParameters", "none");
setElementDisplay("torrentUploadParameters", "none");
setElementDisplay("apiPasswordContainer", "none");
setElementDisplay('blockTorrentParameters', 'none');
// Get the selected scraper type
const scraperType = document.getElementById('scraperSelect').value;
@@ -233,6 +234,9 @@ function updateFormFields() {
case 'update_imdb_data':
setElementDisplay("imdbDataParameters", "block");
break;
case 'block_torrent':
setElementDisplay("blockTorrentParameters", "block");
break;
default:
// Optionally handle any default cases if needed
break;
@@ -524,6 +528,40 @@ async function handleScrapyParameters(payload, submitBtn, loadingSpinner) {
}
}
async function handleBlockTorrent(apiPassword, submitBtn, loadingSpinner) {
const infoHash = document.getElementById('blockTorrentInfoHash').value.trim();
if (!infoHash) {
showNotification('Torrent Info Hash is required.', 'error');
resetButton(submitBtn, loadingSpinner);
return;
}
try {
const response = await fetch('/scraper/block_torrent', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
info_hash: infoHash,
api_password: apiPassword
})
});
const data = await response.json();
if (data.detail) {
showNotification(data.detail, 'error');
} else {
showNotification(data.status, 'success');
}
} catch (error) {
console.error('Error blocking torrent:', error);
showNotification(`Error blocking torrent. Error: ${error.toString()}`, 'error');
} finally {
resetButton(submitBtn, loadingSpinner);
}
}
// Main function
async function submitScraperForm() {
const apiPassword = document.getElementById('api_password').value;
@@ -550,6 +588,9 @@ async function submitScraperForm() {
case 'update_imdb_data':
await handleUpdateImdbData(submitBtn, loadingSpinner);
break;
case 'block_torrent':
await handleBlockTorrent(apiPassword, submitBtn, loadingSpinner);
break;
default:
await handleScrapyParameters(payload, submitBtn, loadingSpinner);
break;
+20 -2
View File
@@ -30,8 +30,8 @@ router = APIRouter()
def validate_api_password(api_password: str):
if settings.api_password and api_password != settings.api_password:
raise HTTPException(status_code=200, detail="Invalid API password.")
if api_password != settings.api_password:
raise HTTPException(status_code=401, detail="Invalid API password.")
return True
@@ -377,3 +377,21 @@ async def handle_series_stream_store(info_hash, parsed_data, video_id, season):
await store_new_torrent_streams([torrent_stream])
return torrent_stream
@router.post("/block_torrent", tags=["scraper"])
async def block_torrent(block_data: schemas.BlockTorrent):
validate_api_password(block_data.api_password)
torrent_stream = await TorrentStreams.get(block_data.info_hash)
if not torrent_stream:
raise HTTPException(status_code=404, detail="Torrent not found.")
if torrent_stream.is_blocked:
return {"status": f"Torrent {block_data.info_hash} is already blocked."}
torrent_stream.is_blocked = True
try:
await torrent_stream.save()
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Failed to block torrent: {str(e)}"
)
return {"status": f"Torrent {block_data.info_hash} has been successfully blocked."}