mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-03 10:00: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">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body> <main>
|
||||||
<main>
|
<section class="container"> <div class="header-container">
|
||||||
<section class="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 class="sites-container">
|
||||||
<div id="site-list" class="site-grid">
|
<div id="site-list" class="site-grid">
|
||||||
<div class="loader"></div>
|
<div class="loader"></div>
|
||||||
@ -21,8 +31,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<div class="footer-content">
|
<div class="footer-content"> <div class="footer-section">
|
||||||
<div class="footer-section">
|
|
||||||
<h3 class="footer-title">Repository</h3>
|
<h3 class="footer-title">Repository</h3>
|
||||||
<ul class="footer-links">
|
<ul class="footer-links">
|
||||||
<li>
|
<li>
|
||||||
@ -33,22 +42,22 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="footer-description">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-section">
|
<div class="footer-section">
|
||||||
<h3 class="footer-title">Author</h3>
|
<h3 class="footer-title">Support</h3>
|
||||||
<ul class="footer-links">
|
<ul class="footer-links">
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/Arrowar" target="_blank" rel="noopener noreferrer">
|
<a href="https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C" target="_blank" rel="noopener noreferrer">
|
||||||
<i class="fas fa-user-tie"></i>
|
<i class="fab fa-paypal"></i>
|
||||||
Arrowar Profile
|
Donate with PayPal
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="footer-description">
|
<p class="footer-description">
|
||||||
Developer of the project.
|
Support the development of this project through donations.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -71,4 +80,4 @@
|
|||||||
|
|
||||||
<script src="js/script.js"></script>
|
<script src="js/script.js"></script>
|
||||||
</body>
|
</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
|
# Get certifi certificate path
|
||||||
CERT_PATH=$(python -c "import certifi; print(certifi.where())")
|
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 \
|
pyinstaller --onefile --hidden-import=pycryptodomex --hidden-import=ua_generator \
|
||||||
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
||||||
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
||||||
@ -82,8 +90,8 @@ jobs:
|
|||||||
--hidden-import=Cryptodome.Random \
|
--hidden-import=Cryptodome.Random \
|
||||||
--hidden-import=telebot \
|
--hidden-import=telebot \
|
||||||
--additional-hooks-dir=pyinstaller/hooks \
|
--additional-hooks-dir=pyinstaller/hooks \
|
||||||
--add-data "${CERT_PATH};certifi" \
|
--add-data="$CERT_DATA" \
|
||||||
--add-data "StreamingCommunity${{ matrix.separator }}StreamingCommunity" \
|
--add-data="$SC_DATA" \
|
||||||
--name=${{ matrix.artifact_name }} test_run.py
|
--name=${{ matrix.artifact_name }} test_run.py
|
||||||
|
|
||||||
- name: Upload executable to latest release
|
- 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
|
# Get certifi certificate path
|
||||||
CERT_PATH=$(python -c "import certifi; print(certifi.where())")
|
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 \
|
pyinstaller --onefile --hidden-import=pycryptodomex --hidden-import=ua_generator \
|
||||||
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
|
||||||
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
|
||||||
@ -126,8 +134,8 @@ jobs:
|
|||||||
--hidden-import=Cryptodome.Random \
|
--hidden-import=Cryptodome.Random \
|
||||||
--hidden-import=telebot \
|
--hidden-import=telebot \
|
||||||
--additional-hooks-dir=pyinstaller/hooks \
|
--additional-hooks-dir=pyinstaller/hooks \
|
||||||
--add-data "${CERT_PATH};certifi" \
|
--add-data="$CERT_DATA" \
|
||||||
--add-data "StreamingCommunity${{ matrix.separator }}StreamingCommunity" \
|
--add-data="$SC_DATA" \
|
||||||
--name=${{ matrix.artifact_name }} test_run.py
|
--name=${{ matrix.artifact_name }} test_run.py
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
|
2
.github/workflows/pages.yml
vendored
2
.github/workflows/pages.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Copy site files
|
- name: Copy site files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p _site
|
mkdir -p _site
|
||||||
cp -r Test/.site/* _site/
|
cp -r .github/.site/* _site/
|
||||||
ls -la _site/
|
ls -la _site/
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
2
Makefile
2
Makefile
@ -2,4 +2,4 @@ build-container:
|
|||||||
docker build -t streaming-community-api .
|
docker build -t streaming-community-api .
|
||||||
|
|
||||||
run-container:
|
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
|
# 📋 Table of Contents
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>📦 Installation</summary>
|
||||||
|
|
||||||
- 🔄 [Update Domains](#update-domains)
|
- 🔄 [Update Domains](#update-domains)
|
||||||
- 🌐 [Available Sites](https://arrowar.github.io/StreamingDirectory/)
|
- 🌐 [Available Sites](https://arrowar.github.io/StreamingDirectory/)
|
||||||
- 🛠️ [Installation](#installation)
|
- 🛠️ [Installation](#installation)
|
||||||
@ -40,6 +43,11 @@
|
|||||||
- 📝 [Manual Installation](#3-manual-installation)
|
- 📝 [Manual Installation](#3-manual-installation)
|
||||||
- 💻 [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7)
|
- 💻 [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7)
|
||||||
- 📱 [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux)
|
- 📱 [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux)
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>⚙️ Configuration & Usage</summary>
|
||||||
|
|
||||||
- ⚙️ [Configuration](#configuration)
|
- ⚙️ [Configuration](#configuration)
|
||||||
- 🔧 [Default](#default-settings)
|
- 🔧 [Default](#default-settings)
|
||||||
- 📩 [Request](#requests-settings)
|
- 📩 [Request](#requests-settings)
|
||||||
@ -48,15 +56,23 @@
|
|||||||
- 📝 [Command](#command)
|
- 📝 [Command](#command)
|
||||||
- 🔍 [Global search](#global-search)
|
- 🔍 [Global search](#global-search)
|
||||||
- 💻 [Examples of terminal](#examples-of-terminal-usage)
|
- 💻 [Examples of terminal](#examples-of-terminal-usage)
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🔧 Advanced Features</summary>
|
||||||
|
|
||||||
- 🔧 [Manual domain configuration](#update-domains)
|
- 🔧 [Manual domain configuration](#update-domains)
|
||||||
- 🐳 [Docker](#docker)
|
- 🐳 [Docker](#docker)
|
||||||
- 📝 [Telegram Usage](#telegram-usage)
|
- 📝 [Telegram Usage](#telegram-usage)
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>ℹ️ Help & Support</summary>
|
||||||
|
|
||||||
- 🎓 [Tutorial](#tutorials)
|
- 🎓 [Tutorial](#tutorials)
|
||||||
- 📝 [To do](#to-do)
|
- 📝 [To do](#to-do)
|
||||||
- 💬 [Support](#support)
|
|
||||||
- 🤝 [Contribute](#contributing)
|
|
||||||
- ⚠️ [Disclaimer](#disclaimer)
|
- ⚠️ [Disclaimer](#disclaimer)
|
||||||
- ⚡ [Contributors](#contributors)
|
</details>
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@ -111,7 +127,8 @@ python run_streaming.py
|
|||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
### HLS Downloader
|
<details>
|
||||||
|
<summary>📥 HLS Downloader</summary>
|
||||||
|
|
||||||
Download HTTP Live Streaming (HLS) content from m3u8 URLs.
|
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.
|
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.
|
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.
|
See [MP4 example](./Test/Download/MP4.py) for complete usage.
|
||||||
|
</details>
|
||||||
|
|
||||||
### Torrent Client
|
<details>
|
||||||
|
<summary>🧲 Torrent Client</summary>
|
||||||
|
|
||||||
Download content via torrent magnet links.
|
Download content via torrent magnet links.
|
||||||
|
|
||||||
@ -178,67 +199,21 @@ client.start_download()
|
|||||||
```
|
```
|
||||||
|
|
||||||
See [Torrent example](./Test/Download/TOR.py) for complete usage.
|
See [Torrent example](./Test/Download/TOR.py) for complete usage.
|
||||||
|
</details>
|
||||||
|
|
||||||
## 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
|
|
||||||
```
|
|
||||||
|
|
||||||
## Binary Location
|
## Binary Location
|
||||||
|
|
||||||
### Default Locations
|
<details>
|
||||||
|
<summary>📂 Default Locations</summary>
|
||||||
|
|
||||||
- **Windows**: `C:\binary`
|
- **Windows**: `C:\binary`
|
||||||
- **MacOS**: `~/Applications/binary`
|
- **MacOS**: `~/Applications/binary`
|
||||||
- **Linux**: `~/.local/bin/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
|
1. Move the binary folder from `C:\binary` to your desired location
|
||||||
2. Add the new path to Windows environment variables:
|
2. Add the new path to Windows environment variables:
|
||||||
- Open Start menu and search for "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
|
- 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/).
|
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
|
1. Move the binary folder from `~/Applications/binary` to your desired location
|
||||||
2. Add the new path to your shell's configuration file:
|
2. Add the new path to your shell's configuration file:
|
||||||
```bash
|
```bash
|
||||||
@ -269,8 +247,11 @@ For detailed Windows PATH instructions, see the [Windows PATH guide](https://www
|
|||||||
# For zsh
|
# For zsh
|
||||||
source ~/.zshrc
|
source ~/.zshrc
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🐧 Linux Configuration</summary>
|
||||||
|
|
||||||
#### Linux
|
|
||||||
1. Move the binary folder from `~/.local/bin/binary` to your desired location
|
1. Move the binary folder from `~/.local/bin/binary` to your desired location
|
||||||
2. Add the new path to your shell's configuration file:
|
2. Add the new path to your shell's configuration file:
|
||||||
```bash
|
```bash
|
||||||
@ -286,6 +267,7 @@ For detailed Windows PATH instructions, see the [Windows PATH guide](https://www
|
|||||||
# or
|
# or
|
||||||
source ~/.zshrc # for zsh
|
source ~/.zshrc # for zsh
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> After moving the binary folder, ensure that all executables (ffmpeg, ffprobe, ffplay) are present in the new location and have the correct permissions:
|
> 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
|
## 3. Manual Installation
|
||||||
|
|
||||||
### Requirements 📋
|
<details>
|
||||||
|
<summary>📋 Requirements</summary>
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
* [Python](https://www.python.org/downloads/) > 3.8
|
* [Python](https://www.python.org/downloads/) > 3.8
|
||||||
* [FFmpeg](https://www.gyan.dev/ffmpeg/builds/)
|
* [FFmpeg](https://www.gyan.dev/ffmpeg/builds/)
|
||||||
|
</details>
|
||||||
|
|
||||||
### Install Python Dependencies
|
<details>
|
||||||
|
<summary>⚙️ Python Dependencies</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Usage
|
<details>
|
||||||
|
<summary>🚀 Usage</summary>
|
||||||
|
|
||||||
#### On Windows:
|
#### On Windows:
|
||||||
|
|
||||||
@ -319,6 +306,7 @@ python test_run.py
|
|||||||
```bash
|
```bash
|
||||||
python3 test_run.py
|
python3 test_run.py
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
## Update
|
## Update
|
||||||
|
|
||||||
@ -338,278 +326,11 @@ python3 update.py
|
|||||||
|
|
||||||
<br>
|
<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
|
## Update Domains
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🌐 Domain Configuration Methods</summary>
|
||||||
|
|
||||||
There are two ways to update the domains for the supported websites:
|
There are two ways to update the domains for the supported websites:
|
||||||
|
|
||||||
### 1. Using Local Configuration
|
### 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
|
#### 💡 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!
|
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
|
# 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.
|
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
|
<details>
|
||||||
|
<summary>🎯 Search Options</summary>
|
||||||
The Global Search feature provides a unified interface to search across all supported sites:
|
|
||||||
|
|
||||||
## Search Options
|
|
||||||
|
|
||||||
When using Global Search, you have three ways to select which sites to search:
|
When using Global Search, you have three ways to select which sites to search:
|
||||||
|
|
||||||
1. **Search all sites** - Searches across all available streaming sites
|
1. **Search all sites** - Searches across all available streaming sites
|
||||||
2. **Search by category** - Group sites by their categories (movies, series, anime, etc.)
|
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
|
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:
|
After performing a search:
|
||||||
|
|
||||||
@ -673,13 +652,16 @@ After performing a search:
|
|||||||
2. Select an item by number to view details or download
|
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
|
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:
|
The Global Search can be configured from the command line:
|
||||||
|
|
||||||
- `--global` - Perform a global search across multiple sites.
|
- `--global` - Perform a global search across multiple sites.
|
||||||
- `-s`, `--search` - Specify the search terms.
|
- `-s`, `--search` - Specify the search terms.
|
||||||
|
</details>
|
||||||
|
|
||||||
# Examples of terminal usage
|
# Examples of terminal usage
|
||||||
|
|
||||||
@ -699,25 +681,32 @@ python test_run.py --global -s "cars"
|
|||||||
|
|
||||||
# Docker
|
# 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 .
|
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
|
<details>
|
||||||
|
<summary>🛠️ Quick Setup with Make</summary>
|
||||||
```
|
|
||||||
docker run -it -p 8000:8000 -v /path/to/download:/app/Video streaming-community-api
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker quick setup with Make
|
|
||||||
|
|
||||||
Inside the Makefile (install `make`) are already configured two commands to build and run the container:
|
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.
|
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
|
# 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.
|
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.
|
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:
|
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.
|
🔹 /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:
|
🔹 /list – Displays the status of active downloads, with options to:
|
||||||
|
- Stop an incorrect download using /stop <ID>
|
||||||
Stop an incorrect download using /stop <ID>.
|
- View the real-time output of a download using /screen <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.
|
⚠ 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:
|
Create an `.env` file with:
|
||||||
|
|
||||||
You need to create an .env file and enter your Telegram token and user ID to authorize only one user to use it
|
|
||||||
|
|
||||||
```
|
```
|
||||||
TOKEN_TELEGRAM=IlTuo2131TOKEN$12D3Telegram
|
TOKEN_TELEGRAM=IlTuo2131TOKEN$12D3Telegram
|
||||||
AUTHORIZED_USER_ID=12345678
|
AUTHORIZED_USER_ID=12345678
|
||||||
DEBUG=False
|
DEBUG=False
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
## Install Python Dependencies
|
<details>
|
||||||
|
<summary>📥 Dependencies & Launch</summary>
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## On Linux/MacOS:
|
Start the bot (from /StreamingCommunity/TelegramHelp):
|
||||||
|
|
||||||
Start the bot from the folder /StreamingCommunity/TelegramHelp
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 telegram_bot.py
|
python3 telegram_bot.py
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
# Tutorials
|
# 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](https://github.com/Arrowar/StreamingCommunity/tree/test_gui_1)
|
||||||
- To finish [website API 2](https://github.com/hydrosh/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
|
## Useful Project
|
||||||
|
|
||||||
### 🎯 [Unit3Dup](https://github.com/31December99/Unit3Dup)
|
### 🎯 [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)
|
### 🎥 [stream-buddy](https://github.com/Bbalduzz/stream-buddy)
|
||||||
Tool per guardare o scaricare film dalla piattaforma StreamingCommunity.
|
Tool per guardare o scaricare film dalla piattaforma StreamingCommunity.
|
||||||
|
|
||||||
## Contributors
|
# Disclaimer
|
||||||
|
|
||||||
<a href="https://github.com/Arrowar/StreamingCommunity/graphs/contributors" alt="View Contributors">
|
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.
|
||||||
<img src="https://contrib.rocks/image?repo=Arrowar/StreamingCommunity&max=1000&columns=10" alt="Contributors" />
|
|
||||||
</a>
|
|
@ -24,6 +24,7 @@ indice = 3
|
|||||||
_useFor = "film_serie"
|
_useFor = "film_serie"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "tor"
|
_engineDownload = "tor"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
|
@ -27,6 +27,7 @@ indice = 2
|
|||||||
_useFor = "film_serie"
|
_useFor = "film_serie"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "hls"
|
_engineDownload = "hls"
|
||||||
|
_deprecate = True
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -27,6 +27,7 @@ indice = 1
|
|||||||
_useFor = "anime"
|
_useFor = "anime"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "mp4"
|
_engineDownload = "mp4"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -22,6 +22,7 @@ indice = 8
|
|||||||
_useFor = "anime"
|
_useFor = "anime"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "mp4"
|
_engineDownload = "mp4"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
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"
|
_useFor = "serie"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "hls"
|
_engineDownload = "hls"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -23,6 +23,7 @@ indice = 8
|
|||||||
_useFor = "film_serie"
|
_useFor = "film_serie"
|
||||||
_priority = 1 # NOTE: Site search need the use of tmbd obj
|
_priority = 1 # NOTE: Site search need the use of tmbd obj
|
||||||
_engineDownload = "hls"
|
_engineDownload = "hls"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -28,6 +28,7 @@ indice = 0
|
|||||||
_useFor = "film_serie"
|
_useFor = "film_serie"
|
||||||
_priority = 0
|
_priority = 0
|
||||||
_engineDownload = "hls"
|
_engineDownload = "hls"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -23,6 +23,7 @@ indice = 8
|
|||||||
_useFor = "film_serie"
|
_useFor = "film_serie"
|
||||||
_priority = 10 # !!! MOLTO LENTO
|
_priority = 10 # !!! MOLTO LENTO
|
||||||
_engineDownload = "hls"
|
_engineDownload = "hls"
|
||||||
|
_deprecate = False
|
||||||
|
|
||||||
msg = Prompt()
|
msg = Prompt()
|
||||||
console = Console()
|
console = Console()
|
||||||
|
@ -166,7 +166,6 @@ class ConfigManager:
|
|||||||
raise Exception(f"Error downloading reference configuration. Code: {response.status_code}")
|
raise Exception(f"Error downloading reference configuration. Code: {response.status_code}")
|
||||||
|
|
||||||
reference_config = response.json()
|
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
|
# Compare and update missing keys
|
||||||
merged_config = self._deep_merge_configs(self.config, reference_config)
|
merged_config = self._deep_merge_configs(self.config, reference_config)
|
||||||
@ -285,7 +284,6 @@ class ConfigManager:
|
|||||||
self.configSite = data[0]['data']
|
self.configSite = data[0]['data']
|
||||||
|
|
||||||
site_count = len(self.configSite) if isinstance(self.configSite, dict) else 0
|
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:
|
else:
|
||||||
console.print("[bold yellow]API returned an empty data set[/bold yellow]")
|
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)
|
priority = getattr(mod, '_priority', 0)
|
||||||
|
|
||||||
if 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:
|
except Exception as e:
|
||||||
console.print(f"[red]Failed to import module {module_name}: {str(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
|
# Config
|
||||||
SHOW_TRENDING = config_manager.get_bool('DEFAULT', 'show_trending')
|
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')
|
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ def load_search_functions():
|
|||||||
loaded_functions = {}
|
loaded_functions = {}
|
||||||
|
|
||||||
# Lista dei siti da escludere se TELEGRAM_BOT è attivo
|
# 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
|
# Find api home directory
|
||||||
if getattr(sys, 'frozen', False): # Modalità PyInstaller
|
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}')
|
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
|
||||||
|
|
||||||
# Get 'indice' from the module
|
# Get 'indice' from the module
|
||||||
indice = getattr(mod, 'indice', 0)
|
indice = getattr(mod, 'indice')
|
||||||
use_for = getattr(mod, '_useFor', 'other')
|
use_for = getattr(mod, '_useFor')
|
||||||
modules.append((module_name, indice, use_for))
|
|
||||||
|
if not getattr(mod, '_deprecate'):
|
||||||
|
modules.append((module_name, indice, use_for))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
|
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
|
||||||
@ -202,13 +204,16 @@ def main(script_id = 0):
|
|||||||
initialize()
|
initialize()
|
||||||
|
|
||||||
if not internet_manager.check_dns_provider():
|
if not internet_manager.check_dns_provider():
|
||||||
|
print()
|
||||||
console.print("[red]❌ ERROR: DNS configuration is required!")
|
console.print("[red]❌ ERROR: DNS configuration is required!")
|
||||||
console.print("[red]The program cannot function correctly without proper DNS settings.")
|
console.print("[red]The program cannot function correctly without proper DNS settings.")
|
||||||
console.print("[yellow]Please configure one of these DNS servers:")
|
console.print("[yellow]Please configure one of these DNS servers:")
|
||||||
console.print("[blue]• Cloudflare (1.1.1.1)")
|
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)")
|
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.")
|
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
|
# Load search functions
|
||||||
search_functions = load_search_functions()
|
search_functions = load_search_functions()
|
||||||
@ -259,21 +264,9 @@ def main(script_id = 0):
|
|||||||
"other": "white"
|
"other": "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add dynamic arguments based on loaded search modules
|
# Add numeric arguments for each search module
|
||||||
used_short_options = set()
|
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})')
|
||||||
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.')
|
|
||||||
|
|
||||||
parser.add_argument('-s', '--search', default=None, help='Search terms')
|
parser.add_argument('-s', '--search', default=None, help='Search terms')
|
||||||
|
|
||||||
@ -309,12 +302,11 @@ def main(script_id = 0):
|
|||||||
global_search(search_terms)
|
global_search(search_terms)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Map command-line arguments to functions
|
# Check for numeric arguments
|
||||||
arg_to_function = {alias: func for alias, (func, _) in search_functions.items()}
|
search_functions_list = list(search_functions.items())
|
||||||
|
for i in range(len(search_functions_list)):
|
||||||
# Check which argument is provided and run the corresponding function
|
if getattr(args, str(i)):
|
||||||
for arg, func in arg_to_function.items():
|
alias, (func, _) = search_functions_list[i]
|
||||||
if getattr(args, arg):
|
|
||||||
run_function(func, search_terms=search_terms)
|
run_function(func, search_terms=search_terms)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -324,28 +316,23 @@ def main(script_id = 0):
|
|||||||
# Create dynamic prompt message and choices
|
# 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())}
|
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
|
# Display the category legend in a single line
|
||||||
legend_text = " | ".join([f"[{color}]{category.capitalize()}[/{color}]" for category, color in color_map.items()])
|
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}")
|
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(
|
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])"
|
) + "[white])"
|
||||||
|
|
||||||
if TELEGRAM_BOT:
|
if TELEGRAM_BOT:
|
||||||
|
|
||||||
# Display the category legend in a single line
|
# Display the category legend in a single line
|
||||||
category_legend_str = "Categorie: \n" + " | ".join([
|
category_legend_str = "Categorie: \n" + " | ".join([
|
||||||
f"{category.capitalize()}" for category in color_map.keys()
|
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(
|
prompt_message = "Inserisci il sito:\n" + "\n".join(
|
||||||
[f"{key}: {label[0]}" for key, label in choice_labels.items()]
|
[f"{key}: {label[0]}" for key, label in choice_labels.items()]
|
||||||
)
|
)
|
||||||
@ -356,7 +343,7 @@ def main(script_id = 0):
|
|||||||
category = bot.ask(
|
category = bot.ask(
|
||||||
"select_provider",
|
"select_provider",
|
||||||
f"{category_legend_str}\n\n{prompt_message}",
|
f"{category_legend_str}\n\n{prompt_message}",
|
||||||
None # Passiamo la lista delle chiavi come scelte
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -379,10 +366,11 @@ def main(script_id = 0):
|
|||||||
|
|
||||||
console.print("[red]Invalid category.")
|
console.print("[red]Invalid category.")
|
||||||
|
|
||||||
if CLOSE_CONSOLE:
|
if NOT_CLOSE_CONSOLE:
|
||||||
restart_script() # Riavvia lo script invece di uscire
|
restart_script()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
force_exit() # Usa la funzione per chiudere sempre
|
force_exit()
|
||||||
|
|
||||||
if TELEGRAM_BOT:
|
if TELEGRAM_BOT:
|
||||||
bot.send_message(f"Chiusura in corso", None)
|
bot.send_message(f"Chiusura in corso", None)
|
||||||
@ -390,4 +378,4 @@ def main(script_id = 0):
|
|||||||
# Delete script_id
|
# Delete script_id
|
||||||
script_id = TelegramSession.get_session()
|
script_id = TelegramSession.get_session()
|
||||||
if script_id != "unknown":
|
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",
|
"user": "admin",
|
||||||
"pass": "adminadmin"
|
"pass": "adminadmin"
|
||||||
},
|
},
|
||||||
"REQUESTS": {
|
|
||||||
"verify": false,
|
|
||||||
"timeout": 20,
|
|
||||||
"max_retry": 8
|
|
||||||
},
|
|
||||||
"M3U8_DOWNLOAD": {
|
"M3U8_DOWNLOAD": {
|
||||||
"tqdm_delay": 0.01,
|
"tqdm_delay": 0.01,
|
||||||
"default_video_workser": 12,
|
"default_video_workser": 12,
|
||||||
@ -59,11 +54,9 @@
|
|||||||
"force_resolution": "Best",
|
"force_resolution": "Best",
|
||||||
"get_only_link": false
|
"get_only_link": false
|
||||||
},
|
},
|
||||||
"SITE_EXTRA": {
|
"REQUESTS": {
|
||||||
"ddlstreamitaly": {
|
"verify": false,
|
||||||
"ips4_device_key": "",
|
"timeout": 20,
|
||||||
"ips4_member_id": "",
|
"max_retry": 8
|
||||||
"ips4_login_key": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
21
dockerfile
21
dockerfile
@ -1,20 +1,19 @@
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
|
|
||||||
COPY . /app
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV TEMP /tmp
|
|
||||||
RUN mkdir -p $TEMP
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
build-essential \
|
build-essential \
|
||||||
libssl-dev \
|
|
||||||
libffi-dev \
|
|
||||||
python3-dev \
|
|
||||||
libxml2-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
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
CMD ["python", "test_run.py"]
|
CMD ["python", "test_run.py"]
|
2
setup.py
2
setup.py
@ -29,4 +29,4 @@ setup(
|
|||||||
"Bug Reports": "https://github.com/Lovi-0/StreamingCommunity/issues",
|
"Bug Reports": "https://github.com/Lovi-0/StreamingCommunity/issues",
|
||||||
"Source": "https://github.com/Lovi-0/StreamingCommunity",
|
"Source": "https://github.com/Lovi-0/StreamingCommunity",
|
||||||
}
|
}
|
||||||
)
|
)
|
Loading…
x
Reference in New Issue
Block a user