mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-01 17:10:10 +00:00
Site: add _deprecate
This commit is contained in:
parent
fab21e572c
commit
bfed63bd41
639
.github/.site/css/style.css
vendored
Normal file
639
.github/.site/css/style.css
vendored
Normal file
@ -0,0 +1,639 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary-color: #8c52ff;
|
||||
--secondary-color: #6930c3;
|
||||
--accent-color: #00e5ff;
|
||||
--background-color: #121212;
|
||||
--card-background: #1e1e1e;
|
||||
--text-color: #f8f9fa;
|
||||
--shadow-color: rgba(0, 0, 0, 0.25);
|
||||
--card-hover: #2a2a2a;
|
||||
--border-color: #333333;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--background-color: #ffffff;
|
||||
--card-background: #f8f9fa;
|
||||
--text-color: #212529;
|
||||
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||
--card-hover: #e9ecef;
|
||||
--border-color: #dee2e6;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Segoe UI', sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--header-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 12px var(--shadow-color);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
background: var(--card-background);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sites-stats {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.total-sites, .last-update-global {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-color);
|
||||
font-size: 0.95rem;
|
||||
background: var(--background-color);
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.total-sites:hover, .last-update-global:hover {
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.total-sites i, .last-update-global i {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.site-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.site-item {
|
||||
min-height: 220px;
|
||||
background-color: var(--card-background);
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 6px 20px var(--shadow-color);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid var(--border-color);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.site-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.site-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 30px var(--shadow-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.site-item:hover::before {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.site-item img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 16px;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--border-color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.site-item:hover img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.site-item h3 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--primary-color);
|
||||
text-align: center;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.site-item:hover h3 {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.site-content {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.domain {
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.site-item a {
|
||||
margin-top: 1rem;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
padding: 12px 28px;
|
||||
border-radius: 8px;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.site-item a:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.site-title {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
transition: opacity 0.3s ease;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.site-item:hover .site-title {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.site-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site-item:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.site-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.last-update, .old-domain {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.last-update i, .old-domain i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.site-item:hover .site-info {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--card-background);
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: auto;
|
||||
padding: 40px 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 30px;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
transition: transform 0.3s ease, background-color 0.3s ease;
|
||||
background-color: var(--card-background);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.footer-section:hover {
|
||||
transform: translateY(-5px);
|
||||
background-color: var(--card-hover);
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
position: relative;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.footer-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s ease;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
opacity: 1;
|
||||
color: var(--accent-color);
|
||||
transform: translateX(8px);
|
||||
background-color: rgba(140, 82, 255, 0.1);
|
||||
}
|
||||
|
||||
.footer-links i {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover i {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.github-stats {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.github-badge {
|
||||
background-color: var(--background-color);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.github-badge i {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
margin-top: 15px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.update-info {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 30px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.update-note {
|
||||
color: var(--accent-color);
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
position: relative;
|
||||
top: unset;
|
||||
right: unset;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.theme-toggle input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-toggle label {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
background: var(--background-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 10px var(--shadow-color);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-toggle label:hover {
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.theme-toggle .fa-sun {
|
||||
display: none;
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
.theme-toggle .fa-moon {
|
||||
color: #8c52ff;
|
||||
}
|
||||
|
||||
.theme-toggle input:checked ~ label .fa-sun {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.theme-toggle input:checked ~ label .fa-moon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid var(--primary-color);
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
.loader::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-bottom-color: var(--accent-color);
|
||||
animation: rotationBack 0.5s linear infinite;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% { transform: rotate(0deg) }
|
||||
100% { transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
@keyframes rotationBack {
|
||||
0% { transform: rotate(0deg) }
|
||||
100% { transform: rotate(-360deg) }
|
||||
}
|
||||
|
||||
/* Improved Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
.site-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.site-item {
|
||||
min-height: 250px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.sites-stats {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.total-sites, .last-update-global {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.site-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.site-item {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-title::after {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.time-change {
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--accent-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.controls-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px 20px;
|
||||
background: var(--card-background);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.grid-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.grid-controls label {
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.grid-controls select {
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
background: var(--background-color);
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.grid-controls select:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.sites-stats {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.total-sites, .last-update-global {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-color);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.total-sites i, .last-update-global i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.site-status {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #4CAF50;
|
||||
}
|
||||
|
||||
.site-status.offline {
|
||||
background: #f44336;
|
||||
}
|
33
Test/.site/index.html → .github/.site/index.html
vendored
33
Test/.site/index.html → .github/.site/index.html
vendored
@ -9,9 +9,19 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<section class="container">
|
||||
<body> <main>
|
||||
<section class="container"> <div class="header-container">
|
||||
<div class="sites-stats">
|
||||
<span class="total-sites">
|
||||
<i class="fas fa-globe"></i>
|
||||
Total Sites: <span id="sites-count">0</span>
|
||||
</span>
|
||||
<span class="last-update-global">
|
||||
<i class="fas fa-clock"></i>
|
||||
Last Update: <span id="last-update-time">-</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sites-container">
|
||||
<div id="site-list" class="site-grid">
|
||||
<div class="loader"></div>
|
||||
@ -21,8 +31,7 @@
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<div class="footer-content"> <div class="footer-section">
|
||||
<h3 class="footer-title">Repository</h3>
|
||||
<ul class="footer-links">
|
||||
<li>
|
||||
@ -33,22 +42,22 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p class="footer-description">
|
||||
An updated collection of streaming sites. Contribute to the project on GitHub!
|
||||
An open-source script for downloading movies, TV shows, and anime from various websites.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h3 class="footer-title">Author</h3>
|
||||
<h3 class="footer-title">Support</h3>
|
||||
<ul class="footer-links">
|
||||
<li>
|
||||
<a href="https://github.com/Arrowar" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
Arrowar Profile
|
||||
<a href="https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fab fa-paypal"></i>
|
||||
Donate with PayPal
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="footer-description">
|
||||
Developer of the project.
|
||||
Support the development of this project through donations.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -71,4 +80,4 @@
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
179
.github/.site/js/script.js
vendored
Normal file
179
.github/.site/js/script.js
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
|
||||
function initGridControls() {
|
||||
const gridSize = document.getElementById('grid-size');
|
||||
const siteGrid = document.querySelector('.site-grid');
|
||||
|
||||
gridSize.addEventListener('change', function() {
|
||||
switch(this.value) {
|
||||
case 'small':
|
||||
siteGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(200px, 1fr))';
|
||||
break;
|
||||
case 'medium':
|
||||
siteGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(300px, 1fr))';
|
||||
break;
|
||||
case 'large':
|
||||
siteGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(400px, 1fr))';
|
||||
break;
|
||||
}
|
||||
localStorage.setItem('preferredGridSize', this.value);
|
||||
});
|
||||
|
||||
const savedSize = localStorage.getItem('preferredGridSize');
|
||||
if (savedSize) {
|
||||
gridSize.value = savedSize;
|
||||
gridSize.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
async function checkSiteStatus(url) {
|
||||
try {
|
||||
console.log(`Checking status for: ${url}`);
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 3000);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Accept': 'text/html',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/133.0.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
const isOnline = response.type === 'opaque';
|
||||
console.log(`Site ${url} is ${isOnline ? 'online' : 'offline'} (Type: ${response.type})`);
|
||||
return isOnline;
|
||||
} catch (error) {
|
||||
console.log(`Error checking ${url}:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const supabaseUrl = 'https://zvfngpoxwrgswnzytadh.supabase.co';
|
||||
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inp2Zm5ncG94d3Jnc3duenl0YWRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDAxNTIxNjMsImV4cCI6MjA1NTcyODE2M30.FNTCCMwi0QaKjOu8gtZsT5yQttUW8QiDDGXmzkn89QE';
|
||||
|
||||
async function loadSiteData() {
|
||||
try {
|
||||
console.log('Starting to load site data...');
|
||||
const siteList = document.getElementById('site-list');
|
||||
siteList.innerHTML = '<div class="loader"></div>';
|
||||
|
||||
const headers = {
|
||||
'accept': '*/*',
|
||||
'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||
'apikey': supabaseKey,
|
||||
'authorization': `Bearer ${supabaseKey}`,
|
||||
'content-type': 'application/json',
|
||||
'cache-control': 'no-cache',
|
||||
'pragma': 'no-cache',
|
||||
'range': '0-9'
|
||||
};
|
||||
|
||||
console.log('Fetching from Supabase with headers:', headers);
|
||||
const response = await fetch(`${supabaseUrl}/rest/v1/public?select=*`, {
|
||||
method: 'GET',
|
||||
headers: headers
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
siteList.innerHTML = ''; if (data && data.length > 0) {
|
||||
console.log('Raw data from Supabase:', data);
|
||||
const configSite = data[0].data;
|
||||
console.log('Parsed config site:', configSite);
|
||||
let totalSites = Object.keys(configSite).length;
|
||||
let latestUpdate = new Date(0);
|
||||
|
||||
document.getElementById('sites-count').textContent = totalSites;
|
||||
|
||||
for (const siteName in configSite) {
|
||||
const site = configSite[siteName];
|
||||
const siteItem = document.createElement('div');
|
||||
siteItem.className = 'site-item';
|
||||
siteItem.style.cursor = 'pointer';
|
||||
|
||||
// Add status indicator
|
||||
const statusDot = document.createElement('div');
|
||||
statusDot.className = 'site-status';
|
||||
const isOnline = await checkSiteStatus(site.full_url);
|
||||
if (!isOnline) statusDot.classList.add('offline');
|
||||
siteItem.appendChild(statusDot);
|
||||
|
||||
// Update latest update time
|
||||
const updateTime = new Date(site.time_change);
|
||||
if (updateTime > latestUpdate) {
|
||||
latestUpdate = updateTime;
|
||||
}
|
||||
|
||||
const siteInfo = document.createElement('div');
|
||||
siteInfo.className = 'site-info';
|
||||
if (site.time_change) {
|
||||
const updateDate = new Date(site.time_change);
|
||||
const formattedDate = updateDate.toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
const lastUpdate = document.createElement('span');
|
||||
lastUpdate.className = 'last-update';
|
||||
lastUpdate.innerHTML = `<i class="fas fa-clock"></i> ${formattedDate}`;
|
||||
siteInfo.appendChild(lastUpdate);
|
||||
}
|
||||
|
||||
if (site.old_domain) {
|
||||
const oldDomain = document.createElement('span');
|
||||
oldDomain.className = 'old-domain';
|
||||
oldDomain.innerHTML = `<i class="fas fa-history"></i> ${site.old_domain}`;
|
||||
siteInfo.appendChild(oldDomain);
|
||||
} siteItem.addEventListener('click', function() {
|
||||
window.open(site.full_url, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
|
||||
const siteIcon = document.createElement('img');
|
||||
siteIcon.src = `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${site.full_url}&size=128`;
|
||||
siteIcon.alt = `${siteName} icon`;
|
||||
siteIcon.onerror = function() {
|
||||
this.src = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="%238c52ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
|
||||
};
|
||||
|
||||
const siteTitle = document.createElement('h3');
|
||||
siteTitle.textContent = siteName;
|
||||
siteItem.appendChild(siteIcon);
|
||||
siteItem.appendChild(siteTitle);
|
||||
siteItem.appendChild(siteInfo);
|
||||
siteList.appendChild(siteItem);
|
||||
}
|
||||
|
||||
const formattedDate = latestUpdate.toLocaleDateString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
document.getElementById('last-update-time').textContent = formattedDate;
|
||||
} else {
|
||||
siteList.innerHTML = '<div class="no-sites">No sites available</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Errore:', error);
|
||||
siteList.innerHTML = `
|
||||
<div class="error-message">
|
||||
<p>Errore nel caricamento</p>
|
||||
<button onclick="loadSiteData()" class="retry-button">Riprova</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadSiteData();
|
||||
});
|
12
.github/workflows/build-dev.yml
vendored
12
.github/workflows/build-dev.yml
vendored
@ -64,6 +64,14 @@ jobs:
|
||||
# Get certifi certificate path
|
||||
CERT_PATH=$(python -c "import certifi; print(certifi.where())")
|
||||
|
||||
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
|
||||
CERT_DATA="${CERT_PATH};certifi"
|
||||
SC_DATA="StreamingCommunity;StreamingCommunity"
|
||||
else
|
||||
CERT_DATA="${CERT_PATH}:certifi"
|
||||
SC_DATA="StreamingCommunity:StreamingCommunity"
|
||||
fi
|
||||
|
||||
pyinstaller --onefile --hidden-import=pycryptodomex --hidden-import=ua_generator \
|
||||
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
||||
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
||||
@ -82,8 +90,8 @@ jobs:
|
||||
--hidden-import=Cryptodome.Random \
|
||||
--hidden-import=telebot \
|
||||
--additional-hooks-dir=pyinstaller/hooks \
|
||||
--add-data "${CERT_PATH};certifi" \
|
||||
--add-data "StreamingCommunity${{ matrix.separator }}StreamingCommunity" \
|
||||
--add-data="$CERT_DATA" \
|
||||
--add-data="$SC_DATA" \
|
||||
--name=${{ matrix.artifact_name }} test_run.py
|
||||
|
||||
- name: Upload executable to latest release
|
||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -108,6 +108,14 @@ jobs:
|
||||
# Get certifi certificate path
|
||||
CERT_PATH=$(python -c "import certifi; print(certifi.where())")
|
||||
|
||||
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
|
||||
CERT_DATA="${CERT_PATH};certifi"
|
||||
SC_DATA="StreamingCommunity;StreamingCommunity"
|
||||
else
|
||||
CERT_DATA="${CERT_PATH}:certifi"
|
||||
SC_DATA="StreamingCommunity:StreamingCommunity"
|
||||
fi
|
||||
|
||||
pyinstaller --onefile --hidden-import=pycryptodomex --hidden-import=ua_generator \
|
||||
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
||||
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
||||
@ -126,8 +134,8 @@ jobs:
|
||||
--hidden-import=Cryptodome.Random \
|
||||
--hidden-import=telebot \
|
||||
--additional-hooks-dir=pyinstaller/hooks \
|
||||
--add-data "${CERT_PATH};certifi" \
|
||||
--add-data "StreamingCommunity${{ matrix.separator }}StreamingCommunity" \
|
||||
--add-data="$CERT_DATA" \
|
||||
--add-data="$SC_DATA" \
|
||||
--name=${{ matrix.artifact_name }} test_run.py
|
||||
|
||||
- name: Upload executable
|
||||
|
2
.github/workflows/pages.yml
vendored
2
.github/workflows/pages.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
- name: Copy site files
|
||||
run: |
|
||||
mkdir -p _site
|
||||
cp -r Test/.site/* _site/
|
||||
cp -r .github/.site/* _site/
|
||||
ls -la _site/
|
||||
|
||||
- name: Upload artifact
|
||||
|
2
Makefile
2
Makefile
@ -2,4 +2,4 @@ build-container:
|
||||
docker build -t streaming-community-api .
|
||||
|
||||
run-container:
|
||||
docker run --rm -it -p 8000:8000 -v ${LOCAL_DIR}:/app/Video -v ./config.json:/app/config.json streaming-community-api
|
||||
docker run --rm -it --dns 9.9.9.9 -p 8000:8000 -v ${LOCAL_DIR}:/app/Video -v ./config.json:/app/config.json streaming-community-api
|
743
README.md
743
README.md
@ -31,6 +31,9 @@
|
||||
|
||||
# 📋 Table of Contents
|
||||
|
||||
<details>
|
||||
<summary>📦 Installation</summary>
|
||||
|
||||
- 🔄 [Update Domains](#update-domains)
|
||||
- 🌐 [Available Sites](https://arrowar.github.io/StreamingDirectory/)
|
||||
- 🛠️ [Installation](#installation)
|
||||
@ -40,6 +43,11 @@
|
||||
- 📝 [Manual Installation](#3-manual-installation)
|
||||
- 💻 [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7)
|
||||
- 📱 [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>⚙️ Configuration & Usage</summary>
|
||||
|
||||
- ⚙️ [Configuration](#configuration)
|
||||
- 🔧 [Default](#default-settings)
|
||||
- 📩 [Request](#requests-settings)
|
||||
@ -48,15 +56,23 @@
|
||||
- 📝 [Command](#command)
|
||||
- 🔍 [Global search](#global-search)
|
||||
- 💻 [Examples of terminal](#examples-of-terminal-usage)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔧 Advanced Features</summary>
|
||||
|
||||
- 🔧 [Manual domain configuration](#update-domains)
|
||||
- 🐳 [Docker](#docker)
|
||||
- 📝 [Telegram Usage](#telegram-usage)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>ℹ️ Help & Support</summary>
|
||||
|
||||
- 🎓 [Tutorial](#tutorials)
|
||||
- 📝 [To do](#to-do)
|
||||
- 💬 [Support](#support)
|
||||
- 🤝 [Contribute](#contributing)
|
||||
- ⚠️ [Disclaimer](#disclaimer)
|
||||
- ⚡ [Contributors](#contributors)
|
||||
</details>
|
||||
|
||||
# Installation
|
||||
|
||||
@ -111,7 +127,8 @@ python run_streaming.py
|
||||
|
||||
## Modules
|
||||
|
||||
### HLS Downloader
|
||||
<details>
|
||||
<summary>📥 HLS Downloader</summary>
|
||||
|
||||
Download HTTP Live Streaming (HLS) content from m3u8 URLs.
|
||||
|
||||
@ -129,8 +146,10 @@ downloader.download()
|
||||
```
|
||||
|
||||
See [HLS example](./Test/Download/HLS.py) for complete usage.
|
||||
</details>
|
||||
|
||||
### MP4 Downloader
|
||||
<details>
|
||||
<summary>📽️ MP4 Downloader</summary>
|
||||
|
||||
Direct MP4 file downloader with support for custom headers and referrer.
|
||||
|
||||
@ -159,8 +178,10 @@ downloader.download()
|
||||
```
|
||||
|
||||
See [MP4 example](./Test/Download/MP4.py) for complete usage.
|
||||
</details>
|
||||
|
||||
### Torrent Client
|
||||
<details>
|
||||
<summary>🧲 Torrent Client</summary>
|
||||
|
||||
Download content via torrent magnet links.
|
||||
|
||||
@ -178,67 +199,21 @@ client.start_download()
|
||||
```
|
||||
|
||||
See [Torrent example](./Test/Download/TOR.py) for complete usage.
|
||||
|
||||
|
||||
## 2. Automatic Installation
|
||||
|
||||
### Supported Operating Systems 💿
|
||||
|
||||
| OS | Automatic Installation Support |
|
||||
|:----------------|:------------------------------:|
|
||||
| Windows 10/11 | ✔️ |
|
||||
| Windows 7 | ❌ |
|
||||
| Debian Linux | ✔️ |
|
||||
| Arch Linux | ✔️ |
|
||||
| CentOS Stream 9 | ✔️ |
|
||||
| FreeBSD | ⏳ |
|
||||
| MacOS | ✔️ |
|
||||
| Termux | ❌ |
|
||||
|
||||
### Installation Steps
|
||||
|
||||
#### On Windows:
|
||||
|
||||
```powershell
|
||||
.\Installer\win_install.bat
|
||||
```
|
||||
|
||||
#### On Linux/MacOS/BSD:
|
||||
|
||||
```bash
|
||||
sudo chmod +x Installer/unix_install.sh && ./Installer/unix_install.sh
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
#### On Windows:
|
||||
|
||||
```powershell
|
||||
python .\test_run.py
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```powershell
|
||||
source .venv/bin/activate && python test_run.py && deactivate
|
||||
```
|
||||
|
||||
#### On Linux/MacOS/BSD:
|
||||
|
||||
```bash
|
||||
./test_run.py
|
||||
```
|
||||
</details>
|
||||
|
||||
## Binary Location
|
||||
|
||||
### Default Locations
|
||||
<details>
|
||||
<summary>📂 Default Locations</summary>
|
||||
|
||||
- **Windows**: `C:\binary`
|
||||
- **MacOS**: `~/Applications/binary`
|
||||
- **Linux**: `~/.local/bin/binary`
|
||||
</details>
|
||||
|
||||
You can customize these locations by following these steps for your operating system:
|
||||
<details>
|
||||
<summary>🪟 Windows Configuration</summary>
|
||||
|
||||
#### Windows
|
||||
1. Move the binary folder from `C:\binary` to your desired location
|
||||
2. Add the new path to Windows environment variables:
|
||||
- Open Start menu and search for "Environment Variables"
|
||||
@ -250,8 +225,11 @@ You can customize these locations by following these steps for your operating sy
|
||||
- Click "OK" to save changes
|
||||
|
||||
For detailed Windows PATH instructions, see the [Windows PATH guide](https://www.eukhost.com/kb/how-to-add-to-the-path-on-windows-10-and-windows-11/).
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🍎 MacOS Configuration</summary>
|
||||
|
||||
#### MacOS
|
||||
1. Move the binary folder from `~/Applications/binary` to your desired location
|
||||
2. Add the new path to your shell's configuration file:
|
||||
```bash
|
||||
@ -269,8 +247,11 @@ For detailed Windows PATH instructions, see the [Windows PATH guide](https://www
|
||||
# For zsh
|
||||
source ~/.zshrc
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🐧 Linux Configuration</summary>
|
||||
|
||||
#### Linux
|
||||
1. Move the binary folder from `~/.local/bin/binary` to your desired location
|
||||
2. Add the new path to your shell's configuration file:
|
||||
```bash
|
||||
@ -286,6 +267,7 @@ For detailed Windows PATH instructions, see the [Windows PATH guide](https://www
|
||||
# or
|
||||
source ~/.zshrc # for zsh
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> After moving the binary folder, ensure that all executables (ffmpeg, ffprobe, ffplay) are present in the new location and have the correct permissions:
|
||||
@ -294,19 +276,24 @@ For detailed Windows PATH instructions, see the [Windows PATH guide](https://www
|
||||
|
||||
## 3. Manual Installation
|
||||
|
||||
### Requirements 📋
|
||||
<details>
|
||||
<summary>📋 Requirements</summary>
|
||||
|
||||
Prerequisites:
|
||||
* [Python](https://www.python.org/downloads/) > 3.8
|
||||
* [FFmpeg](https://www.gyan.dev/ffmpeg/builds/)
|
||||
</details>
|
||||
|
||||
### Install Python Dependencies
|
||||
<details>
|
||||
<summary>⚙️ Python Dependencies</summary>
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
</details>
|
||||
|
||||
### Usage
|
||||
<details>
|
||||
<summary>🚀 Usage</summary>
|
||||
|
||||
#### On Windows:
|
||||
|
||||
@ -319,6 +306,7 @@ python test_run.py
|
||||
```bash
|
||||
python3 test_run.py
|
||||
```
|
||||
</details>
|
||||
|
||||
## Update
|
||||
|
||||
@ -338,278 +326,11 @@ python3 update.py
|
||||
|
||||
<br>
|
||||
|
||||
# Configuration
|
||||
|
||||
You can change some behaviors by tweaking the configuration file.
|
||||
|
||||
The configuration file is divided into several main sections:
|
||||
|
||||
## DEFAULT Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"DEFAULT": {
|
||||
"debug": false,
|
||||
"show_message": true,
|
||||
"clean_console": true,
|
||||
"show_trending": true,
|
||||
"use_api": true,
|
||||
"not_close": false,
|
||||
"telegram_bot": false,
|
||||
"download_site_data": false,
|
||||
"validate_github_config": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `debug`: Enables debug logging
|
||||
- `show_message`: Displays informational messages
|
||||
- `clean_console`: Clears the console between operations
|
||||
- `show_trending`: Shows trending content
|
||||
- `use_api`: Uses API for domain updates instead of local configuration
|
||||
- `not_close`: If set to true, keeps the program running after download is complete
|
||||
* Can be changed from terminal with `--not_close true/false`
|
||||
- `telegram_bot`: Enables Telegram bot integration
|
||||
- `download_site_data`: If set to false, disables automatic site data download
|
||||
- `validate_github_config`: If set to false, disables validation and updating of configuration from GitHub
|
||||
|
||||
## OUT_FOLDER Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"OUT_FOLDER": {
|
||||
"root_path": "Video",
|
||||
"movie_folder_name": "Movie",
|
||||
"serie_folder_name": "Serie",
|
||||
"anime_folder_name": "Anime",
|
||||
"map_episode_name": "E%(episode)_%(episode_name)",
|
||||
"add_siteName": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `root_path`: Directory where all videos will be saved
|
||||
|
||||
### Path examples:
|
||||
* Windows: `C:\\MyLibrary\\Folder` or `\\\\MyServer\\MyLibrary` (if you want to use a network folder)
|
||||
* Linux/MacOS: `Desktop/MyLibrary/Folder`
|
||||
<br/><br/>
|
||||
|
||||
- `movie_folder_name`: The name of the subdirectory where movies will be stored
|
||||
* Can be changed from terminal with `--movie_folder_name`
|
||||
<br/><br/>
|
||||
|
||||
- `serie_folder_name`: The name of the subdirectory where TV series will be stored
|
||||
* Can be changed from terminal with `--serie_folder_name`
|
||||
<br/><br/>
|
||||
|
||||
- `anime_folder_name`: The name of the subdirectory where anime will be stored
|
||||
* Can be changed from terminal with `--anime_folder_name`
|
||||
<br/><br/>
|
||||
|
||||
- `map_episode_name`: Template for episode filenames
|
||||
|
||||
### Episode name usage:
|
||||
|
||||
You can choose different vars:
|
||||
* `%(tv_name)` : Is the name of TV Show
|
||||
* `%(season)` : Is the number of the season
|
||||
* `%(episode)` : Is the number of the episode
|
||||
* `%(episode_name)` : Is the name of the episode
|
||||
* Can be changed from terminal with `--map_episode_name`
|
||||
<br><br>
|
||||
|
||||
- `add_siteName`: If set to true, appends the site_name to the root path before the movie and serie folders
|
||||
* Can be changed from terminal with `--add_siteName true/false`
|
||||
<br/><br/>
|
||||
|
||||
## QBIT_CONFIG Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"QBIT_CONFIG": {
|
||||
"host": "192.168.1.51",
|
||||
"port": "6666",
|
||||
"user": "admin",
|
||||
"pass": "adminadmin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To enable qBittorrent integration, follow the setup guide [here](https://github.com/lgallard/qBittorrent-Controller/wiki/How-to-enable-the-qBittorrent-Web-UI).
|
||||
|
||||
## REQUESTS Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"REQUESTS": {
|
||||
"verify": false,
|
||||
"timeout": 20,
|
||||
"max_retry": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `verify`: Verifies SSL certificates
|
||||
- `timeout`: Maximum timeout (in seconds) for each request
|
||||
- `max_retry`: Number of retry attempts per segment during M3U8 index download
|
||||
|
||||
## M3U8_DOWNLOAD Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_DOWNLOAD": {
|
||||
"tqdm_delay": 0.01,
|
||||
"default_video_workser": 12,
|
||||
"default_audio_workser": 12,
|
||||
"segment_timeout": 8,
|
||||
"download_audio": true,
|
||||
"merge_audio": true,
|
||||
"specific_list_audio": [
|
||||
"ita"
|
||||
],
|
||||
"download_subtitle": true,
|
||||
"merge_subs": true,
|
||||
"specific_list_subtitles": [
|
||||
"ita",
|
||||
"eng"
|
||||
],
|
||||
"cleanup_tmp_folder": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `tqdm_delay`: Delay between progress bar updates
|
||||
- `default_video_workser`: Number of threads for video download
|
||||
* Can be changed from terminal with `--default_video_worker <number>`
|
||||
<br/><br/>
|
||||
|
||||
- `default_audio_workser`: Number of threads for audio download
|
||||
* Can be changed from terminal with `--default_audio_worker <number>`
|
||||
<br/><br/>
|
||||
|
||||
- `segment_timeout`: Timeout for downloading individual segments
|
||||
- `download_audio`: Whether to download audio tracks
|
||||
- `merge_audio`: Whether to merge audio with video
|
||||
- `specific_list_audio`: List of audio languages to download
|
||||
* Can be changed from terminal with `--specific_list_audio ita,eng`
|
||||
<br/><br/>
|
||||
|
||||
- `download_subtitle`: Whether to download subtitles
|
||||
- `merge_subs`: Whether to merge subtitles with video
|
||||
- `specific_list_subtitles`: List of subtitle languages to download
|
||||
* Can be changed from terminal with `--specific_list_subtitles ita,eng`
|
||||
<br/><br/>
|
||||
|
||||
- `cleanup_tmp_folder`: Remove temporary .ts files after download
|
||||
|
||||
## Available Language Codes
|
||||
|
||||
| European | Asian | Middle Eastern | Others |
|
||||
|-----------------|-----------------|-----------------|-----------------|
|
||||
| ita - Italian | chi - Chinese | ara - Arabic | eng - English |
|
||||
| spa - Spanish | jpn - Japanese | heb - Hebrew | por - Portuguese|
|
||||
| fre - French | kor - Korean | tur - Turkish | fil - Filipino |
|
||||
| ger - German | hin - Hindi | | ind - Indonesian|
|
||||
| rus - Russian | mal - Malayalam | | may - Malay |
|
||||
| swe - Swedish | tam - Tamil | | vie - Vietnamese|
|
||||
| pol - Polish | tel - Telugu | | |
|
||||
| ukr - Ukrainian | tha - Thai | | |
|
||||
|
||||
## M3U8_CONVERSION Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_CONVERSION": {
|
||||
"use_codec": false,
|
||||
"use_vcodec": true,
|
||||
"use_acodec": true,
|
||||
"use_bitrate": true,
|
||||
"use_gpu": false,
|
||||
"default_preset": "ultrafast"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `use_codec`: Use specific codec settings
|
||||
- `use_vcodec`: Use specific video codec
|
||||
- `use_acodec`: Use specific audio codec
|
||||
- `use_bitrate`: Apply bitrate settings
|
||||
- `use_gpu`: Enable GPU acceleration (if available)
|
||||
- `default_preset`: FFmpeg encoding preset (ultrafast, fast, medium, slow, etc.)
|
||||
|
||||
### Advanced M3U8 Conversion Options
|
||||
|
||||
The software supports various advanced encoding options via FFmpeg:
|
||||
|
||||
#### Encoding Presets
|
||||
The `default_preset` configuration can be set to one of the following values:
|
||||
- `ultrafast`: Extremely fast conversion but larger file size
|
||||
- `superfast`: Very fast with good quality/size ratio
|
||||
- `veryfast`: Fast with good compression
|
||||
- `faster`: Optimal balance for most users
|
||||
- `fast`: Good compression, moderate time
|
||||
- `medium`: FFmpeg default setting
|
||||
- `slow`: High quality, slower process
|
||||
- `slower`: Very high quality, slow process
|
||||
- `veryslow`: Maximum quality, very slow process
|
||||
|
||||
#### GPU Acceleration
|
||||
When `use_gpu` is enabled, the system will use available hardware acceleration:
|
||||
- NVIDIA: NVENC
|
||||
- AMD: AMF
|
||||
- Intel: QSV
|
||||
|
||||
You need to have updated drivers and FFmpeg compiled with hardware acceleration support.
|
||||
|
||||
## M3U8_PARSER Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_PARSER": {
|
||||
"force_resolution": "Best",
|
||||
"get_only_link": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `force_resolution`: Choose the video resolution for downloading:
|
||||
* `"Best"`: Highest available resolution
|
||||
* `"Worst"`: Lowest available resolution
|
||||
* `"720p"`: Force 720p resolution
|
||||
* Or specify one of these resolutions:
|
||||
- 1080p (1920x1080)
|
||||
- 720p (1280x720)
|
||||
- 480p (640x480)
|
||||
- 360p (640x360)
|
||||
- 320p (480x320)
|
||||
- 240p (426x240)
|
||||
- 240p (320x240)
|
||||
- 144p (256x144)
|
||||
|
||||
- `get_only_link`: Return M3U8 playlist/index URL instead of downloading
|
||||
|
||||
## SITE_EXTRA Settings
|
||||
|
||||
```json
|
||||
{
|
||||
"SITE_EXTRA": {
|
||||
"ddlstreamitaly": {
|
||||
"ips4_device_key": "",
|
||||
"ips4_member_id": "",
|
||||
"ips4_login_key": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Site-specific configuration for `ddlstreamitaly`:
|
||||
- `ips4_device_key`: Device key for authentication
|
||||
- `ips4_member_id`: Member ID for authentication
|
||||
- `ips4_login_key`: Login key for authentication
|
||||
|
||||
## Update Domains
|
||||
|
||||
<details>
|
||||
<summary>🌐 Domain Configuration Methods</summary>
|
||||
|
||||
There are two ways to update the domains for the supported websites:
|
||||
|
||||
### 1. Using Local Configuration
|
||||
@ -645,23 +366,281 @@ Note: If `use_api` is set to `false` and no `domains.json` file is found, the sc
|
||||
#### 💡 Adding a New Site to the Legacy API
|
||||
If you want to add a new site to the legacy API, just message me on the Discord server, and I'll add it!
|
||||
|
||||
</details>
|
||||
|
||||
# Configuration
|
||||
|
||||
<details>
|
||||
<summary>⚙️ Overview</summary>
|
||||
|
||||
You can change some behaviors by tweaking the configuration file. The configuration file is divided into several main sections.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔧 DEFAULT Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"DEFAULT": {
|
||||
"debug": false,
|
||||
"show_message": true,
|
||||
"clean_console": true,
|
||||
"show_trending": true,
|
||||
"use_api": true,
|
||||
"not_close": false,
|
||||
"telegram_bot": false,
|
||||
"download_site_data": false,
|
||||
"validate_github_config": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `debug`: Enables debug logging
|
||||
- `show_message`: Displays informational messages
|
||||
- `clean_console`: Clears the console between operations
|
||||
- `show_trending`: Shows trending content
|
||||
- `use_api`: Uses API for domain updates instead of local configuration
|
||||
- `not_close`: If set to true, keeps the program running after download is complete
|
||||
* Can be changed from terminal with `--not_close true/false`
|
||||
- `telegram_bot`: Enables Telegram bot integration
|
||||
- `download_site_data`: If set to false, disables automatic site data download
|
||||
- `validate_github_config`: If set to false, disables validation and updating of configuration from GitHub
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>📁 OUT_FOLDER Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"OUT_FOLDER": {
|
||||
"root_path": "Video",
|
||||
"movie_folder_name": "Movie",
|
||||
"serie_folder_name": "Serie",
|
||||
"anime_folder_name": "Anime",
|
||||
"map_episode_name": "E%(episode)_%(episode_name)",
|
||||
"add_siteName": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Directory Configuration
|
||||
- `root_path`: Directory where all videos will be saved
|
||||
* Windows: `C:\\MyLibrary\\Folder` or `\\\\MyServer\\MyLibrary` (network folder)
|
||||
* Linux/MacOS: `Desktop/MyLibrary/Folder`
|
||||
|
||||
#### Folder Names
|
||||
- `movie_folder_name`: Subdirectory for movies (can be changed with `--movie_folder_name`)
|
||||
- `serie_folder_name`: Subdirectory for TV series (can be changed with `--serie_folder_name`)
|
||||
- `anime_folder_name`: Subdirectory for anime (can be changed with `--anime_folder_name`)
|
||||
|
||||
#### Episode Naming
|
||||
- `map_episode_name`: Template for episode filenames
|
||||
* `%(tv_name)`: Name of TV Show
|
||||
* `%(season)`: Season number
|
||||
* `%(episode)`: Episode number
|
||||
* `%(episode_name)`: Episode name
|
||||
* Can be changed with `--map_episode_name`
|
||||
|
||||
#### Additional Options
|
||||
- `add_siteName`: Appends site_name to root path (can be changed with `--add_siteName true/false`)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔄 QBIT_CONFIG Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"QBIT_CONFIG": {
|
||||
"host": "192.168.1.51",
|
||||
"port": "6666",
|
||||
"user": "admin",
|
||||
"pass": "adminadmin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To enable qBittorrent integration, follow the setup guide [here](https://github.com/lgallard/qBittorrent-Controller/wiki/How-to-enable-the-qBittorrent-Web-UI).
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>📡 REQUESTS Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"REQUESTS": {
|
||||
"verify": false,
|
||||
"timeout": 20,
|
||||
"max_retry": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `verify`: Verifies SSL certificates
|
||||
- `timeout`: Maximum timeout (in seconds) for each request
|
||||
- `max_retry`: Number of retry attempts per segment during M3U8 index download
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>📥 M3U8_DOWNLOAD Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_DOWNLOAD": {
|
||||
"tqdm_delay": 0.01,
|
||||
"default_video_workser": 12,
|
||||
"default_audio_workser": 12,
|
||||
"segment_timeout": 8,
|
||||
"download_audio": true,
|
||||
"merge_audio": true,
|
||||
"specific_list_audio": [
|
||||
"ita"
|
||||
],
|
||||
"download_subtitle": true,
|
||||
"merge_subs": true,
|
||||
"specific_list_subtitles": [
|
||||
"ita",
|
||||
"eng"
|
||||
],
|
||||
"cleanup_tmp_folder": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Performance Settings
|
||||
- `tqdm_delay`: Delay between progress bar updates
|
||||
- `default_video_workser`: Number of threads for video download
|
||||
* Can be changed with `--default_video_worker <number>`
|
||||
- `default_audio_workser`: Number of threads for audio download
|
||||
* Can be changed with `--default_audio_worker <number>`
|
||||
- `segment_timeout`: Timeout for downloading individual segments
|
||||
|
||||
#### Audio Settings
|
||||
- `download_audio`: Whether to download audio tracks
|
||||
- `merge_audio`: Whether to merge audio with video
|
||||
- `specific_list_audio`: List of audio languages to download
|
||||
* Can be changed with `--specific_list_audio ita,eng`
|
||||
|
||||
#### Subtitle Settings
|
||||
- `download_subtitle`: Whether to download subtitles
|
||||
- `merge_subs`: Whether to merge subtitles with video
|
||||
- `specific_list_subtitles`: List of subtitle languages to download
|
||||
* Can be changed with `--specific_list_subtitles ita,eng`
|
||||
|
||||
#### Cleanup
|
||||
- `cleanup_tmp_folder`: Remove temporary .ts files after download
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🌍 Available Language Codes</summary>
|
||||
|
||||
| European | Asian | Middle Eastern | Others |
|
||||
|-----------------|-----------------|-----------------|-----------------|
|
||||
| ita - Italian | chi - Chinese | ara - Arabic | eng - English |
|
||||
| spa - Spanish | jpn - Japanese | heb - Hebrew | por - Portuguese|
|
||||
| fre - French | kor - Korean | tur - Turkish | fil - Filipino |
|
||||
| ger - German | hin - Hindi | | ind - Indonesian|
|
||||
| rus - Russian | mal - Malayalam | | may - Malay |
|
||||
| swe - Swedish | tam - Tamil | | vie - Vietnamese|
|
||||
| pol - Polish | tel - Telugu | | |
|
||||
| ukr - Ukrainian | tha - Thai | | |
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🎥 M3U8_CONVERSION Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_CONVERSION": {
|
||||
"use_codec": false,
|
||||
"use_vcodec": true,
|
||||
"use_acodec": true,
|
||||
"use_bitrate": true,
|
||||
"use_gpu": false,
|
||||
"default_preset": "ultrafast"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Basic Settings
|
||||
- `use_codec`: Use specific codec settings
|
||||
- `use_vcodec`: Use specific video codec
|
||||
- `use_acodec`: Use specific audio codec
|
||||
- `use_bitrate`: Apply bitrate settings
|
||||
- `use_gpu`: Enable GPU acceleration (if available)
|
||||
- `default_preset`: FFmpeg encoding preset
|
||||
|
||||
#### Encoding Presets
|
||||
The `default_preset` configuration can be set to:
|
||||
- `ultrafast`: Extremely fast conversion but larger file size
|
||||
- `superfast`: Very fast with good quality/size ratio
|
||||
- `veryfast`: Fast with good compression
|
||||
- `faster`: Optimal balance for most users
|
||||
- `fast`: Good compression, moderate time
|
||||
- `medium`: FFmpeg default setting
|
||||
- `slow`: High quality, slower process
|
||||
- `slower`: Very high quality, slow process
|
||||
- `veryslow`: Maximum quality, very slow process
|
||||
|
||||
#### GPU Acceleration
|
||||
When `use_gpu` is enabled, supports:
|
||||
- NVIDIA: NVENC
|
||||
- AMD: AMF
|
||||
- Intel: QSV
|
||||
|
||||
Note: Requires updated drivers and FFmpeg with hardware acceleration support.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔍 M3U8_PARSER Settings</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"M3U8_PARSER": {
|
||||
"force_resolution": "Best",
|
||||
"get_only_link": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Resolution Options
|
||||
- `force_resolution`: Choose video resolution:
|
||||
* `"Best"`: Highest available resolution
|
||||
* `"Worst"`: Lowest available resolution
|
||||
* `"720p"`: Force 720p resolution
|
||||
* Specific resolutions:
|
||||
- 1080p (1920x1080)
|
||||
- 720p (1280x720)
|
||||
- 480p (640x480)
|
||||
- 360p (640x360)
|
||||
- 320p (480x320)
|
||||
- 240p (426x240)
|
||||
- 240p (320x240)
|
||||
- 144p (256x144)
|
||||
|
||||
#### Link Options
|
||||
- `get_only_link`: Return M3U8 playlist/index URL instead of downloading
|
||||
</details>
|
||||
|
||||
# Global Search
|
||||
|
||||
<details>
|
||||
<summary>🔍 Feature Overview</summary>
|
||||
|
||||
You can now search across multiple streaming sites at once using the Global Search feature. This allows you to find content more efficiently without having to search each site individually.
|
||||
</details>
|
||||
|
||||
## Using Global Search
|
||||
|
||||
The Global Search feature provides a unified interface to search across all supported sites:
|
||||
|
||||
## Search Options
|
||||
<details>
|
||||
<summary>🎯 Search Options</summary>
|
||||
|
||||
When using Global Search, you have three ways to select which sites to search:
|
||||
|
||||
1. **Search all sites** - Searches across all available streaming sites
|
||||
2. **Search by category** - Group sites by their categories (movies, series, anime, etc.)
|
||||
3. **Select specific sites** - Choose individual sites to include in your search
|
||||
</details>
|
||||
|
||||
## Navigation and Selection
|
||||
<details>
|
||||
<summary>📝 Navigation and Selection</summary>
|
||||
|
||||
After performing a search:
|
||||
|
||||
@ -673,13 +652,16 @@ After performing a search:
|
||||
2. Select an item by number to view details or download
|
||||
|
||||
3. The system will automatically use the appropriate site's API to handle the download
|
||||
</details>
|
||||
|
||||
## Command Line Arguments
|
||||
<details>
|
||||
<summary>⌨️ Command Line Arguments</summary>
|
||||
|
||||
The Global Search can be configured from the command line:
|
||||
|
||||
- `--global` - Perform a global search across multiple sites.
|
||||
- `-s`, `--search` - Specify the search terms.
|
||||
</details>
|
||||
|
||||
# Examples of terminal usage
|
||||
|
||||
@ -699,25 +681,32 @@ python test_run.py --global -s "cars"
|
||||
|
||||
# Docker
|
||||
|
||||
You can run the script in a docker container, to build the image just run
|
||||
<details>
|
||||
<summary>🐳 Basic Setup</summary>
|
||||
|
||||
Build the image:
|
||||
```
|
||||
docker build -t streaming-community-api .
|
||||
```
|
||||
|
||||
and to run it use
|
||||
Run the container with Cloudflare DNS for better connectivity:
|
||||
```
|
||||
docker run -it --dns 1.1.1.1 -p 8000:8000 streaming-community-api
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>💾 Custom Storage Location</summary>
|
||||
|
||||
By default the videos will be saved in `/app/Video` inside the container. To save them on your machine:
|
||||
|
||||
```
|
||||
docker run -it -p 8000:8000 streaming-community-api
|
||||
docker run -it --dns 9.9.9.9 -p 8000:8000 -v /path/to/download:/app/Video streaming-community-api
|
||||
```
|
||||
</details>
|
||||
|
||||
By default the videos will be saved in `/app/Video` inside the container, if you want to to save them in your machine instead of the container just run
|
||||
|
||||
```
|
||||
docker run -it -p 8000:8000 -v /path/to/download:/app/Video streaming-community-api
|
||||
```
|
||||
|
||||
### Docker quick setup with Make
|
||||
<details>
|
||||
<summary>🛠️ Quick Setup with Make</summary>
|
||||
|
||||
Inside the Makefile (install `make`) are already configured two commands to build and run the container:
|
||||
|
||||
@ -729,52 +718,57 @@ make LOCAL_DIR=/path/to/download run-container
|
||||
```
|
||||
|
||||
The `run-container` command mounts also the `config.json` file, so any change to the configuration file is reflected immediately without having to rebuild the image.
|
||||
</details>
|
||||
|
||||
# Telegram Usage
|
||||
|
||||
## Configuration
|
||||
<details>
|
||||
<summary>⚙️ Basic Configuration</summary>
|
||||
|
||||
The bot was created to replace terminal commands and allow interaction via Telegram. Each download runs within a screen session, enabling multiple downloads to run simultaneously.
|
||||
|
||||
To run the bot in the background, simply start it inside a screen session and then press Ctrl + A, followed by D, to detach from the session without stopping the bot.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🤖 Bot Commands</summary>
|
||||
|
||||
Command Functions:
|
||||
|
||||
🔹 /start – Starts a new search for a download. This command performs the same operations as manually running the script in the terminal with test_run.py.
|
||||
|
||||
🔹 /list – Displays the status of active downloads, with options to:
|
||||
|
||||
Stop an incorrect download using /stop <ID>.
|
||||
|
||||
View the real-time output of a download using /screen <ID>.
|
||||
- Stop an incorrect download using /stop <ID>
|
||||
- View the real-time output of a download using /screen <ID>
|
||||
|
||||
⚠ Warning: If a download is interrupted, incomplete files may remain in the folder specified in config.json. These files must be deleted manually to avoid storage or management issues.
|
||||
</details>
|
||||
|
||||
🛠 Configuration: Currently, the bot's settings are stored in the config.json file, which is located in the same directory as the telegram_bot.py script.
|
||||
<details>
|
||||
<summary>🔧 Environment Setup</summary>
|
||||
|
||||
## .env Example:
|
||||
|
||||
You need to create an .env file and enter your Telegram token and user ID to authorize only one user to use it
|
||||
Create an `.env` file with:
|
||||
|
||||
```
|
||||
TOKEN_TELEGRAM=IlTuo2131TOKEN$12D3Telegram
|
||||
AUTHORIZED_USER_ID=12345678
|
||||
DEBUG=False
|
||||
```
|
||||
</details>
|
||||
|
||||
## Install Python Dependencies
|
||||
<details>
|
||||
<summary>📥 Dependencies & Launch</summary>
|
||||
|
||||
Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## On Linux/MacOS:
|
||||
|
||||
Start the bot from the folder /StreamingCommunity/TelegramHelp
|
||||
|
||||
Start the bot (from /StreamingCommunity/TelegramHelp):
|
||||
```bash
|
||||
python3 telegram_bot.py
|
||||
```
|
||||
</details>
|
||||
|
||||
# Tutorials
|
||||
|
||||
@ -788,19 +782,6 @@ python3 telegram_bot.py
|
||||
- To Finish [website API](https://github.com/Arrowar/StreamingCommunity/tree/test_gui_1)
|
||||
- To finish [website API 2](https://github.com/hydrosh/StreamingCommunity/tree/test_gui_1)
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome! Steps:
|
||||
1. Fork the repository
|
||||
2. Create feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open Pull Request
|
||||
|
||||
# Disclaimer
|
||||
|
||||
This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
||||
|
||||
## Useful Project
|
||||
|
||||
### 🎯 [Unit3Dup](https://github.com/31December99/Unit3Dup)
|
||||
@ -816,8 +797,6 @@ API non ufficiale per accedere ai contenuti del sito italiano StreamingCommunity
|
||||
### 🎥 [stream-buddy](https://github.com/Bbalduzz/stream-buddy)
|
||||
Tool per guardare o scaricare film dalla piattaforma StreamingCommunity.
|
||||
|
||||
## Contributors
|
||||
# Disclaimer
|
||||
|
||||
<a href="https://github.com/Arrowar/StreamingCommunity/graphs/contributors" alt="View Contributors">
|
||||
<img src="https://contrib.rocks/image?repo=Arrowar/StreamingCommunity&max=1000&columns=10" alt="Contributors" />
|
||||
</a>
|
||||
This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
@ -24,6 +24,7 @@ indice = 3
|
||||
_useFor = "film_serie"
|
||||
_priority = 0
|
||||
_engineDownload = "tor"
|
||||
_deprecate = False
|
||||
|
||||
console = Console()
|
||||
msg = Prompt()
|
||||
|
@ -27,6 +27,7 @@ indice = 2
|
||||
_useFor = "film_serie"
|
||||
_priority = 0
|
||||
_engineDownload = "hls"
|
||||
_deprecate = True
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -27,6 +27,7 @@ indice = 1
|
||||
_useFor = "anime"
|
||||
_priority = 0
|
||||
_engineDownload = "mp4"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -22,6 +22,7 @@ indice = 8
|
||||
_useFor = "anime"
|
||||
_priority = 0
|
||||
_engineDownload = "mp4"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -1,74 +0,0 @@
|
||||
# 09.06.24
|
||||
|
||||
import logging
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
|
||||
# External library
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Api.Template import get_select_title
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
||||
|
||||
|
||||
# Logic class
|
||||
from .site import title_search, media_search_manager, table_show_manager
|
||||
from .series import download_thread
|
||||
|
||||
|
||||
# Variable
|
||||
indice = 6
|
||||
_useFor = "serie"
|
||||
_priority = 0
|
||||
_engineDownload = "mp4"
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
||||
|
||||
def process_search_result(select_title):
|
||||
"""
|
||||
Handles the search result and initiates the download for either a film or series.
|
||||
"""
|
||||
if "Serie TV" in str(select_title.type):
|
||||
download_thread(select_title)
|
||||
else:
|
||||
logging.error(f"Not supported: {select_title.type}")
|
||||
|
||||
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
||||
"""
|
||||
Main function of the application for search.
|
||||
|
||||
Parameters:
|
||||
string_to_search (str, optional): String to search for
|
||||
get_onylDatabase (bool, optional): If True, return only the database object
|
||||
direct_item (dict, optional): Direct item to process (bypass search)
|
||||
"""
|
||||
if direct_item:
|
||||
select_title = MediaItem(**direct_item)
|
||||
process_search_result(select_title)
|
||||
return
|
||||
|
||||
if string_to_search is None:
|
||||
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
|
||||
|
||||
# Search on database
|
||||
len_database = title_search(quote_plus(string_to_search))
|
||||
|
||||
# If only the database is needed, return the manager
|
||||
if get_onlyDatabase:
|
||||
return media_search_manager
|
||||
|
||||
if len_database > 0:
|
||||
select_title = get_select_title(table_show_manager, media_search_manager)
|
||||
process_search_result(select_title)
|
||||
|
||||
else:
|
||||
|
||||
# If no results are found, ask again
|
||||
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
||||
search()
|
@ -1,118 +0,0 @@
|
||||
# 13.06.24
|
||||
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
# External library
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.message import start_message
|
||||
from StreamingCommunity.Util.os import os_manager
|
||||
from StreamingCommunity.Lib.Downloader import MP4_downloader
|
||||
|
||||
|
||||
# Logic class
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
||||
from StreamingCommunity.Api.Template.Util import (
|
||||
manage_selection,
|
||||
map_episode_title,
|
||||
validate_episode_selection,
|
||||
display_episodes_list
|
||||
)
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
|
||||
|
||||
# Player
|
||||
from .util.ScrapeSerie import GetSerieInfo
|
||||
from StreamingCommunity.Api.Player.ddl import VideoSource
|
||||
|
||||
|
||||
# Variable
|
||||
console = Console()
|
||||
|
||||
|
||||
def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo) -> Tuple[str,bool]:
|
||||
"""
|
||||
Downloads a specific episode.
|
||||
|
||||
Parameters:
|
||||
- index_episode_selected (int): Episode index
|
||||
- scape_info_serie (GetSerieInfo): Scraper object with series information
|
||||
|
||||
Returns:
|
||||
- str: Path to downloaded file
|
||||
- bool: Whether download was stopped
|
||||
"""
|
||||
start_message()
|
||||
|
||||
# Get episode information
|
||||
obj_episode = scape_info_serie.selectEpisode(1, index_episode_selected-1)
|
||||
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]E{index_episode_selected}[/cyan]) \n")
|
||||
|
||||
# Define filename and path for the downloaded video
|
||||
title_name = os_manager.get_sanitize_file(
|
||||
f"{map_episode_title(scape_info_serie.tv_name, None, index_episode_selected, obj_episode.get('name'))}.mp4"
|
||||
)
|
||||
mp4_path = os.path.join(site_constant.SERIES_FOLDER, scape_info_serie.tv_name)
|
||||
|
||||
# Create output folder
|
||||
os_manager.create_path(mp4_path)
|
||||
|
||||
# Setup video source
|
||||
video_source = VideoSource(site_constant.COOKIE, obj_episode.get('url'))
|
||||
|
||||
# Get m3u8 master playlist
|
||||
master_playlist = video_source.get_playlist()
|
||||
|
||||
# Parse start page url
|
||||
parsed_url = urlparse(obj_episode.get('url'))
|
||||
|
||||
# Start download
|
||||
r_proc = MP4_downloader(
|
||||
url=master_playlist,
|
||||
path=os.path.join(mp4_path, title_name),
|
||||
referer=f"{parsed_url.scheme}://{parsed_url.netloc}/",
|
||||
)
|
||||
|
||||
if r_proc != None:
|
||||
console.print("[green]Result: ")
|
||||
console.print(r_proc)
|
||||
|
||||
return os.path.join(mp4_path, title_name), False
|
||||
|
||||
|
||||
def download_thread(dict_serie: MediaItem, episode_selection: str = None):
|
||||
"""
|
||||
Download all episode of a thread
|
||||
|
||||
Parameters:
|
||||
dict_serie (MediaItem): The selected media item
|
||||
episode_selection (str, optional): Episode selection input that bypasses manual input
|
||||
"""
|
||||
scrape_serie = GetSerieInfo(dict_serie, site_constant.COOKIE)
|
||||
|
||||
# Get episode list
|
||||
episodes = scrape_serie.getEpisodeSeasons()
|
||||
episodes_count = len(episodes)
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
if episode_selection is None:
|
||||
last_command = display_episodes_list(scrape_serie.list_episodes)
|
||||
else:
|
||||
last_command = episode_selection
|
||||
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
||||
|
||||
# Validate episode selection
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
||||
|
||||
# Download selected episodes
|
||||
kill_handler = bool(False)
|
||||
for i_episode in list_episode_select:
|
||||
if kill_handler:
|
||||
break
|
||||
kill_handler = download_video(i_episode, scrape_serie)[1]
|
@ -1,87 +0,0 @@
|
||||
# 09.06.24
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.config_json import config_manager
|
||||
from StreamingCommunity.Util.headers import get_userAgent
|
||||
from StreamingCommunity.Util.table import TVShowManager
|
||||
|
||||
|
||||
# Logic class
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
||||
|
||||
|
||||
# Variable
|
||||
console = Console()
|
||||
media_search_manager = MediaManager()
|
||||
table_show_manager = TVShowManager()
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
|
||||
|
||||
def title_search(query: str) -> int:
|
||||
"""
|
||||
Search for titles based on a search query.
|
||||
|
||||
Parameters:
|
||||
- query (str): The query to search for.
|
||||
|
||||
Returns:
|
||||
- int: The number of titles found.
|
||||
"""
|
||||
media_search_manager.clear()
|
||||
table_show_manager.clear()
|
||||
|
||||
search_url = f"{site_constant.FULL_URL}/search/?&q={query}&quick=1&type=videobox_video&nodes=11"
|
||||
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
||||
|
||||
try:
|
||||
response = httpx.get(
|
||||
search_url,
|
||||
headers={'user-agent': get_userAgent()},
|
||||
timeout=max_timeout,
|
||||
follow_redirects=True
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
|
||||
return 0
|
||||
|
||||
# Create soup and find table
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
table_content = soup.find('ol', class_="ipsStream")
|
||||
|
||||
if table_content:
|
||||
for title_div in table_content.find_all('li', class_='ipsStreamItem'):
|
||||
try:
|
||||
|
||||
title_type = title_div.find("p", class_="ipsType_reset").find_all("a")[-1].get_text(strip=True)
|
||||
name = title_div.find("span", class_="ipsContained").find("a").get_text(strip=True)
|
||||
link = title_div.find("span", class_="ipsContained").find("a").get("href")
|
||||
|
||||
title_info = {
|
||||
'name': name,
|
||||
'url': link,
|
||||
'type': title_type,
|
||||
'image': title_div.find("div", class_="ipsColumn").find("img").get("src")
|
||||
}
|
||||
|
||||
media_search_manager.add_media(title_info)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error parsing a film entry: {e}")
|
||||
|
||||
return media_search_manager.get_length()
|
||||
|
||||
else:
|
||||
logging.error("No table content found.")
|
||||
return -999
|
@ -1,112 +0,0 @@
|
||||
# 13.06.24
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.config_json import config_manager
|
||||
from StreamingCommunity.Util.headers import get_userAgent
|
||||
|
||||
|
||||
# Logic class
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
||||
|
||||
|
||||
# Variable
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
|
||||
|
||||
class GetSerieInfo:
|
||||
def __init__(self, dict_serie: MediaItem, cookies) -> None:
|
||||
"""
|
||||
Initializes the GetSerieInfo object with default values.
|
||||
|
||||
Parameters:
|
||||
- dict_serie (MediaItem): Dictionary containing series information (optional).
|
||||
"""
|
||||
self.headers = {'user-agent': get_userAgent()}
|
||||
self.cookies = cookies
|
||||
self.url = dict_serie.url
|
||||
self.tv_name = None
|
||||
self.list_episodes = None
|
||||
|
||||
def get_episode_number(self) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Retrieves the number of episodes for a specific season.
|
||||
|
||||
Parameters:
|
||||
n_season (int): The season number.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, str]]: List of dictionaries containing episode information.
|
||||
"""
|
||||
try:
|
||||
response = httpx.get(f"{self.url}?area=online", cookies=self.cookies, headers=self.headers, timeout=max_timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Insert value for [ips4_device_key, ips4_member_id, ips4_login_key] in config.json file SITE \\ ddlstreamitaly \\ cookie. Use browser debug and cookie request with a valid account, filter by DOC. Error: {e}")
|
||||
sys.exit(0)
|
||||
|
||||
# Parse HTML content of the page
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
|
||||
# Get tv name
|
||||
self.tv_name = soup.find("span", class_= "ipsType_break").get_text(strip=True)
|
||||
|
||||
# Find the container of episodes for the specified season
|
||||
table_content = soup.find('div', class_='ipsMargin_bottom:half')
|
||||
list_dict_episode = []
|
||||
|
||||
for episode_div in table_content.find_all('a', href=True):
|
||||
|
||||
# Get text of episode
|
||||
part_name = episode_div.get_text(strip=True)
|
||||
|
||||
if part_name:
|
||||
obj_episode = {
|
||||
'name': part_name,
|
||||
'url': episode_div['href']
|
||||
}
|
||||
|
||||
list_dict_episode.append(obj_episode)
|
||||
|
||||
self.list_episodes = list_dict_episode
|
||||
return list_dict_episode
|
||||
|
||||
|
||||
# ------------- FOR GUI -------------
|
||||
def getNumberSeason(self) -> int:
|
||||
"""
|
||||
Get the total number of seasons available for the series.
|
||||
Note: DDLStreamItaly typically provides content organized as threads, not seasons.
|
||||
"""
|
||||
return 1
|
||||
|
||||
def getEpisodeSeasons(self, season_number: int = 1) -> list:
|
||||
"""
|
||||
Get all episodes for a specific season.
|
||||
Note: For DDLStreamItaly, this returns all episodes as they're typically in one list.
|
||||
"""
|
||||
if not self.list_episodes:
|
||||
self.list_episodes = self.get_episode_number()
|
||||
|
||||
return self.list_episodes
|
||||
|
||||
def selectEpisode(self, season_number: int = 1, episode_index: int = 0) -> dict:
|
||||
"""
|
||||
Get information for a specific episode.
|
||||
"""
|
||||
episodes = self.getEpisodeSeasons()
|
||||
if not episodes or episode_index < 0 or episode_index >= len(episodes):
|
||||
logging.error(f"Episode index {episode_index} is out of range")
|
||||
return None
|
||||
|
||||
return episodes[episode_index]
|
@ -24,6 +24,7 @@ indice = 5
|
||||
_useFor = "serie"
|
||||
_priority = 0
|
||||
_engineDownload = "hls"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -23,6 +23,7 @@ indice = 8
|
||||
_useFor = "film_serie"
|
||||
_priority = 1 # NOTE: Site search need the use of tmbd obj
|
||||
_engineDownload = "hls"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -28,6 +28,7 @@ indice = 0
|
||||
_useFor = "film_serie"
|
||||
_priority = 0
|
||||
_engineDownload = "hls"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -23,6 +23,7 @@ indice = 8
|
||||
_useFor = "film_serie"
|
||||
_priority = 10 # !!! MOLTO LENTO
|
||||
_engineDownload = "hls"
|
||||
_deprecate = False
|
||||
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
@ -166,7 +166,6 @@ class ConfigManager:
|
||||
raise Exception(f"Error downloading reference configuration. Code: {response.status_code}")
|
||||
|
||||
reference_config = response.json()
|
||||
console.print(f"[bold cyan]Reference configuration downloaded:[/bold cyan] [green]{len(reference_config)} keys available[/green]")
|
||||
|
||||
# Compare and update missing keys
|
||||
merged_config = self._deep_merge_configs(self.config, reference_config)
|
||||
@ -285,7 +284,6 @@ class ConfigManager:
|
||||
self.configSite = data[0]['data']
|
||||
|
||||
site_count = len(self.configSite) if isinstance(self.configSite, dict) else 0
|
||||
console.print(f"[bold green]Site data retrieved:[/bold green] {site_count} streaming services available")
|
||||
|
||||
else:
|
||||
console.print("[bold yellow]API returned an empty data set[/bold yellow]")
|
||||
|
@ -61,7 +61,8 @@ def load_search_functions():
|
||||
priority = getattr(mod, '_priority', 0)
|
||||
|
||||
if priority == 0:
|
||||
modules.append((module_name, indice, use_for))
|
||||
if not getattr(mod, '_deprecate'):
|
||||
modules.append((module_name, indice, use_for))
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
|
||||
|
@ -30,7 +30,7 @@ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance, Teleg
|
||||
|
||||
# Config
|
||||
SHOW_TRENDING = config_manager.get_bool('DEFAULT', 'show_trending')
|
||||
CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
||||
NOT_CLOSE_CONSOLE = config_manager.get_bool('DEFAULT', 'not_close')
|
||||
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ def load_search_functions():
|
||||
loaded_functions = {}
|
||||
|
||||
# Lista dei siti da escludere se TELEGRAM_BOT è attivo
|
||||
excluded_sites = {"cb01new", "ddlstreamitaly", "guardaserie", "ilcorsaronero", "mostraguarda"} if TELEGRAM_BOT else set()
|
||||
excluded_sites = {"cb01new", "guardaserie", "ilcorsaronero", "mostraguarda"} if TELEGRAM_BOT else set()
|
||||
|
||||
# Find api home directory
|
||||
if getattr(sys, 'frozen', False): # Modalità PyInstaller
|
||||
@ -89,9 +89,11 @@ def load_search_functions():
|
||||
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
|
||||
|
||||
# Get 'indice' from the module
|
||||
indice = getattr(mod, 'indice', 0)
|
||||
use_for = getattr(mod, '_useFor', 'other')
|
||||
modules.append((module_name, indice, use_for))
|
||||
indice = getattr(mod, 'indice')
|
||||
use_for = getattr(mod, '_useFor')
|
||||
|
||||
if not getattr(mod, '_deprecate'):
|
||||
modules.append((module_name, indice, use_for))
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
|
||||
@ -202,13 +204,16 @@ def main(script_id = 0):
|
||||
initialize()
|
||||
|
||||
if not internet_manager.check_dns_provider():
|
||||
print()
|
||||
console.print("[red]❌ ERROR: DNS configuration is required!")
|
||||
console.print("[red]The program cannot function correctly without proper DNS settings.")
|
||||
console.print("[yellow]Please configure one of these DNS servers:")
|
||||
console.print("[blue]• Cloudflare (1.1.1.1)")
|
||||
console.print("[blue]• Quad9 (9.9.9.9)")
|
||||
console.print("[blue]• Cloudflare (1.1.1.1) 'https://developers.cloudflare.com/1.1.1.1/setup/windows/'")
|
||||
console.print("[blue]• Quad9 (9.9.9.9) 'https://docs.quad9.net/Setup_Guides/Windows/Windows_10/'")
|
||||
console.print("\n[yellow]⚠️ The program will not work until you configure your DNS settings.")
|
||||
input("[yellow]Press Enter to exit...")
|
||||
|
||||
time.sleep(1)
|
||||
msg.ask("[yellow]Press Enter to exit...")
|
||||
|
||||
# Load search functions
|
||||
search_functions = load_search_functions()
|
||||
@ -259,21 +264,9 @@ def main(script_id = 0):
|
||||
"other": "white"
|
||||
}
|
||||
|
||||
# Add dynamic arguments based on loaded search modules
|
||||
used_short_options = set()
|
||||
|
||||
for alias, (_, use_for) in search_functions.items():
|
||||
short_option = alias[:3].upper()
|
||||
|
||||
original_short_option = short_option
|
||||
count = 1
|
||||
while short_option in used_short_options:
|
||||
short_option = f"{original_short_option}{count}"
|
||||
count += 1
|
||||
|
||||
used_short_options.add(short_option)
|
||||
long_option = alias
|
||||
parser.add_argument(f'-{short_option}', f'--{long_option}', action='store_true', help=f'Search for {alias.split("_")[0]} on streaming platforms.')
|
||||
# Add numeric arguments for each search module
|
||||
for idx, (alias, (_, use_for)) in enumerate(search_functions.items()):
|
||||
parser.add_argument(f'--{idx}', action='store_true', help=f'Search using {alias.split("_")[0]} ({use_for})')
|
||||
|
||||
parser.add_argument('-s', '--search', default=None, help='Search terms')
|
||||
|
||||
@ -309,12 +302,11 @@ def main(script_id = 0):
|
||||
global_search(search_terms)
|
||||
return
|
||||
|
||||
# Map command-line arguments to functions
|
||||
arg_to_function = {alias: func for alias, (func, _) in search_functions.items()}
|
||||
|
||||
# Check which argument is provided and run the corresponding function
|
||||
for arg, func in arg_to_function.items():
|
||||
if getattr(args, arg):
|
||||
# Check for numeric arguments
|
||||
search_functions_list = list(search_functions.items())
|
||||
for i in range(len(search_functions_list)):
|
||||
if getattr(args, str(i)):
|
||||
alias, (func, _) = search_functions_list[i]
|
||||
run_function(func, search_terms=search_terms)
|
||||
return
|
||||
|
||||
@ -324,28 +316,23 @@ def main(script_id = 0):
|
||||
# Create dynamic prompt message and choices
|
||||
choice_labels = {str(i): (alias.split("_")[0].capitalize(), use_for) for i, (alias, (_, use_for)) in enumerate(search_functions.items())}
|
||||
|
||||
# Add global search option to the menu
|
||||
#global_search_key = str(len(choice_labels))
|
||||
#choice_labels[global_search_key] = ("Global Search", "all")
|
||||
#input_to_function[global_search_key] = global_search
|
||||
|
||||
# Display the category legend in a single line
|
||||
legend_text = " | ".join([f"[{color}]{category.capitalize()}[/{color}]" for category, color in color_map.items()])
|
||||
console.print(f"\n[bold green]Category Legend:[/bold green] {legend_text}")
|
||||
|
||||
# Construct the prompt message with color-coded site names
|
||||
# Construct the prompt message with color-coded site names and aliases
|
||||
prompt_message = "[green]Insert category [white](" + ", ".join(
|
||||
[f"{key}: [{color_map.get(label[1], 'white')}]{label[0]}[/{color_map.get(label[1], 'white')}]" for key, label in choice_labels.items()]
|
||||
[f"{key}: [{color_map.get(label[1], 'white')}]{label[0]}"
|
||||
for key, label in choice_labels.items()]
|
||||
) + "[white])"
|
||||
|
||||
if TELEGRAM_BOT:
|
||||
|
||||
# Display the category legend in a single line
|
||||
category_legend_str = "Categorie: \n" + " | ".join([
|
||||
f"{category.capitalize()}" for category in color_map.keys()
|
||||
])
|
||||
|
||||
# Costruisci il messaggio senza emoji
|
||||
# Build message with aliases
|
||||
prompt_message = "Inserisci il sito:\n" + "\n".join(
|
||||
[f"{key}: {label[0]}" for key, label in choice_labels.items()]
|
||||
)
|
||||
@ -356,7 +343,7 @@ def main(script_id = 0):
|
||||
category = bot.ask(
|
||||
"select_provider",
|
||||
f"{category_legend_str}\n\n{prompt_message}",
|
||||
None # Passiamo la lista delle chiavi come scelte
|
||||
None
|
||||
)
|
||||
|
||||
else:
|
||||
@ -379,10 +366,11 @@ def main(script_id = 0):
|
||||
|
||||
console.print("[red]Invalid category.")
|
||||
|
||||
if CLOSE_CONSOLE:
|
||||
restart_script() # Riavvia lo script invece di uscire
|
||||
if NOT_CLOSE_CONSOLE:
|
||||
restart_script()
|
||||
|
||||
else:
|
||||
force_exit() # Usa la funzione per chiudere sempre
|
||||
force_exit()
|
||||
|
||||
if TELEGRAM_BOT:
|
||||
bot.send_message(f"Chiusura in corso", None)
|
||||
@ -390,4 +378,4 @@ def main(script_id = 0):
|
||||
# Delete script_id
|
||||
script_id = TelegramSession.get_session()
|
||||
if script_id != "unknown":
|
||||
TelegramSession.deleteScriptId(script_id)
|
||||
TelegramSession.deleteScriptId(script_id)
|
@ -1,329 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary-color: #8c52ff;
|
||||
--secondary-color: #6930c3;
|
||||
--accent-color: #00e5ff;
|
||||
--background-color: #121212;
|
||||
--card-background: #1e1e1e;
|
||||
--text-color: #f8f9fa;
|
||||
--shadow-color: rgba(0, 0, 0, 0.25);
|
||||
--card-hover: #2a2a2a;
|
||||
--border-color: #333333;
|
||||
--header-bg: rgba(18, 18, 18, 0.95);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Segoe UI', sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--header-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 12px var(--shadow-color);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.site-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.site-item {
|
||||
min-height: 280px;
|
||||
background-color: var(--card-background);
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 6px 20px var(--shadow-color);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid var(--border-color);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
|
||||
}
|
||||
|
||||
.site-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 30px var(--shadow-color);
|
||||
}
|
||||
|
||||
.site-item img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 16px;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.site-content {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.site-item h3 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.domain {
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.site-item a {
|
||||
margin-top: 1rem;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
padding: 12px 28px;
|
||||
border-radius: 8px;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.site-item a:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--card-background);
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: auto;
|
||||
padding: 40px 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 30px;
|
||||
position: relative;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
transition: transform 0.3s ease, background-color 0.3s ease;
|
||||
background-color: var(--card-background);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.footer-section:hover {
|
||||
transform: translateY(-5px);
|
||||
background-color: var(--card-hover);
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
position: relative;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.footer-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s ease;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
opacity: 1;
|
||||
color: var(--accent-color);
|
||||
transform: translateX(8px);
|
||||
background-color: rgba(140, 82, 255, 0.1);
|
||||
}
|
||||
|
||||
.footer-links i {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover i {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.github-stats {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.github-badge {
|
||||
background-color: var(--background-color);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.github-badge i {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
margin-top: 15px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.update-info {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 30px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.update-note {
|
||||
color: var(--accent-color);
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-title::after {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 3px solid var(--border-color);
|
||||
border-top: 3px solid var(--primary-color);
|
||||
border-right: 3px solid var(--accent-color);
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.site-item {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.site-item img {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.old-domain, .time-change {
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--accent-color);
|
||||
font-weight: 500;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
const supabaseUrl = 'https://zvfngpoxwrgswnzytadh.supabase.co';
|
||||
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inp2Zm5ncG94d3Jnc3duenl0YWRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDAxNTIxNjMsImV4cCI6MjA1NTcyODE2M30.FNTCCMwi0QaKjOu8gtZsT5yQttUW8QiDDGXmzkn89QE';
|
||||
|
||||
async function loadSiteData() {
|
||||
try {
|
||||
const siteList = document.getElementById('site-list');
|
||||
const headers = {
|
||||
'apikey': supabaseKey,
|
||||
'Authorization': `Bearer ${supabaseKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
const response = await fetch(`${supabaseUrl}/rest/v1/public`, {
|
||||
method: 'GET',
|
||||
headers: headers
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
siteList.innerHTML = '';
|
||||
|
||||
if (data && data.length > 0) {
|
||||
const configSite = data[0].data;
|
||||
|
||||
for (const siteName in configSite) {
|
||||
const site = configSite[siteName];
|
||||
const siteItem = document.createElement('div');
|
||||
siteItem.className = 'site-item';
|
||||
|
||||
const siteIcon = document.createElement('img');
|
||||
siteIcon.src = `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${site.full_url}&size=128`;
|
||||
siteIcon.alt = `${siteName} icon`;
|
||||
siteIcon.onerror = function() {
|
||||
this.src = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="%238c52ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
|
||||
};
|
||||
|
||||
const siteContent = document.createElement('div');
|
||||
siteContent.className = 'site-content';
|
||||
|
||||
const siteTitle = document.createElement('h3');
|
||||
siteTitle.textContent = siteName;
|
||||
|
||||
if (site.old_domain) {
|
||||
const oldDomain = document.createElement('p');
|
||||
oldDomain.className = 'old-domain';
|
||||
oldDomain.innerHTML = `<span class="label">Previous domain:</span> ${site.old_domain.replace(/^https?:\/\//, '')}`;
|
||||
siteContent.appendChild(oldDomain);
|
||||
}
|
||||
|
||||
if (site.time_change) {
|
||||
const timeChange = document.createElement('p');
|
||||
timeChange.className = 'time-change';
|
||||
|
||||
const changeDate = new Date(site.time_change);
|
||||
const dateString = isNaN(changeDate) ? site.time_change : changeDate.toLocaleDateString();
|
||||
timeChange.innerHTML = `<span class="label">Updated:</span> ${dateString}`;
|
||||
siteContent.appendChild(timeChange);
|
||||
}
|
||||
|
||||
const siteLink = document.createElement('a');
|
||||
siteLink.href = site.full_url;
|
||||
siteLink.target = '_blank';
|
||||
siteLink.innerHTML = 'Visit <i class="fas fa-external-link-alt"></i>';
|
||||
siteLink.rel = 'noopener noreferrer';
|
||||
|
||||
siteContent.appendChild(siteTitle);
|
||||
siteContent.appendChild(siteLink);
|
||||
siteItem.appendChild(siteIcon);
|
||||
siteItem.appendChild(siteContent);
|
||||
siteList.appendChild(siteItem);
|
||||
}
|
||||
} else {
|
||||
siteList.innerHTML = '<div class="no-sites">No sites available</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Errore:', error);
|
||||
siteList.innerHTML = `
|
||||
<div class="error-message">
|
||||
<p>Errore nel caricamento</p>
|
||||
<button onclick="loadSiteData()" class="retry-button">Riprova</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', loadSiteData);
|
15
config.json
15
config.json
@ -24,11 +24,6 @@
|
||||
"user": "admin",
|
||||
"pass": "adminadmin"
|
||||
},
|
||||
"REQUESTS": {
|
||||
"verify": false,
|
||||
"timeout": 20,
|
||||
"max_retry": 8
|
||||
},
|
||||
"M3U8_DOWNLOAD": {
|
||||
"tqdm_delay": 0.01,
|
||||
"default_video_workser": 12,
|
||||
@ -59,11 +54,9 @@
|
||||
"force_resolution": "Best",
|
||||
"get_only_link": false
|
||||
},
|
||||
"SITE_EXTRA": {
|
||||
"ddlstreamitaly": {
|
||||
"ips4_device_key": "",
|
||||
"ips4_member_id": "",
|
||||
"ips4_login_key": ""
|
||||
}
|
||||
"REQUESTS": {
|
||||
"verify": false,
|
||||
"timeout": 20,
|
||||
"max_retry": 8
|
||||
}
|
||||
}
|
21
dockerfile
21
dockerfile
@ -1,20 +1,19 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
ENV TEMP /tmp
|
||||
RUN mkdir -p $TEMP
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
build-essential \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
python3-dev \
|
||||
libxml2-dev \
|
||||
libxslt1-dev
|
||||
libxslt1-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "test_run.py"]
|
Loading…
x
Reference in New Issue
Block a user