mirror of
https://github.com/Viren070/MediaFusion.git
synced 2025-12-01 23:21:11 +01:00
Improve Scraper Control UI, Improve torrent specification selection, Fix date selection type & Add support for exclude files annotation in series import
This commit is contained in:
@@ -0,0 +1,795 @@
|
||||
.flatpickr-calendar {
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
text-align: center;
|
||||
visibility: hidden;
|
||||
padding: 0;
|
||||
-webkit-animation: none;
|
||||
animation: none;
|
||||
direction: ltr;
|
||||
border: 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-ms-touch-action: manipulation;
|
||||
touch-action: manipulation;
|
||||
background: #3f4458;
|
||||
-webkit-box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08);
|
||||
box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08);
|
||||
}
|
||||
.flatpickr-calendar.open,
|
||||
.flatpickr-calendar.inline {
|
||||
opacity: 1;
|
||||
max-height: 640px;
|
||||
visibility: visible;
|
||||
}
|
||||
.flatpickr-calendar.open {
|
||||
display: inline-block;
|
||||
z-index: 99999;
|
||||
}
|
||||
.flatpickr-calendar.animate.open {
|
||||
-webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.flatpickr-calendar.inline {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.flatpickr-calendar.static {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
}
|
||||
.flatpickr-calendar.static.open {
|
||||
z-index: 999;
|
||||
display: block;
|
||||
}
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7) {
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) {
|
||||
-webkit-box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
}
|
||||
.flatpickr-calendar .hasWeeks .dayContainer,
|
||||
.flatpickr-calendar .hasTime .dayContainer {
|
||||
border-bottom: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.flatpickr-calendar .hasWeeks .dayContainer {
|
||||
border-left: 0;
|
||||
}
|
||||
.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
height: 40px;
|
||||
border-top: 1px solid #20222c;
|
||||
}
|
||||
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
|
||||
height: auto;
|
||||
}
|
||||
.flatpickr-calendar:before,
|
||||
.flatpickr-calendar:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
border: solid transparent;
|
||||
content: '';
|
||||
height: 0;
|
||||
width: 0;
|
||||
left: 22px;
|
||||
}
|
||||
.flatpickr-calendar.rightMost:before,
|
||||
.flatpickr-calendar.arrowRight:before,
|
||||
.flatpickr-calendar.rightMost:after,
|
||||
.flatpickr-calendar.arrowRight:after {
|
||||
left: auto;
|
||||
right: 22px;
|
||||
}
|
||||
.flatpickr-calendar.arrowCenter:before,
|
||||
.flatpickr-calendar.arrowCenter:after {
|
||||
left: 50%;
|
||||
right: 50%;
|
||||
}
|
||||
.flatpickr-calendar:before {
|
||||
border-width: 5px;
|
||||
margin: 0 -5px;
|
||||
}
|
||||
.flatpickr-calendar:after {
|
||||
border-width: 4px;
|
||||
margin: 0 -4px;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:before,
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
bottom: 100%;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:before {
|
||||
border-bottom-color: #20222c;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
border-bottom-color: #3f4458;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:before,
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
top: 100%;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:before {
|
||||
border-top-color: #20222c;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
border-top-color: #3f4458;
|
||||
}
|
||||
.flatpickr-calendar:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.flatpickr-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.flatpickr-months {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.flatpickr-months .flatpickr-month {
|
||||
background: #3f4458;
|
||||
color: #fff;
|
||||
fill: #fff;
|
||||
height: 34px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 34px;
|
||||
padding: 10px;
|
||||
z-index: 3;
|
||||
color: #fff;
|
||||
fill: #fff;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-disabled {
|
||||
display: none;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month i,
|
||||
.flatpickr-months .flatpickr-next-month i {
|
||||
position: relative;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
*/
|
||||
left: 0;
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
/*
|
||||
*/
|
||||
}
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-next-month {
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
*/
|
||||
right: 0;
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
/*
|
||||
*/
|
||||
}
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
.flatpickr-months .flatpickr-prev-month:hover,
|
||||
.flatpickr-months .flatpickr-next-month:hover {
|
||||
color: #eee;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: #f64747;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month svg,
|
||||
.flatpickr-months .flatpickr-next-month svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month svg path,
|
||||
.flatpickr-months .flatpickr-next-month svg path {
|
||||
-webkit-transition: fill 0.1s;
|
||||
transition: fill 0.1s;
|
||||
fill: inherit;
|
||||
}
|
||||
.numInputWrapper {
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
.numInputWrapper input,
|
||||
.numInputWrapper span {
|
||||
display: inline-block;
|
||||
}
|
||||
.numInputWrapper input {
|
||||
width: 100%;
|
||||
}
|
||||
.numInputWrapper input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.numInputWrapper input::-webkit-outer-spin-button,
|
||||
.numInputWrapper input::-webkit-inner-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.numInputWrapper span {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 14px;
|
||||
padding: 0 4px 0 2px;
|
||||
height: 50%;
|
||||
line-height: 50%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.numInputWrapper span:hover {
|
||||
background: rgba(192,187,167,0.1);
|
||||
}
|
||||
.numInputWrapper span:active {
|
||||
background: rgba(192,187,167,0.2);
|
||||
}
|
||||
.numInputWrapper span:after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
.numInputWrapper span.arrowUp {
|
||||
top: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.numInputWrapper span.arrowUp:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid rgba(255,255,255,0.6);
|
||||
top: 26%;
|
||||
}
|
||||
.numInputWrapper span.arrowDown {
|
||||
top: 50%;
|
||||
}
|
||||
.numInputWrapper span.arrowDown:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid rgba(255,255,255,0.6);
|
||||
top: 40%;
|
||||
}
|
||||
.numInputWrapper span svg {
|
||||
width: inherit;
|
||||
height: auto;
|
||||
}
|
||||
.numInputWrapper span svg path {
|
||||
fill: rgba(255,255,255,0.5);
|
||||
}
|
||||
.numInputWrapper:hover {
|
||||
background: rgba(192,187,167,0.05);
|
||||
}
|
||||
.numInputWrapper:hover span {
|
||||
opacity: 1;
|
||||
}
|
||||
.flatpickr-current-month {
|
||||
font-size: 135%;
|
||||
line-height: inherit;
|
||||
font-weight: 300;
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
left: 12.5%;
|
||||
padding: 7.48px 0 0 0;
|
||||
line-height: 1;
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
-webkit-transform: translate3d(0px, 0px, 0px);
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
.flatpickr-current-month span.cur-month {
|
||||
font-family: inherit;
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
margin-left: 0.5ch;
|
||||
padding: 0;
|
||||
}
|
||||
.flatpickr-current-month span.cur-month:hover {
|
||||
background: rgba(192,187,167,0.05);
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper {
|
||||
width: 6ch;
|
||||
width: 7ch\0;
|
||||
display: inline-block;
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.flatpickr-current-month input.cur-year {
|
||||
background: transparent;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: text;
|
||||
padding: 0 0 0 0.5ch;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
line-height: inherit;
|
||||
height: auto;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
vertical-align: initial;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
.flatpickr-current-month input.cur-year:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.flatpickr-current-month input.cur-year[disabled],
|
||||
.flatpickr-current-month input.cur-year[disabled]:hover {
|
||||
font-size: 100%;
|
||||
color: rgba(255,255,255,0.5);
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||
appearance: menulist;
|
||||
background: #3f4458;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
margin: -1px 0 0 0;
|
||||
outline: none;
|
||||
padding: 0 0 0 0.5ch;
|
||||
position: relative;
|
||||
vertical-align: initial;
|
||||
-webkit-box-sizing: border-box;
|
||||
-webkit-appearance: menulist;
|
||||
-moz-appearance: menulist;
|
||||
width: auto;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:focus,
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:active {
|
||||
outline: none;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: rgba(192,187,167,0.05);
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
|
||||
background-color: #3f4458;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
.flatpickr-weekdays {
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
}
|
||||
.flatpickr-weekdays .flatpickr-weekdaycontainer {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
span.flatpickr-weekday {
|
||||
cursor: default;
|
||||
font-size: 90%;
|
||||
background: #3f4458;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.dayContainer,
|
||||
.flatpickr-weeks {
|
||||
padding: 1px 0 0 0;
|
||||
}
|
||||
.flatpickr-days {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
width: 307.875px;
|
||||
}
|
||||
.flatpickr-days:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.dayContainer {
|
||||
padding: 0;
|
||||
outline: 0;
|
||||
text-align: left;
|
||||
width: 307.875px;
|
||||
min-width: 307.875px;
|
||||
max-width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
-webkit-transform: translate3d(0px, 0px, 0px);
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
opacity: 1;
|
||||
}
|
||||
.dayContainer + .dayContainer {
|
||||
-webkit-box-shadow: -1px 0 0 #20222c;
|
||||
box-shadow: -1px 0 0 #20222c;
|
||||
}
|
||||
.flatpickr-day {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 150px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: rgba(255,255,255,0.95);
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
width: 14.2857143%;
|
||||
-webkit-flex-basis: 14.2857143%;
|
||||
-ms-flex-preferred-size: 14.2857143%;
|
||||
flex-basis: 14.2857143%;
|
||||
max-width: 39px;
|
||||
height: 39px;
|
||||
line-height: 39px;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
.flatpickr-day.inRange,
|
||||
.flatpickr-day.prevMonthDay.inRange,
|
||||
.flatpickr-day.nextMonthDay.inRange,
|
||||
.flatpickr-day.today.inRange,
|
||||
.flatpickr-day.prevMonthDay.today.inRange,
|
||||
.flatpickr-day.nextMonthDay.today.inRange,
|
||||
.flatpickr-day:hover,
|
||||
.flatpickr-day.prevMonthDay:hover,
|
||||
.flatpickr-day.nextMonthDay:hover,
|
||||
.flatpickr-day:focus,
|
||||
.flatpickr-day.prevMonthDay:focus,
|
||||
.flatpickr-day.nextMonthDay:focus {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
background: #646c8c;
|
||||
border-color: #646c8c;
|
||||
}
|
||||
.flatpickr-day.today {
|
||||
border-color: #eee;
|
||||
}
|
||||
.flatpickr-day.today:hover,
|
||||
.flatpickr-day.today:focus {
|
||||
border-color: #eee;
|
||||
background: #eee;
|
||||
color: #3f4458;
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: #80cbc4;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
border-color: #80cbc4;
|
||||
}
|
||||
.flatpickr-day.selected.startRange,
|
||||
.flatpickr-day.startRange.startRange,
|
||||
.flatpickr-day.endRange.startRange {
|
||||
border-radius: 50px 0 0 50px;
|
||||
}
|
||||
.flatpickr-day.selected.endRange,
|
||||
.flatpickr-day.startRange.endRange,
|
||||
.flatpickr-day.endRange.endRange {
|
||||
border-radius: 0 50px 50px 0;
|
||||
}
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
|
||||
-webkit-box-shadow: -10px 0 0 #80cbc4;
|
||||
box-shadow: -10px 0 0 #80cbc4;
|
||||
}
|
||||
.flatpickr-day.selected.startRange.endRange,
|
||||
.flatpickr-day.startRange.startRange.endRange,
|
||||
.flatpickr-day.endRange.startRange.endRange {
|
||||
border-radius: 50px;
|
||||
}
|
||||
.flatpickr-day.inRange {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c;
|
||||
box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c;
|
||||
}
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover,
|
||||
.flatpickr-day.prevMonthDay,
|
||||
.flatpickr-day.nextMonthDay,
|
||||
.flatpickr-day.notAllowed,
|
||||
.flatpickr-day.notAllowed.prevMonthDay,
|
||||
.flatpickr-day.notAllowed.nextMonthDay {
|
||||
color: rgba(255,255,255,0.3);
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover {
|
||||
cursor: not-allowed;
|
||||
color: rgba(255,255,255,0.1);
|
||||
}
|
||||
.flatpickr-day.week.selected {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4;
|
||||
box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4;
|
||||
}
|
||||
.flatpickr-day.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
.rangeMode .flatpickr-day {
|
||||
margin-top: 1px;
|
||||
}
|
||||
.flatpickr-weekwrapper {
|
||||
float: left;
|
||||
}
|
||||
.flatpickr-weekwrapper .flatpickr-weeks {
|
||||
padding: 0 12px;
|
||||
-webkit-box-shadow: 1px 0 0 #20222c;
|
||||
box-shadow: 1px 0 0 #20222c;
|
||||
}
|
||||
.flatpickr-weekwrapper .flatpickr-weekday {
|
||||
float: none;
|
||||
width: 100%;
|
||||
line-height: 28px;
|
||||
}
|
||||
.flatpickr-weekwrapper span.flatpickr-day,
|
||||
.flatpickr-weekwrapper span.flatpickr-day:hover {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
color: rgba(255,255,255,0.3);
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
border: none;
|
||||
}
|
||||
.flatpickr-innerContainer {
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.flatpickr-rContainer {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.flatpickr-time {
|
||||
text-align: center;
|
||||
outline: 0;
|
||||
display: block;
|
||||
height: 0;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.flatpickr-time:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
.flatpickr-time .numInputWrapper {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
width: 40%;
|
||||
height: 40px;
|
||||
float: left;
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: rgba(255,255,255,0.95);
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: rgba(255,255,255,0.95);
|
||||
}
|
||||
.flatpickr-time.hasSeconds .numInputWrapper {
|
||||
width: 26%;
|
||||
}
|
||||
.flatpickr-time.time24hr .numInputWrapper {
|
||||
width: 49%;
|
||||
}
|
||||
.flatpickr-time input {
|
||||
background: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
color: rgba(255,255,255,0.95);
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
.flatpickr-time input.flatpickr-hour {
|
||||
font-weight: bold;
|
||||
}
|
||||
.flatpickr-time input.flatpickr-minute,
|
||||
.flatpickr-time input.flatpickr-second {
|
||||
font-weight: 400;
|
||||
}
|
||||
.flatpickr-time input:focus {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
.flatpickr-time .flatpickr-time-separator,
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
height: inherit;
|
||||
float: left;
|
||||
line-height: inherit;
|
||||
color: rgba(255,255,255,0.95);
|
||||
font-weight: bold;
|
||||
width: 2%;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-align-self: center;
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
outline: 0;
|
||||
width: 18%;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
.flatpickr-time input:hover,
|
||||
.flatpickr-time .flatpickr-am-pm:hover,
|
||||
.flatpickr-time input:focus,
|
||||
.flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: #6a7395;
|
||||
}
|
||||
.flatpickr-input[readonly] {
|
||||
cursor: pointer;
|
||||
}
|
||||
@-webkit-keyframes fpFadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -20px, 0);
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
@keyframes fpFadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -20px, 0);
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
Vendored
+13
File diff suppressed because one or more lines are too long
+66
-11
@@ -141,15 +141,15 @@ body, html {
|
||||
.list-group-item-action {
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid rgba(74, 71, 163, 0.2);
|
||||
background: rgba(30, 30, 50, 0.95); /* Slightly more opaque */
|
||||
color: #ffffff; /* Bright white for main text */
|
||||
background: rgba(30, 30, 50, 0.95); /* Slightly more opaque */
|
||||
color: #ffffff; /* Bright white for main text */
|
||||
}
|
||||
|
||||
.list-group-item-action:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(74, 71, 163, 0.4);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
background: rgba(40, 40, 60, 0.98); /* Even more opaque on hover */
|
||||
background: rgba(40, 40, 60, 0.98); /* Even more opaque on hover */
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
@@ -317,29 +317,29 @@ body, html {
|
||||
}
|
||||
|
||||
.list-group-item h5 .text-muted {
|
||||
color: #bdbdbd !important; /* Lighter gray for year */
|
||||
color: #bdbdbd !important; /* Lighter gray for year */
|
||||
}
|
||||
|
||||
/* Description and other text */
|
||||
.list-group-item .text-muted {
|
||||
color: #bdbdbd !important; /* Lighter gray for better readability */
|
||||
color: #bdbdbd !important; /* Lighter gray for better readability */
|
||||
}
|
||||
|
||||
/* Meta information */
|
||||
.list-group-item .small,
|
||||
.list-group-item small {
|
||||
color: #e0e0e0; /* Light gray for small text */
|
||||
color: #e0e0e0; /* Light gray for small text */
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
.list-group-item i {
|
||||
color: #4a47a3; /* Accent color for icons */
|
||||
color: #4a47a3; /* Accent color for icons */
|
||||
}
|
||||
|
||||
/* Badge Styles - Improve readability */
|
||||
.badge.bg-info {
|
||||
background-color: rgba(74, 71, 163, 0.25) !important; /* Lighter background */
|
||||
color: #ffffff !important; /* White text for contrast */
|
||||
background-color: rgba(74, 71, 163, 0.25) !important; /* Lighter background */
|
||||
color: #ffffff !important; /* White text for contrast */
|
||||
border: 1px solid rgba(74, 71, 163, 0.4);
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -383,7 +383,7 @@ body, html {
|
||||
|
||||
/* Meta sections */
|
||||
.list-group-item .d-flex.align-items-center {
|
||||
color: #e0e0e0; /* Light gray for meta information */
|
||||
color: #e0e0e0; /* Light gray for meta information */
|
||||
}
|
||||
|
||||
/* Content type labels (Comedy, Romance, etc.) */
|
||||
@@ -399,7 +399,7 @@ body, html {
|
||||
|
||||
/* IMDb rating */
|
||||
.text-warning {
|
||||
color: #ffd700 !important; /* Brighter yellow for rating stars */
|
||||
color: #ffd700 !important; /* Brighter yellow for rating stars */
|
||||
}
|
||||
|
||||
.list-group-item img {
|
||||
@@ -1024,6 +1024,10 @@ input[type="text"]:focus, input[type="password"]:focus, select.form-control:focu
|
||||
color: #b0b0b0 !important;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Description text */
|
||||
.description-text {
|
||||
color: #9e9e9e !important;
|
||||
@@ -1099,6 +1103,25 @@ small.text-muted,
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
#fileAnnotationList .card-subtitle {
|
||||
flex-grow: 1;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.excluded-file {
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.form-switch .form-check-input {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#file-inputs-container {
|
||||
transition: opacity 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Cards within modal */
|
||||
.modal .card {
|
||||
background: rgba(30, 30, 50, 0.9);
|
||||
@@ -1143,6 +1166,7 @@ small.text-muted,
|
||||
|
||||
.modal .form-check-label {
|
||||
color: #e0e0e0;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Input groups */
|
||||
@@ -1208,6 +1232,37 @@ small.text-muted,
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.spec-item {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.spec-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.spec-item::after {
|
||||
content: '✎';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.spec-item:hover::after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.spec-item .spec-value {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.sort-direction-toggle {
|
||||
min-width: auto;
|
||||
|
||||
+404
-450
@@ -10,6 +10,8 @@
|
||||
<link href="/static/css/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="/static/css/styles.css" rel="stylesheet">
|
||||
<link href="/static/css/toastr.min.css" rel="stylesheet"/>
|
||||
<link href="/static/css/flatpickr.min.css" rel="stylesheet">
|
||||
<link href="/static/css/flatpickr.dark.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" href="{{ logo_url }}" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
@@ -38,388 +40,285 @@
|
||||
<option value="update_images">Update Images</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dynamic Scraper Parameters -->
|
||||
<div id="scraperParameters">
|
||||
<!-- Dynamic Scraper Parameters -->
|
||||
<div id="scraperParameters">
|
||||
|
||||
<div id="quickImportParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<h4 class="section-header">Quick Import</h4>
|
||||
<hr class="section-divider">
|
||||
<div id="quickImportParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<h4 class="section-header">Quick Import</h4>
|
||||
<hr class="section-divider">
|
||||
|
||||
<!-- Content Type Selection -->
|
||||
<div class="mb-3">
|
||||
<label for="quickMetaType" class="form-label">Content Type</label>
|
||||
<select class="form-select" id="quickMetaType" name="quickMetaType" required>
|
||||
<option value="movie">Movie</option>
|
||||
<option value="series">Series</option>
|
||||
<option value="sports">Sports</option>
|
||||
</select>
|
||||
<!-- Content Type Selection -->
|
||||
<div class="mb-3">
|
||||
<label for="quickMetaType" class="form-label">Content Type</label>
|
||||
<select class="form-select" id="quickMetaType" name="quickMetaType" required>
|
||||
<option value="movie">Movie</option>
|
||||
<option value="series">Series</option>
|
||||
<option value="sports">Sports</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Torrent Upload Area -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-3 text-muted">Recommended: Torrent File Upload</h6>
|
||||
<div class="mb-3">
|
||||
<label for="quickTorrentFile" class="form-label">Torrent File</label>
|
||||
<input type="file" class="form-control" id="quickTorrentFile"
|
||||
name="quickTorrentFile" accept=".torrent"
|
||||
onchange="toggleInput('quickMagnetLink', this)">
|
||||
<div class="form-text">Upload a torrent file for better metadata extraction</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Torrent Upload Area -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-3 text-muted">Recommended: Torrent File Upload</h6>
|
||||
<div class="mb-3">
|
||||
<label for="quickTorrentFile" class="form-label">Torrent File</label>
|
||||
<input type="file" class="form-control" id="quickTorrentFile"
|
||||
name="quickTorrentFile" accept=".torrent"
|
||||
onchange="toggleInput('quickMagnetLink', this)">
|
||||
<div class="form-text">Upload a torrent file for better metadata extraction</div>
|
||||
<!-- Magnet Link Alternative -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-3 text-muted">Alternative: Magnet Link</h6>
|
||||
<div class="mb-3">
|
||||
<label for="quickMagnetLink" class="form-label">Magnet Link</label>
|
||||
<input type="text" class="form-control" id="quickMagnetLink"
|
||||
name="quickMagnetLink" placeholder="magnet:?xt=urn:btih:..."
|
||||
onchange="toggleInput('quickTorrentFile', this)">
|
||||
<div class="form-text text-warning">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
Note: Magnet links may have limited metadata availability
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Magnet Link Alternative -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-3 text-muted">Alternative: Magnet Link</h6>
|
||||
<div class="mb-3">
|
||||
<label for="quickMagnetLink" class="form-label">Magnet Link</label>
|
||||
<input type="text" class="form-control" id="quickMagnetLink"
|
||||
name="quickMagnetLink" placeholder="magnet:?xt=urn:btih:..."
|
||||
onchange="toggleInput('quickTorrentFile', this)">
|
||||
<div class="form-text text-warning">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
Note: Magnet links may have limited metadata availability
|
||||
</div>
|
||||
<!-- Loading Indicator -->
|
||||
<div id="analysisLoading" style="display: none;" class="text-center mb-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Analyzing...</span>
|
||||
</div>
|
||||
<p class="mt-2">Analyzing torrent and searching for matches...</p>
|
||||
</div>
|
||||
|
||||
<!-- Results Container -->
|
||||
<div id="matchResults" class="mb-4" style="display: none;">
|
||||
<h5>Potential Matches</h5>
|
||||
<div id="matchResultsContent" class="list-group">
|
||||
<!-- Matches will be inserted here -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary mt-3 w-100"
|
||||
onclick="switchToManualImport()">
|
||||
None of these match? Import manually
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Torrent Upload Parameters Section -->
|
||||
<div id="torrentUploadParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<!-- Basic Content Information -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Basic Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Content Type and Release Date -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<label for="metaType" class="form-label">Content Type</label>
|
||||
<select class="form-select" id="metaType" name="metaType" onchange="updateContentType()">
|
||||
<option value="movie" {% if prefill_data.meta_type == 'movie' %}selected{% endif %}>Movie</option>
|
||||
<option value="series" {% if prefill_data.meta_type == 'series' %}selected{% endif %}>Series</option>
|
||||
<option value="sports">Sports</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="torrentType" class="form-label">Torrent Type</label>
|
||||
<select class="form-select" id="torrentType" name="torrentType">
|
||||
<option value="public">Public</option>
|
||||
<option value="semi-private">Semi-Private</option>
|
||||
<option value="private">Private</option>
|
||||
<option value="web-seed">Web-Seed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="createdAt" class="form-label">Release Date (DD/MM/YYYY)</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="createdAt"
|
||||
name="createdAt"
|
||||
required
|
||||
placeholder="dd/mm/yyyy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Metadata -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4" id="metaIdContainer">
|
||||
<label for="torrentImdbId" class="form-label">IMDb ID</label>
|
||||
<input type="text" class="form-control" id="torrentImdbId" maxlength="20" placeholder="e.g., tt1234567"
|
||||
value="{% if prefill_data.meta_id %}{{ prefill_data.meta_id }}{% endif %}">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<alert class="alert alert-info py-1" role="alert">
|
||||
<i class="bi bi-info circle"></i> Validate auto-filled data before submission
|
||||
</alert>
|
||||
<input type="text" class="form-control" id="title" placeholder="Content title">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Media URLs -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label for="poster" class="form-label">Poster URL</label>
|
||||
<input type="url" class="form-control" id="poster">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="background" class="form-label">Background URL</label>
|
||||
<input type="url" class="form-control" id="background">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="logo" class="form-label">Logo URL</label>
|
||||
<input type="url" class="form-control" id="logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add title to poster option -->
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="addTitleToPoster" name="addTitleToPoster">
|
||||
<label class="form-check-label" for="addTitleToPoster">
|
||||
Add title to poster
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Indicator -->
|
||||
<div id="analysisLoading" style="display: none;" class="text-center mb-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Analyzing...</span>
|
||||
</div>
|
||||
<p class="mt-2">Analyzing torrent and searching for matches...</p>
|
||||
<!-- Technical Specifications -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Technical Specifications</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="technical-specs-basic"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results Container -->
|
||||
<div id="matchResults" class="mb-4" style="display: none;">
|
||||
<h5>Potential Matches</h5>
|
||||
<div id="matchResultsContent" class="list-group">
|
||||
<!-- Matches will be inserted here -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary mt-3 w-100"
|
||||
onclick="switchToManualImport()">
|
||||
None of these match? Import manually
|
||||
</button>
|
||||
<!-- Catalogs Section -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Content Categories</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Movie Catalogs -->
|
||||
<div id="movieCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i> Select categories to help organize your content
|
||||
</div>
|
||||
<div class="catalog-grid">
|
||||
{% for catalog in supported_movie_catalog_ids %}
|
||||
<div class="catalog-item">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="catalogs"
|
||||
value="{{ catalog }}" id="catalog-{{ catalog }}">
|
||||
<label class="form-check-label" for="catalog-{{ catalog }}">
|
||||
{{ catalog_data.get(catalog) }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Series Catalogs -->
|
||||
<div id="seriesCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i> Select categories to help organize your content
|
||||
</div>
|
||||
<div class="catalog-grid">
|
||||
{% for catalog in supported_series_catalog_ids %}
|
||||
<div class="catalog-item">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="catalogs"
|
||||
value="{{ catalog }}" id="catalog-{{ catalog }}">
|
||||
<label class="form-check-label" for="catalog-{{ catalog }}">
|
||||
{{ catalog_data.get(catalog) }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sports Catalogs -->
|
||||
<div id="sportsCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="col-md-12">
|
||||
<select class="form-select" id="sportsCatalog">
|
||||
<option value="">Select Sports Category</option>
|
||||
<option value="american_football">American Football / NFL</option>
|
||||
<option value="baseball">Baseball / MLB</option>
|
||||
<option value="basketball">Basketball / NBA</option>
|
||||
<option value="football">Football / Soccer</option>
|
||||
<option value="formula_racing">Formula Racing</option>
|
||||
<option value="hockey">Hockey / NHL</option>
|
||||
<option value="motogp_racing">MotoGP Racing</option>
|
||||
<option value="other_sports">Other Sports</option>
|
||||
<option value="rugby">Rugby / AFL</option>
|
||||
<option value="wwe">WWE</option>
|
||||
<option value="ufc">UFC</option>
|
||||
<option value="fighting">Fighting</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Torrent Data Section -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Torrent Data</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="torrentFile" class="form-label">Torrent File</label>
|
||||
<input type="file" class="form-control" id="torrentFile" accept=".torrent" onchange="toggleInput('magnetLink', this)">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="magnetLink" class="form-label">Magnet Link</label>
|
||||
<input type="text" class="form-control" id="magnetLink" placeholder="magnet:?xt=urn:btih:..." onchange="toggleInput('torrentFile', this)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Uploader Information -->
|
||||
<div class="mb-3">
|
||||
<label for="uploaderName" class="form-label">Uploader Name (Optional)</label>
|
||||
<input type="text" class="form-control" id="uploaderName" placeholder="Enter your name or leave blank for Anonymous">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Torrent Upload Parameters Section -->
|
||||
<div id="torrentUploadParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<!-- Basic Content Information -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Basic Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Content Type and Release Date -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<label for="metaType" class="form-label">Content Type</label>
|
||||
<select class="form-select" id="metaType" name="metaType" onchange="updateContentType()">
|
||||
<option value="movie" {% if prefill_data.meta_type == 'movie' %}selected{% endif %}>Movie</option>
|
||||
<option value="series" {% if prefill_data.meta_type == 'series' %}selected{% endif %}>Series</option>
|
||||
<option value="sports">Sports</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="torrentType" class="form-label">Torrent Type</label>
|
||||
<select class="form-select" id="torrentType" name="torrentType">
|
||||
<option value="public">Public</option>
|
||||
<option value="semi-private">Semi-Private</option>
|
||||
<option value="private">Private</option>
|
||||
<option value="web-seed">Web-Seed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="createdAt" class="form-label">Release Date</label>
|
||||
<input type="date"
|
||||
class="form-control"
|
||||
id="createdAt"
|
||||
name="createdAt"
|
||||
required
|
||||
pattern="\d{2}/\d{2}/\d{4}"
|
||||
placeholder="dd/mm/yyyy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Metadata -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4" id="metaIdContainer">
|
||||
<label for="torrentImdbId" class="form-label">IMDb ID</label>
|
||||
<input type="text" class="form-control" id="torrentImdbId" maxlength="20" placeholder="e.g., tt1234567"
|
||||
value="{% if prefill_data.meta_id %}{{ prefill_data.meta_id }}{% endif %}">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<alert class="alert alert-info py-1" role="alert">
|
||||
<i class="bi bi-info circle"></i> Validate auto-filled data before submission
|
||||
</alert>
|
||||
<input type="text" class="form-control" id="title" placeholder="Content title">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Media URLs -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label for="poster" class="form-label">Poster URL</label>
|
||||
<input type="url" class="form-control" id="poster">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="background" class="form-label">Background URL</label>
|
||||
<input type="url" class="form-control" id="background">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="logo" class="form-label">Logo URL</label>
|
||||
<input type="url" class="form-control" id="logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add title to poster option -->
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="addTitleToPoster" name="addTitleToPoster">
|
||||
<label class="form-check-label" for="addTitleToPoster">
|
||||
Add title to poster
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Technical Specifications -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Technical Specifications</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Basic specs grid -->
|
||||
<div class="technical-specs-basic"></div>
|
||||
|
||||
<!-- Advanced Toggle Button will be inserted here -->
|
||||
<div class="advanced-toggle" id="toggleAdvanced">
|
||||
<i class="bi bi-chevron-down"></i>
|
||||
<span>Advanced Technical Options</span>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Technical Specifications -->
|
||||
<div class="advanced-section" id="advancedSection">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<label for="resolution" class="form-label">Resolution</label>
|
||||
<select class="form-select" id="resolution" onchange="updateSpecField('resolution', this.value)">
|
||||
<option value="">Select Resolution</option>
|
||||
<option value="480p">480p</option>
|
||||
<option value="576p">576p</option>
|
||||
<option value="720p">720p</option>
|
||||
<option value="1080p">1080p</option>
|
||||
<option value="1440p">1440p</option>
|
||||
<option value="2160p">2160p</option>
|
||||
<option value="4K">4K</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="quality" class="form-label">Quality</label>
|
||||
<select class="form-select" id="quality" onchange="updateSpecField('quality', this.value)">
|
||||
<option value="">Select Quality</option>
|
||||
<option value="BluRay">BluRay</option>
|
||||
<option value="BluRay REMUX">BluRay REMUX</option>
|
||||
<option value="BRRip">BRRip</option>
|
||||
<option value="BDRip">BDRip</option>
|
||||
<option value="WEB-DL">WEB-DL</option>
|
||||
<option value="HDRip">HDRip</option>
|
||||
<option value="DVDRip">DVDRip</option>
|
||||
<option value="HDTV">HDTV</option>
|
||||
<option value="CAM">CAM</option>
|
||||
<option value="TeleSync">TeleSync</option>
|
||||
<option value="SCR">SCR</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Video Codec -->
|
||||
<div id="video-codec-section" class="col-md-4">
|
||||
<label for="videoCodec" class="form-label">Video Codec</label>
|
||||
<select class="form-select" id="videoCodec" onchange="updateSpecField('codec', this.value)">
|
||||
<option value="">Select Video Codec</option>
|
||||
<option value="x264">x264</option>
|
||||
<option value="x265">x265</option>
|
||||
<option value="h.264">H.264</option>
|
||||
<option value="h.265">H.265</option>
|
||||
<option value="hevc">HEVC</option>
|
||||
<option value="avc">AVC</option>
|
||||
<option value="mpeg-2">MPEG-2</option>
|
||||
<option value="mpeg-4">MPEG-4</option>
|
||||
<option value="vp9">VP9</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audio Codecs -->
|
||||
<div id="audio-codecs-section">
|
||||
<label class="form-label">Audio Codecs</label>
|
||||
<div class="row">
|
||||
{% for codec in ['AAC', 'AC3', 'DTS', 'DTS-HD MA', 'TrueHD', 'Atmos', 'DD+', 'Dolby Digital Plus', 'DTS Lossless'] %}
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="audioCodecs" value="{{ codec }}"
|
||||
id="audio-{{ codec|replace(' ', '')|replace('+', '') }}">
|
||||
<label class="form-check-label"
|
||||
for="audio-{{ codec|replace(' ', '')|replace('+', '') }}">
|
||||
{{ codec }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HDR Formats -->
|
||||
<div id="hdr-formats-section">
|
||||
<label class="form-label">HDR Formats</label>
|
||||
<div class="row">
|
||||
{% for format in ['DV', 'HDR10+', 'HDR', 'SDR'] %}
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="hdrFormats" value="{{ format }}"
|
||||
id="hdr-{{ format|replace('+', '') }}">
|
||||
<label class="form-check-label"
|
||||
for="hdr-{{ format|replace('+', '') }}">
|
||||
{{ format }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Languages -->
|
||||
<div id="languages-section">
|
||||
<label class="form-label">Languages</label>
|
||||
<div class="row">
|
||||
{% for language in supported_languages %}
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="languages" value="{{ language }}"
|
||||
id="lang-{{ language }}">
|
||||
<label class="form-check-label"
|
||||
for="lang-{{ language }}">
|
||||
{{ language }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Catalogs Section -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Content Categories</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Movie Catalogs -->
|
||||
<div id="movieCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i> Select categories to help organize your content
|
||||
</div>
|
||||
<div class="catalog-grid">
|
||||
{% for catalog in supported_movie_catalog_ids %}
|
||||
<div class="catalog-item">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="catalogs"
|
||||
value="{{ catalog }}" id="catalog-{{ catalog }}">
|
||||
<label class="form-check-label" for="catalog-{{ catalog }}">
|
||||
{{ catalog_data.get(catalog) }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Series Catalogs -->
|
||||
<div id="seriesCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i> Select categories to help organize your content
|
||||
</div>
|
||||
<div class="catalog-grid">
|
||||
{% for catalog in supported_series_catalog_ids %}
|
||||
<div class="catalog-item">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="catalogs"
|
||||
value="{{ catalog }}" id="catalog-{{ catalog }}">
|
||||
<label class="form-check-label" for="catalog-{{ catalog }}">
|
||||
{{ catalog_data.get(catalog) }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sports Catalogs -->
|
||||
<div id="sportsCatalogs" class="catalog-group" style="display: none;">
|
||||
<div class="col-md-12">
|
||||
<select class="form-select" id="sportsCatalog">
|
||||
<option value="">Select Sports Category</option>
|
||||
<option value="american_football">American Football / NFL</option>
|
||||
<option value="baseball">Baseball / MLB</option>
|
||||
<option value="basketball">Basketball / NBA</option>
|
||||
<option value="football">Football / Soccer</option>
|
||||
<option value="formula_racing">Formula Racing</option>
|
||||
<option value="hockey">Hockey / NHL</option>
|
||||
<option value="motogp_racing">MotoGP Racing</option>
|
||||
<option value="other_sports">Other Sports</option>
|
||||
<option value="rugby">Rugby / AFL</option>
|
||||
<option value="wwe">WWE</option>
|
||||
<option value="ufc">UFC</option>
|
||||
<option value="fighting">Fighting</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Torrent Data Section -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Torrent Data</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="torrentFile" class="form-label">Torrent File</label>
|
||||
<input type="file" class="form-control" id="torrentFile" accept=".torrent" onchange="toggleInput('magnetLink', this)">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="magnetLink" class="form-label">Magnet Link</label>
|
||||
<input type="text" class="form-control" id="magnetLink" placeholder="magnet:?xt=urn:btih:..." onchange="toggleInput('torrentFile', this)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Uploader Information -->
|
||||
<div class="mb-3">
|
||||
<label for="uploaderName" class="form-label">Uploader Name (Optional)</label>
|
||||
<input type="text" class="form-control" id="uploaderName" placeholder="Enter your name or leave blank for Anonymous">
|
||||
<!-- Success Alert with Import New Button -->
|
||||
<div class="alert alert-success alert-dismissible fade" role="alert" id="successAlert">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<i class="bi bi-check-circle-fill me-2"></i>
|
||||
<span id="successMessage">Torrent added successfully!</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="switchToQuickImport()">
|
||||
Import New Torrent
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn-close" onclick="hideSuccessAlert()"></button>
|
||||
</div>
|
||||
|
||||
<!-- Scrapy Parameters -->
|
||||
<div id="scrapyParameters" style="display: none;">
|
||||
</div>
|
||||
|
||||
<!-- Scrapy Parameters -->
|
||||
<div id="scrapyParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<div class="mb-3">
|
||||
<label for="spiderName" class="form-label">Spider Name</label>
|
||||
<select class="form-select" id="spiderName" name="spider_name">
|
||||
@@ -480,9 +379,13 @@
|
||||
<input type="number" class="form-control" id="totalPages" name="total_pages" value="1">
|
||||
<label><input type="checkbox" name="scrape_all" id="scrape_all"> Scrape All Pages</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- TV Metadata Input -->
|
||||
<div id="tvMetadataInput" style="display: none;">
|
||||
</div>
|
||||
|
||||
<!-- TV Metadata Input -->
|
||||
<div id="tvMetadataInput" style="display: none;">
|
||||
<div class="section-container">
|
||||
<h4 class="section-header">TV Metadata Input</h4>
|
||||
<hr class="section-divider">
|
||||
<div class="mb-3">
|
||||
@@ -520,8 +423,13 @@
|
||||
<div id="streamInputs"></div>
|
||||
<button type="button" class="btn btn-primary" onclick="addStreamInput()">Add Stream</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="m3uPlaylistInput" style="display: none;">
|
||||
</div>
|
||||
|
||||
<div id="m3uPlaylistInput" style="display: none;">
|
||||
<div class="section-container">
|
||||
|
||||
<div class="mb-3">
|
||||
<div>
|
||||
<label><input type="radio" name="m3uInputType" value="url" checked> M3U Playlist URL</label>
|
||||
@@ -540,9 +448,14 @@
|
||||
<label for="m3uPlaylistSource" class="form-label">Playlist Source Name</label>
|
||||
<input type="text" class="form-control" id="m3uPlaylistSource" name="m3uPlaylistSource">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- IMDb Data Update Parameters -->
|
||||
<div id="imdbDataParameters" style="display: none;">
|
||||
</div>
|
||||
|
||||
<!-- IMDb Data Update Parameters -->
|
||||
<div id="imdbDataParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="imdbId" class="form-label">IMDb ID</label>
|
||||
<input type="text" class="form-control" id="imdbId" name="imdb_id" placeholder="Enter IMDb ID (e.g., tt1234567)">
|
||||
@@ -555,16 +468,22 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="blockTorrentParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
|
||||
<div id="blockTorrentParameters" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="blockTorrentInfoHash" class="form-label">Torrent Info Hash</label>
|
||||
<input type="text" class="form-control" id="blockTorrentInfoHash" name="blockTorrentInfoHash" required
|
||||
value="{% if prefill_data.info_hash %}{{ prefill_data.info_hash }}{% endif %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="migrationParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
|
||||
<div id="migrationParameters" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="mediafusionId" class="form-label">MediaFusion ID</label>
|
||||
<input type="text" class="form-control" id="mediafusionId" name="mediafusion_id" required
|
||||
@@ -583,99 +502,99 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="updateImagesParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<h4 class="section-header">Update Images</h4>
|
||||
<hr class="section-divider">
|
||||
<div id="updateImagesParameters" style="display: none;">
|
||||
<div class="section-container">
|
||||
<h4 class="section-header">Update Images</h4>
|
||||
<hr class="section-divider">
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Provide the content ID and at least one image URL to update
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Provide the content ID and at least one image URL to update
|
||||
</div>
|
||||
|
||||
<!-- Content ID Input -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Content Information</h5>
|
||||
</div>
|
||||
|
||||
<!-- Content ID Input -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Content Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="imageUpdateMetaId" class="form-label">Content ID *</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="imageUpdateMetaId"
|
||||
name="meta_id"
|
||||
required
|
||||
placeholder="Enter IMDb ID (tt1234567) or MediaFusion ID">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
This can be either an IMDb ID (starts with 'tt') or a MediaFusion ID (starts with 'mf')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image URLs Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Image URLs</h5>
|
||||
<small class="text-muted">At least one image URL must be provided</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Poster URL -->
|
||||
<div class="mb-4">
|
||||
<label for="imageUpdatePoster" class="form-label">Poster URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-image"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdatePoster"
|
||||
name="poster"
|
||||
placeholder="https://example.com/poster.jpg">
|
||||
</div>
|
||||
<div class="form-text">High-quality vertical image for the content poster</div>
|
||||
</div>
|
||||
|
||||
<!-- Background URL -->
|
||||
<div class="mb-4">
|
||||
<label for="imageUpdateBackground" class="form-label">Background URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-image-fill"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdateBackground"
|
||||
name="background"
|
||||
placeholder="https://example.com/background.jpg">
|
||||
</div>
|
||||
<div class="form-text">Wide image to be used as background/backdrop</div>
|
||||
</div>
|
||||
|
||||
<!-- Logo URL -->
|
||||
<div class="mb-3">
|
||||
<label for="imageUpdateLogo" class="form-label">Logo URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-vector-pen"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdateLogo"
|
||||
name="logo"
|
||||
placeholder="https://example.com/logo.png">
|
||||
</div>
|
||||
<div class="form-text">Transparent PNG logo of the content</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="imageUpdateMetaId" class="form-label">Content ID *</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="imageUpdateMetaId"
|
||||
name="meta_id"
|
||||
required
|
||||
placeholder="Enter IMDb ID (tt1234567) or MediaFusion ID">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
This can be either an IMDb ID (starts with 'tt') or a MediaFusion ID (starts with 'mf')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image URLs Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Image URLs</h5>
|
||||
<small class="text-muted">At least one image URL must be provided</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Poster URL -->
|
||||
<div class="mb-4">
|
||||
<label for="imageUpdatePoster" class="form-label">Poster URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-image"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdatePoster"
|
||||
name="poster"
|
||||
placeholder="https://example.com/poster.jpg">
|
||||
</div>
|
||||
<div class="form-text">High-quality vertical image for the content poster</div>
|
||||
</div>
|
||||
|
||||
<!-- Background URL -->
|
||||
<div class="mb-4">
|
||||
<label for="imageUpdateBackground" class="form-label">Background URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-image-fill"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdateBackground"
|
||||
name="background"
|
||||
placeholder="https://example.com/background.jpg">
|
||||
</div>
|
||||
<div class="form-text">Wide image to be used as background/backdrop</div>
|
||||
</div>
|
||||
|
||||
<!-- Logo URL -->
|
||||
<div class="mb-3">
|
||||
<label for="imageUpdateLogo" class="form-label">Logo URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-vector-pen"></i>
|
||||
</span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="imageUpdateLogo"
|
||||
name="logo"
|
||||
placeholder="https://example.com/logo.png">
|
||||
</div>
|
||||
<div class="form-text">Transparent PNG logo of the content</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- API Password Configuration -->
|
||||
@@ -773,11 +692,46 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Spec Selection Modal -->
|
||||
<div class="modal fade" id="specSelectionModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="specModalTitle">Select Specification</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Single Selection -->
|
||||
<div id="singleSpecSelection" style="display: none;">
|
||||
<select class="form-select mb-3" id="specSelectSingle">
|
||||
<!-- Options will be populated dynamically -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Multiple Selection -->
|
||||
<div id="multipleSpecSelection" style="display: none;">
|
||||
<div class="spec-checkboxes mb-3">
|
||||
<!-- Checkboxes will be populated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmSpecSelection">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="supportedLanguages" value='{{ supported_languages|tojson|safe }}'>
|
||||
|
||||
<!-- JS for Bootstrap -->
|
||||
<script src="/static/js/jquery-3.6.0.min.js"></script>
|
||||
<script src="/static/js/popper.min.js"></script>
|
||||
<script src="/static/js/bootstrap.min.js"></script>
|
||||
<script src="/static/js/toastr.min.js"></script>
|
||||
<script src="/static/js/flatpickr.js"></script>
|
||||
<script src="/static/js/scraperControl.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
+411
-193
@@ -1,3 +1,57 @@
|
||||
// Define spec options
|
||||
const SPEC_OPTIONS = {
|
||||
resolution: [
|
||||
{value: '480p', label: '480p (SD)'},
|
||||
{value: '576p', label: '576p (SD)'},
|
||||
{value: '720p', label: '720p (HD)'},
|
||||
{value: '1080p', label: '1080p (Full HD)'},
|
||||
{value: '1440p', label: '1440p (2K)'},
|
||||
{value: '2160p', label: '2160p (4K)'},
|
||||
{value: '4K', label: '4K UHD'}
|
||||
],
|
||||
quality: [
|
||||
{value: 'BluRay', label: 'BluRay'},
|
||||
{value: 'BluRay REMUX', label: 'BluRay REMUX'},
|
||||
{value: 'BRRip', label: 'BRRip'},
|
||||
{value: 'BDRip', label: 'BDRip'},
|
||||
{value: 'WEB-DL', label: 'WEB-DL'},
|
||||
{value: 'HDRip', label: 'HDRip'},
|
||||
{value: 'DVDRip', label: 'DVDRip'},
|
||||
{value: 'HDTV', label: 'HDTV'},
|
||||
{value: 'CAM', label: 'CAM'},
|
||||
{value: 'TeleSync', label: 'TeleSync'},
|
||||
{value: 'SCR', label: 'SCR'}
|
||||
],
|
||||
codec: [
|
||||
{value: 'x264', label: 'x264'},
|
||||
{value: 'x265', label: 'x265 (HEVC)'},
|
||||
{value: 'h.264', label: 'H.264 (AVC)'},
|
||||
{value: 'h.265', label: 'H.265 (HEVC)'},
|
||||
{value: 'hevc', label: 'HEVC'},
|
||||
{value: 'avc', label: 'AVC'},
|
||||
{value: 'mpeg-2', label: 'MPEG-2'},
|
||||
{value: 'mpeg-4', label: 'MPEG-4'},
|
||||
{value: 'vp9', label: 'VP9'}
|
||||
],
|
||||
audio: [
|
||||
{value: 'AAC', label: 'AAC'},
|
||||
{value: 'AC3', label: 'AC3 (Dolby Digital)'},
|
||||
{value: 'DTS', label: 'DTS'},
|
||||
{value: 'DTS-HD MA', label: 'DTS-HD MA'},
|
||||
{value: 'TrueHD', label: 'Dolby TrueHD'},
|
||||
{value: 'Atmos', label: 'Dolby Atmos'},
|
||||
{value: 'DD+', label: 'DD+'},
|
||||
{value: 'Dolby Digital Plus', label: 'Dolby Digital Plus'},
|
||||
{value: 'DTS Lossless', label: 'DTS Lossless'}
|
||||
],
|
||||
hdr: [
|
||||
{value: 'DV', label: 'Dolby Vision'},
|
||||
{value: 'HDR10+', label: 'HDR10+'},
|
||||
{value: 'HDR', label: 'HDR'},
|
||||
{value: 'SDR', label: 'SDR'}
|
||||
]
|
||||
};
|
||||
|
||||
// ---- Helper Functions ----
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
@@ -315,10 +369,28 @@ function showFileAnnotationModal(files) {
|
||||
|
||||
files.forEach((file, index) => {
|
||||
const fileRow = `
|
||||
<div class="card mb-3">
|
||||
<div class="card mb-3" id="file-row-${index}">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-2 text-muted">${file.filename}</h6>
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center flex-grow-1">
|
||||
<!-- Include/Exclude Toggle -->
|
||||
<div class="form-check form-switch me-2">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
id="include-file-${index}"
|
||||
checked
|
||||
onchange="toggleFileInclusion(${index})">
|
||||
<label class="form-check-label" for="include-file-${index}">
|
||||
Include
|
||||
</label>
|
||||
</div>
|
||||
<h6 class="card-subtitle mb-0 text-muted text-truncate"
|
||||
style="max-width: 80%;"
|
||||
title="${file.filename}">
|
||||
${file.filename}
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="file-inputs-${index}">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Season</label>
|
||||
<input type="number" class="form-control season-input"
|
||||
@@ -359,28 +431,38 @@ function showFileAnnotationModal(files) {
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Release Date</label>
|
||||
<input type="date" class="form-control"
|
||||
id="release-${index}">
|
||||
<input type="text" class="form-control"
|
||||
id="release-${index}"
|
||||
placeholder="DD/MM/YYYY">
|
||||
</div>
|
||||
</div>
|
||||
` : `
|
||||
`}
|
||||
` : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
fileList.insertAdjacentHTML('beforeend', fileRow);
|
||||
});
|
||||
|
||||
files.forEach((file, index) => {
|
||||
// Initialize date picker for each file
|
||||
setupDateInput(`release-${index}`, false);
|
||||
});
|
||||
|
||||
// Set up bulk season assignment handler
|
||||
document.getElementById('applyBulkSeason').onclick = () => {
|
||||
const season = document.getElementById('bulkSeason').value;
|
||||
if (season) {
|
||||
document.querySelectorAll('.season-input').forEach(input => {
|
||||
input.value = season;
|
||||
document.querySelectorAll('.season-input:not([disabled])').forEach(input => {
|
||||
// Only apply to included files
|
||||
const index = input.dataset.index;
|
||||
const isIncluded = document.getElementById(`include-file-${index}`)?.checked;
|
||||
if (isIncluded) {
|
||||
input.value = season;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Set up multiple seasons handler
|
||||
// Set up multiple seasons handler with the same included files logic
|
||||
document.getElementById('applyMultiSeasons').onclick = () => {
|
||||
const seasonsInput = document.getElementById('multipleSeasons').value;
|
||||
if (!seasonsInput) return;
|
||||
@@ -395,7 +477,13 @@ function showFileAnnotationModal(files) {
|
||||
const distribution = document.querySelector('input[name="seasonDistribution"]:checked').value;
|
||||
const episodesPerSeason = parseInt(document.getElementById('episodeCount').value) || 0;
|
||||
|
||||
const seasonInputs = document.querySelectorAll('.season-input');
|
||||
// Filter for only included files
|
||||
const seasonInputs = Array.from(document.querySelectorAll('.season-input:not([disabled])'))
|
||||
.filter(input => {
|
||||
const index = input.dataset.index;
|
||||
return document.getElementById(`include-file-${index}`)?.checked;
|
||||
});
|
||||
|
||||
if (distribution === 'auto') {
|
||||
// Distribute episodes evenly across seasons
|
||||
const filesPerSeason = Math.ceil(seasonInputs.length / seasons.length);
|
||||
@@ -414,7 +502,7 @@ function showFileAnnotationModal(files) {
|
||||
}
|
||||
};
|
||||
|
||||
// Set up distribution method handlers
|
||||
// Distribution method handlers
|
||||
document.querySelectorAll('input[name="seasonDistribution"]').forEach(radio => {
|
||||
radio.onchange = () => {
|
||||
document.getElementById('episodesPerSeason').style.display =
|
||||
@@ -431,7 +519,6 @@ function showFileAnnotationModal(files) {
|
||||
}
|
||||
};
|
||||
|
||||
// Initial application
|
||||
applySeasons();
|
||||
};
|
||||
|
||||
@@ -440,24 +527,28 @@ function showFileAnnotationModal(files) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
document.getElementById('confirmAnnotation').onclick = () => {
|
||||
const annotatedFiles = files.map((file, index) => {
|
||||
const baseData = {
|
||||
...file,
|
||||
season_number: parseInt(document.getElementById(`season-${index}`).value) || null,
|
||||
episode_number: parseInt(document.getElementById(`episode-${index}`).value) || null,
|
||||
};
|
||||
|
||||
if (isSportsContent) {
|
||||
return {
|
||||
...baseData,
|
||||
title: document.getElementById(`title-${index}`).value || null,
|
||||
overview: document.getElementById(`overview-${index}`).value || null,
|
||||
thumbnail: document.getElementById(`thumbnail-${index}`).value || null,
|
||||
release_date: document.getElementById(`release-${index}`).value || null,
|
||||
|
||||
const annotatedFiles = [];
|
||||
files.forEach((file, index) => {
|
||||
// Only include files that are marked for inclusion
|
||||
if (document.getElementById(`include-file-${index}`)?.checked) {
|
||||
const baseData = {
|
||||
...file,
|
||||
season_number: parseInt(document.getElementById(`season-${index}`).value) || null,
|
||||
episode_number: parseInt(document.getElementById(`episode-${index}`).value) || null,
|
||||
};
|
||||
|
||||
if (isSportsContent) {
|
||||
annotatedFiles.push({
|
||||
...baseData,
|
||||
title: document.getElementById(`title-${index}`).value || null,
|
||||
overview: document.getElementById(`overview-${index}`).value || null,
|
||||
thumbnail: document.getElementById(`thumbnail-${index}`).value || null,
|
||||
release_date: document.getElementById(`release-${index}`).value || null,
|
||||
});
|
||||
} else {
|
||||
annotatedFiles.push(baseData);
|
||||
}
|
||||
}
|
||||
return baseData;
|
||||
});
|
||||
bsModal.hide();
|
||||
resolve(annotatedFiles);
|
||||
@@ -469,6 +560,33 @@ function showFileAnnotationModal(files) {
|
||||
});
|
||||
}
|
||||
|
||||
// Function to toggle file inclusion
|
||||
function toggleFileInclusion(index) {
|
||||
const checkbox = document.getElementById(`include-file-${index}`);
|
||||
const inputsContainer = document.getElementById(`file-inputs-${index}`);
|
||||
const fileRow = document.getElementById(`file-row-${index}`);
|
||||
|
||||
if (checkbox.checked) {
|
||||
// File is included
|
||||
inputsContainer.style.opacity = '1';
|
||||
inputsContainer.style.pointerEvents = 'auto';
|
||||
fileRow.classList.remove('excluded-file');
|
||||
// Enable all inputs in the container
|
||||
inputsContainer.querySelectorAll('input, textarea').forEach(input => {
|
||||
input.disabled = false;
|
||||
});
|
||||
} else {
|
||||
// File is excluded
|
||||
inputsContainer.style.opacity = '0.5';
|
||||
inputsContainer.style.pointerEvents = 'none';
|
||||
fileRow.classList.add('excluded-file');
|
||||
// Disable all inputs in the container
|
||||
inputsContainer.querySelectorAll('input, textarea').forEach(input => {
|
||||
input.disabled = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toggleInput(disableId, input) {
|
||||
document.getElementById(disableId).disabled = !!input.value;
|
||||
@@ -557,38 +675,28 @@ function handleInitialSetup() {
|
||||
updateFormFields();
|
||||
}
|
||||
|
||||
function setupDateInput(inputId, defaultToToday = true) {
|
||||
const dateInput = document.getElementById(inputId);
|
||||
if (!dateInput) return;
|
||||
|
||||
if (defaultToToday) {
|
||||
// Set default value to today's date
|
||||
const today = new Date();
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const year = today.getFullYear();
|
||||
dateInput.value = `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
// Format date for display
|
||||
dateInput.addEventListener('input', function(e) {
|
||||
const date = new Date(this.value);
|
||||
if (!isNaN(date)) {
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
this.title = `${day}/${month}/${year}`;
|
||||
}
|
||||
function setupDateInput(inputId, defaultToToday = false, initialDate = null) {
|
||||
const fp = flatpickr(`#${inputId}`, {
|
||||
dateFormat: "d/m/Y",
|
||||
altInput: true,
|
||||
altFormat: "d/m/Y",
|
||||
allowInput: true,
|
||||
defaultHour: 12,
|
||||
maxDate: "today",
|
||||
locale: {
|
||||
firstDayOfWeek: 1
|
||||
},
|
||||
theme: "dark",
|
||||
defaultDate: initialDate // Set initial date if provided
|
||||
});
|
||||
|
||||
// Trigger initial formatting
|
||||
if (dateInput.value) {
|
||||
const event = new Event('input');
|
||||
dateInput.dispatchEvent(event);
|
||||
if (defaultToToday && !initialDate) {
|
||||
const today = new Date();
|
||||
fp.setDate(today);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function constructTvMetadata() {
|
||||
// Basic TV Metadata collection
|
||||
let tvMetaData = {
|
||||
@@ -861,29 +969,174 @@ function updateBasicTechnicalSpecs(torrentData = {}) {
|
||||
];
|
||||
|
||||
specsContainer.innerHTML = specs.map(spec => `
|
||||
<div class="spec-item">
|
||||
<div class="spec-item" data-spec-type="${spec.type}">
|
||||
<i class="bi ${spec.icon}"></i>
|
||||
<div>
|
||||
<div class="spec-label">${spec.label}</div>
|
||||
<div class="spec-value" id="${spec.type}Spec" >${formatTechnicalSpec(spec.value, spec.type)}</div>
|
||||
<div class="spec-value" id="${spec.type}Spec">${formatTechnicalSpec(spec.value, spec.type)}</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Function to set up the advanced toggle system
|
||||
function setupAdvancedToggle() {
|
||||
document.getElementById('toggleAdvanced').addEventListener('click', () => {
|
||||
const toggleButton = document.getElementById('toggleAdvanced');
|
||||
toggleButton.classList.toggle('active');
|
||||
const advancedSection = document.getElementById('advancedSection');
|
||||
advancedSection.classList.toggle('show');
|
||||
// Add click handlers to each spec item
|
||||
specsContainer.querySelectorAll('.spec-item').forEach(item => {
|
||||
item.addEventListener('click', function () {
|
||||
const specType = this.dataset.specType;
|
||||
const modal = new bootstrap.Modal(document.getElementById('specSelectionModal'));
|
||||
const modalTitle = document.getElementById('specModalTitle');
|
||||
const singleSelection = document.getElementById('singleSpecSelection');
|
||||
const multipleSelection = document.getElementById('multipleSpecSelection');
|
||||
|
||||
// Determine if this is a multi-select specification
|
||||
const isMultiSelect = ['audio', 'hdr', 'languages'].includes(specType);
|
||||
|
||||
// Update modal title
|
||||
modalTitle.textContent = `Select ${this.querySelector('.spec-label').textContent}`;
|
||||
|
||||
// Show appropriate selection interface
|
||||
singleSelection.style.display = isMultiSelect ? 'none' : 'block';
|
||||
multipleSelection.style.display = isMultiSelect ? 'block' : 'none';
|
||||
|
||||
// Get current value
|
||||
const currentValue = this.querySelector('.spec-value').textContent;
|
||||
|
||||
// Populate options based on spec type
|
||||
if (isMultiSelect) {
|
||||
populateMultiSelect(specType, currentValue);
|
||||
} else {
|
||||
populateSingleSelect(specType, currentValue);
|
||||
}
|
||||
|
||||
// Set up confirmation handler
|
||||
document.getElementById('confirmSpecSelection').onclick = () => {
|
||||
let selectedValue;
|
||||
if (isMultiSelect) {
|
||||
selectedValue = Array.from(document.querySelectorAll('.spec-checkboxes input:checked'))
|
||||
.map(cb => cb.value)
|
||||
.join(', ');
|
||||
} else {
|
||||
selectedValue = document.getElementById('specSelectSingle').value;
|
||||
}
|
||||
|
||||
if (selectedValue) {
|
||||
// Update the spec display
|
||||
updateSpecField(specType, selectedValue);
|
||||
}
|
||||
modal.hide();
|
||||
};
|
||||
|
||||
modal.show();
|
||||
});
|
||||
});
|
||||
|
||||
// Initial update of basic specs
|
||||
updateBasicTechnicalSpecs();
|
||||
}
|
||||
|
||||
// Update populateSingleSelect to use new options format
|
||||
function populateSingleSelect(specType, currentValue) {
|
||||
const select = document.getElementById('specSelectSingle');
|
||||
select.innerHTML = '<option value="">Select an option</option>';
|
||||
|
||||
const options = getOptionsForSpecType(specType);
|
||||
options.forEach(option => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = option.value;
|
||||
opt.textContent = option.label;
|
||||
opt.selected = currentValue === option.value;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
// Update populateMultiSelect to use new options format
|
||||
function populateMultiSelect(specType, currentValue) {
|
||||
const container = document.querySelector('.spec-checkboxes');
|
||||
container.innerHTML = '';
|
||||
|
||||
const currentValues = currentValue.split(', ').map(v => v.trim());
|
||||
const options = getOptionsForSpecType(specType);
|
||||
|
||||
options.forEach(option => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'form-check';
|
||||
div.innerHTML = `
|
||||
<input class="form-check-input" type="checkbox" value="${option.value}"
|
||||
id="spec-${option.value.replace(/[^a-zA-Z0-9]/g, '')}"
|
||||
${currentValues.includes(option.value) ? 'checked' : ''}>
|
||||
<label class="form-check-label" for="spec-${option.value.replace(/[^a-zA-Z0-9]/g, '')}">
|
||||
${option.label}
|
||||
</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to get current spec values
|
||||
function getSpecValues() {
|
||||
return {
|
||||
resolution: document.getElementById('resolutionSpec')?.textContent.trim(),
|
||||
quality: document.getElementById('qualitySpec')?.textContent.trim(),
|
||||
codec: document.getElementById('codecSpec')?.textContent.trim(),
|
||||
audio: document.getElementById('audioSpec')?.textContent.trim(),
|
||||
languages: document.getElementById('languagesSpec')?.textContent.trim(),
|
||||
hdr: document.getElementById('hdrSpec')?.textContent.trim()
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to get options for a spec type
|
||||
function getOptionsForSpecType(specType) {
|
||||
if (specType === 'languages') {
|
||||
// Get languages from hidden input
|
||||
try {
|
||||
const supportedLanguages = JSON.parse(document.getElementById('supportedLanguages').value);
|
||||
return supportedLanguages.map(lang => ({value: lang, label: lang}));
|
||||
} catch (e) {
|
||||
console.error('Error parsing supported languages:', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return SPEC_OPTIONS[specType] || [];
|
||||
}
|
||||
|
||||
function switchToQuickImport() {
|
||||
document.getElementById('scraperSelect').value = 'quick_import';
|
||||
updateFormFields();
|
||||
formUtils.resetForm();
|
||||
hideSuccessAlert();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
// Function to show success message with Import New button
|
||||
function showSuccessWithImportNew(message) {
|
||||
const alert = document.getElementById('successAlert');
|
||||
const messageElement = document.getElementById('successMessage');
|
||||
|
||||
// If alert doesn't exist, use notification
|
||||
if (!alert) {
|
||||
showNotification(message, 'success');
|
||||
return;
|
||||
}
|
||||
|
||||
messageElement.textContent = message;
|
||||
alert.style.display = 'block';
|
||||
alert.classList.add('show');
|
||||
|
||||
// Scroll to the top to make the alert visible
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function hideSuccessAlert() {
|
||||
const alert = document.getElementById('successAlert');
|
||||
if (alert) {
|
||||
alert.classList.remove('show');
|
||||
alert.style.display = 'none';
|
||||
|
||||
// Clear the message
|
||||
const messageElement = document.getElementById('successMessage');
|
||||
if (messageElement) {
|
||||
messageElement.textContent = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateSpecField(fieldId, value) {
|
||||
const targetInput = document.getElementById(`${fieldId}Spec`);
|
||||
if (targetInput) {
|
||||
@@ -984,35 +1237,6 @@ const formUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
setCheckboxValues(items, prefix) {
|
||||
if (!items) return;
|
||||
|
||||
const values = Array.isArray(items) ? items : items.split(',');
|
||||
values.forEach(value => {
|
||||
const checkboxId = `${prefix}-${value.replace(/[^a-zA-Z0-9]/g, '')}`;
|
||||
const checkbox = document.getElementById(checkboxId);
|
||||
if (checkbox) checkbox.checked = true;
|
||||
});
|
||||
},
|
||||
|
||||
setCatalogs(type, catalogs) {
|
||||
if (!type || !catalogs) return;
|
||||
|
||||
const container = document.getElementById(`${type}Catalogs`);
|
||||
if (!container) return;
|
||||
|
||||
// Clear existing selections
|
||||
container.querySelectorAll('input[type="checkbox"]')
|
||||
.forEach(cb => cb.checked = false);
|
||||
|
||||
// Set new selections
|
||||
const catalogList = Array.isArray(catalogs) ? catalogs : catalogs.split(',');
|
||||
catalogList.forEach(cat => {
|
||||
const checkbox = container.querySelector(`input[value="${cat}"]`);
|
||||
if (checkbox) checkbox.checked = true;
|
||||
});
|
||||
},
|
||||
|
||||
transferTorrentFile() {
|
||||
const quickFile = document.getElementById('quickTorrentFile');
|
||||
const torrentFile = document.getElementById('torrentFile');
|
||||
@@ -1083,48 +1307,81 @@ function switchToManualImport(torrentData) {
|
||||
function applyTorrentData(torrentData) {
|
||||
if (!torrentData) return;
|
||||
|
||||
// Update basic specs display
|
||||
updateBasicTechnicalSpecs(torrentData);
|
||||
|
||||
// check title and if its empty then add title from torrentData
|
||||
if (!document.getElementById('title').value) {
|
||||
// Update title if empty
|
||||
if (!document.getElementById('title').value && torrentData.title) {
|
||||
document.getElementById('title').value = torrentData.title;
|
||||
}
|
||||
|
||||
// Set release date
|
||||
if (torrentData.created_at) {
|
||||
const date = new Date(torrentData.created_at);
|
||||
document.getElementById('createdAt').value = date.toISOString().split('T')[0];
|
||||
setupDateInput('createdAt', false, date);
|
||||
}
|
||||
|
||||
// Set technical specs
|
||||
const technicalFields = {
|
||||
'resolution': 'resolution',
|
||||
'quality': 'quality',
|
||||
'codec': 'videoCodec'
|
||||
// Map torrent data fields to spec types
|
||||
const specMapping = {
|
||||
resolution: torrentData.resolution,
|
||||
quality: torrentData.quality,
|
||||
codec: torrentData.codec,
|
||||
audio: torrentData.audio,
|
||||
hdr: torrentData.hdr,
|
||||
languages: torrentData.languages
|
||||
};
|
||||
|
||||
Object.entries(technicalFields).forEach(([dataKey, fieldId]) => {
|
||||
if (torrentData[dataKey]) {
|
||||
document.getElementById(fieldId).value = torrentData[dataKey];
|
||||
// Helper function to format value based on spec type
|
||||
function formatSpecValue(type, value) {
|
||||
if (!value) return 'Not Set';
|
||||
|
||||
// For array-like values (could be array or comma-separated string)
|
||||
if (typeof value === 'string' && value.includes(',') || Array.isArray(value)) {
|
||||
const valueArray = Array.isArray(value) ? value : value.split(',').map(v => v.trim());
|
||||
return valueArray.join(', ');
|
||||
}
|
||||
|
||||
// For single values, validate against available options
|
||||
const options = getOptionsForSpecType(type);
|
||||
const option = options.find(opt => opt.value.toLowerCase() === value.toLowerCase());
|
||||
return option ? option.value : value;
|
||||
}
|
||||
|
||||
// Update basic specs display
|
||||
Object.entries(specMapping).forEach(([type, value]) => {
|
||||
const formattedValue = formatSpecValue(type, value);
|
||||
updateSpecField(type, formattedValue);
|
||||
});
|
||||
|
||||
// Set checkbox-based fields
|
||||
formUtils.setCheckboxValues(torrentData.audio, 'audio');
|
||||
formUtils.setCheckboxValues(torrentData.hdr, 'hdr');
|
||||
formUtils.setCheckboxValues(torrentData.languages, 'lang');
|
||||
// Update basic technical specs display
|
||||
updateBasicTechnicalSpecs({
|
||||
resolution: specMapping.resolution,
|
||||
quality: specMapping.quality,
|
||||
codec: specMapping.codec,
|
||||
audio: specMapping.audio,
|
||||
hdr: specMapping.hdr,
|
||||
languages: specMapping.languages
|
||||
});
|
||||
|
||||
// Handle catalogs
|
||||
// Handle content type specific settings
|
||||
document.getElementById('metaType').value = torrentData.type;
|
||||
if (torrentData.type === 'movie' || torrentData.type === 'series') {
|
||||
formUtils.setCatalogs(torrentData.type, torrentData.catalog);
|
||||
if (torrentData.catalog) {
|
||||
const catalogs = Array.isArray(torrentData.catalog)
|
||||
? torrentData.catalog
|
||||
: torrentData.catalog.split(',').map(c => c.trim());
|
||||
|
||||
const container = document.getElementById(`${torrentData.type}Catalogs`);
|
||||
if (container) {
|
||||
catalogs.forEach(catalog => {
|
||||
const checkbox = container.querySelector(`input[value="${catalog}"]`);
|
||||
if (checkbox) checkbox.checked = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer torrent file or magnet link
|
||||
formUtils.transferTorrentFile();
|
||||
|
||||
// Set metaType
|
||||
document.getElementById('metaType').value = torrentData.type;
|
||||
// Update UI to reflect content type
|
||||
updateContentType();
|
||||
}
|
||||
|
||||
@@ -1134,76 +1391,42 @@ async function handleAddTorrent(submitBtn, loadingSpinner, forceImport = false,
|
||||
const torrentType = document.getElementById('torrentType').value;
|
||||
const isSportsContent = metaType === 'sports';
|
||||
|
||||
// Handle content metadata
|
||||
const metaId = document.getElementById('torrentImdbId')?.value;
|
||||
const title = document.getElementById('title').value;
|
||||
const poster = document.getElementById('poster').value;
|
||||
const background = document.getElementById('background').value;
|
||||
const logo = document.getElementById('logo').value;
|
||||
const createdAt = document.getElementById('createdAt').value;
|
||||
const uploaderName = document.getElementById('uploaderName').value.trim() || 'Anonymous';
|
||||
|
||||
// Validate required fields
|
||||
if (isSportsContent && !title) {
|
||||
showNotification('Title is required for sports content.', 'error');
|
||||
resetButton(submitBtn, loadingSpinner);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!createdAt) {
|
||||
showNotification('Created At is required.', 'error');
|
||||
resetButton(submitBtn, loadingSpinner);
|
||||
return;
|
||||
}
|
||||
// Get current spec values
|
||||
const specValues = getSpecValues();
|
||||
|
||||
// Add basic metadata to formData
|
||||
formData.append('meta_type', metaType);
|
||||
formData.append('torrent_type', torrentType);
|
||||
formData.append('created_at', createdAt);
|
||||
formData.append('uploader', uploaderName);
|
||||
formData.append('created_at', document.getElementById('createdAt').value);
|
||||
formData.append('uploader', document.getElementById('uploaderName').value.trim() || 'Anonymous');
|
||||
|
||||
if (!isSportsContent && metaId) {
|
||||
formData.append('meta_id', metaId);
|
||||
// Add content metadata
|
||||
if (!isSportsContent && document.getElementById('torrentImdbId')?.value) {
|
||||
formData.append('meta_id', document.getElementById('torrentImdbId').value);
|
||||
}
|
||||
|
||||
// Add optional metadata fields if provided
|
||||
const title = document.getElementById('title').value;
|
||||
if (!title && isSportsContent) {
|
||||
showNotification('Title is required for sports content.', 'error');
|
||||
resetButton(submitBtn, loadingSpinner);
|
||||
return;
|
||||
}
|
||||
if (title) formData.append('title', title);
|
||||
if (poster) formData.append('poster', poster);
|
||||
if (background) formData.append('background', background);
|
||||
if (logo) formData.append('logo', logo);
|
||||
|
||||
// Add optional metadata
|
||||
['poster', 'background', 'logo'].forEach(field => {
|
||||
const value = document.getElementById(field)?.value;
|
||||
if (value) formData.append(field, value);
|
||||
});
|
||||
|
||||
// Add technical specifications
|
||||
const resolution = document.getElementById('resolution').value;
|
||||
const quality = document.getElementById('quality').value;
|
||||
const videoCodec = document.getElementById('videoCodec').value;
|
||||
|
||||
if (resolution) formData.append('resolution', resolution);
|
||||
if (quality) formData.append('quality', quality);
|
||||
if (videoCodec) formData.append('codec', videoCodec);
|
||||
|
||||
// Handle multiple audio codecs
|
||||
const selectedAudioCodecs = Array.from(
|
||||
document.querySelectorAll('input[name="audioCodecs"]:checked')
|
||||
).map(el => el.value);
|
||||
if (selectedAudioCodecs.length > 0) {
|
||||
formData.append('audio', selectedAudioCodecs.join(','));
|
||||
}
|
||||
|
||||
// Handle HDR formats
|
||||
const selectedHdrFormats = Array.from(
|
||||
document.querySelectorAll('input[name="hdrFormats"]:checked')
|
||||
).map(el => el.value);
|
||||
if (selectedHdrFormats.length > 0) {
|
||||
formData.append('hdr', selectedHdrFormats.join(','));
|
||||
}
|
||||
|
||||
// Handle languages
|
||||
const selectedLanguages = Array.from(document.querySelectorAll('input[name="languages"]:checked'))
|
||||
.map(el => el.value);
|
||||
if (selectedLanguages.length > 0) {
|
||||
formData.append('languages', selectedLanguages.join(','));
|
||||
}
|
||||
Object.entries(specValues).forEach(([key, value]) => {
|
||||
if (value && value !== 'Not Set') {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle catalogs
|
||||
if (isSportsContent) {
|
||||
const sportsCatalog = document.getElementById('sportsCatalog').value;
|
||||
if (!sportsCatalog) {
|
||||
@@ -1213,7 +1436,6 @@ async function handleAddTorrent(submitBtn, loadingSpinner, forceImport = false,
|
||||
}
|
||||
formData.append('catalogs', sportsCatalog);
|
||||
} else {
|
||||
// Handle movie/series catalogs
|
||||
const catalogInputs = document.querySelectorAll(`#${metaType}Catalogs input[name="catalogs"]:checked`);
|
||||
const catalogs = Array.from(catalogInputs).map(el => el.value);
|
||||
if (catalogs.length > 0) {
|
||||
@@ -1221,8 +1443,9 @@ async function handleAddTorrent(submitBtn, loadingSpinner, forceImport = false,
|
||||
}
|
||||
}
|
||||
|
||||
const addTitleToPoster = document.getElementById('addTitleToPoster').checked;
|
||||
formData.append('is_add_title_to_poster', addTitleToPoster.toString());
|
||||
// Handle add title to poster option
|
||||
formData.append('is_add_title_to_poster',
|
||||
document.getElementById('addTitleToPoster').checked.toString());
|
||||
|
||||
// Handle torrent file/magnet
|
||||
const magnetLink = document.getElementById('magnetLink').value;
|
||||
@@ -1246,16 +1469,11 @@ async function handleAddTorrent(submitBtn, loadingSpinner, forceImport = false,
|
||||
formData.append('torrent_file', torrentFile);
|
||||
}
|
||||
|
||||
// Add force import flag if needed
|
||||
if (forceImport) {
|
||||
formData.append('force_import', 'true');
|
||||
}
|
||||
|
||||
// Add annotated files if available
|
||||
if (annotatedFiles) {
|
||||
formData.append('file_data', JSON.stringify(annotatedFiles));
|
||||
}
|
||||
// Add force import and annotated files if present
|
||||
if (forceImport) formData.append('force_import', 'true');
|
||||
if (annotatedFiles) formData.append('file_data', JSON.stringify(annotatedFiles));
|
||||
|
||||
// Submit the form
|
||||
try {
|
||||
const response = await fetch('/scraper/torrent', {
|
||||
method: 'POST',
|
||||
@@ -1287,7 +1505,7 @@ async function handleAddTorrent(submitBtn, loadingSpinner, forceImport = false,
|
||||
} else if (data.detail) {
|
||||
showNotification(data.detail, 'error');
|
||||
} else {
|
||||
showNotification(data.status, 'success');
|
||||
showSuccessWithImportNew(data.status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error submitting torrent:', error);
|
||||
@@ -1602,8 +1820,8 @@ async function submitScraperForm() {
|
||||
// Initial update for form fields on page load
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
handleInitialSetup();
|
||||
setupAdvancedToggle();
|
||||
updateBasicTechnicalSpecs();
|
||||
setupFieldChangeHandlers();
|
||||
setupDateInput('createdAt');
|
||||
setupDateInput('createdAt', true);
|
||||
});
|
||||
document.getElementById('spiderName').addEventListener('change', toggleSpiderSpecificFields);
|
||||
+3
-2
@@ -263,7 +263,8 @@ async def add_torrent(
|
||||
await torrent_stream.delete()
|
||||
else:
|
||||
return {
|
||||
"status": f"Torrent already exists and attached to meta id: {torrent_stream.meta_id}"
|
||||
"status": f"⚠️ Torrent {info_hash} already exists and is attached to meta ID: {torrent_stream.meta_id}. "
|
||||
f"Thank you for trying to contribute ✨. If the torrent is not visible, please contact support with the Torrent InfoHash."
|
||||
}
|
||||
|
||||
# Add technical specifications to torrent_data
|
||||
@@ -524,7 +525,7 @@ async def add_torrent(
|
||||
)
|
||||
|
||||
return {
|
||||
"status": f"Successfully added torrent: {torrent_stream.id} for {title}. Thanks for your contribution."
|
||||
"status": f"🎉 Successfully added torrent: {torrent_stream.id} for {title} ({torrent_stream.meta_id}). Thanks for your contribution! 🙌"
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user