mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-07 12:05:35 +00:00
revert base
This commit is contained in:
parent
ad132b7c53
commit
28b0338f23
@ -1,81 +1,44 @@
|
|||||||
import axios from "axios";
|
import axios from 'axios'
|
||||||
import type { AxiosResponse } from "axios";
|
import type {MediaItemResponse} from '@/api/interfaces'
|
||||||
import type { DownloadResponse, MediaItemResponse } from "@/api/interfaces";
|
|
||||||
|
const BASE_URL = 'http://localhost:8000/api'
|
||||||
const BASE_URL = "http://localhost:8000/api";
|
|
||||||
|
function get(url: string): Promise<any> {
|
||||||
const api = axios.create({
|
return axios.get(`${BASE_URL}${url}`)
|
||||||
baseURL: BASE_URL,
|
.then(response => response.data)
|
||||||
});
|
.catch(error => {
|
||||||
|
throw error;
|
||||||
async function get<T>(url: string): Promise<AxiosResponse<T>> {
|
});
|
||||||
return api.get(url);
|
}
|
||||||
}
|
|
||||||
|
function post(url: string, data: any): Promise<any> {
|
||||||
async function post<T>(url: string, data: any): Promise<AxiosResponse<T>> {
|
return axios.post(`${BASE_URL}${url}`, data)
|
||||||
return api.post(url, data);
|
.then(response => response.data)
|
||||||
}
|
.catch(error => {
|
||||||
|
throw error;
|
||||||
export default function search(
|
});
|
||||||
query: string,
|
}
|
||||||
type: string
|
|
||||||
): Promise<AxiosResponse<MediaItemResponse>> {
|
export default function search(query: string, type: string) : Promise<MediaItemResponse> {
|
||||||
return get(`/search?search_terms=${query}&type=${type}`);
|
return get(`/search?search_terms=${query}&type=${type}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEpisodesInfo(
|
export async function getEpisodesInfo(mediaId: number, mediaSlug: string, mediaType: string): Promise<Response> {
|
||||||
mediaId: number,
|
const url = `${BASE_URL}/search/get_episodes_info?media_id=${mediaId}&media_slug=${mediaSlug}&type_media=${mediaType}`;
|
||||||
mediaSlug: string,
|
return await fetch(url, {
|
||||||
mediaType: string
|
method: 'GET',
|
||||||
): Promise<Response> {
|
headers: {
|
||||||
const url = `/search/get_episodes_info?media_id=${mediaId}&media_slug=${mediaSlug}&type_media=${mediaType}`;
|
'Content-Type': 'text/event-stream'
|
||||||
return fetch(`${BASE_URL}${url}`, {
|
}
|
||||||
method: "GET",
|
});
|
||||||
headers: {
|
}
|
||||||
"Content-Type": "text/event-stream",
|
|
||||||
},
|
export async function downloadFilm(mediaId: number, mediaSlug: string, mediaType: string): Promise<Response> {
|
||||||
});
|
const url = `/download/`;
|
||||||
}
|
const data = {
|
||||||
|
media_id: mediaId,
|
||||||
export async function getPreview(
|
media_slug: mediaSlug,
|
||||||
mediaId: number,
|
type_media: mediaType
|
||||||
mediaSlug: string,
|
};
|
||||||
mediaType: string
|
return post(url, data);
|
||||||
): Promise<AxiosResponse<any>> {
|
}
|
||||||
const url = `/search/get_preview?media_id=${mediaId}&media_slug=${mediaSlug}&type_media=${mediaType}`;
|
|
||||||
return get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadMedia(
|
|
||||||
mediaId: number,
|
|
||||||
mediaSlug: string,
|
|
||||||
mediaType: string,
|
|
||||||
downloadId?: number,
|
|
||||||
tvSeriesEpisodeId?: number
|
|
||||||
): Promise<AxiosResponse<DownloadResponse>> {
|
|
||||||
const url = `/download/`;
|
|
||||||
const data = {
|
|
||||||
media_id: mediaId,
|
|
||||||
media_slug: mediaSlug,
|
|
||||||
type_media: mediaType,
|
|
||||||
download_id: downloadId,
|
|
||||||
tv_series_episode_id: tvSeriesEpisodeId,
|
|
||||||
};
|
|
||||||
return post(url, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const downloadFilm = (mediaId: number, mediaSlug: string) =>
|
|
||||||
downloadMedia(mediaId, mediaSlug, "MOVIE");
|
|
||||||
export const downloadTvSeries = (
|
|
||||||
mediaId: number,
|
|
||||||
mediaSlug: string,
|
|
||||||
downloadId: number,
|
|
||||||
tvSeriesEpisodeId?: number
|
|
||||||
) => downloadMedia(mediaId, mediaSlug, "TV", downloadId, tvSeriesEpisodeId);
|
|
||||||
export const downloadAnimeFilm = (mediaId: number, mediaSlug: string) =>
|
|
||||||
downloadMedia(mediaId, mediaSlug, "OVA");
|
|
||||||
export const downloadAnimeSeries = (
|
|
||||||
mediaId: number,
|
|
||||||
mediaSlug: string,
|
|
||||||
downloadId: number
|
|
||||||
) => downloadMedia(mediaId, mediaSlug, "TV_ANIME", downloadId);
|
|
@ -56,9 +56,4 @@ export interface Season {
|
|||||||
|
|
||||||
export interface SeasonResponse {
|
export interface SeasonResponse {
|
||||||
episodes: Season;
|
episodes: Season;
|
||||||
}
|
|
||||||
|
|
||||||
export interface DownloadResponse {
|
|
||||||
error: string;
|
|
||||||
message: string;
|
|
||||||
}
|
}
|
@ -1,335 +1,267 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type {Episode, MediaItem, SeasonResponse} from "@/api/interfaces";
|
import type {Episode, MediaItem, Season, SeasonResponse} from "@/api/interfaces";
|
||||||
import { onBeforeMount, onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { getEpisodesInfo, getPreview } from "@/api/api";
|
import {downloadFilm, getEpisodesInfo} from "@/api/api";
|
||||||
import {
|
|
||||||
alertDownload,
|
const route = useRoute()
|
||||||
handleMovieDownload,
|
|
||||||
handleOVADownload,
|
const item: MediaItem = JSON.parse(<string>route.params.item)
|
||||||
handleTVAnimeDownload,
|
const imageUrl: string = <string>route.params.imageUrl
|
||||||
handleTvAnimeEpisodesDownload,
|
const animeEpisodes = ref<Episode[]>([])
|
||||||
handleTVDownload, handleTVEpisodesDownload
|
const tvShowEpisodes = ref<any[]>([])
|
||||||
} from "@/api/utils";
|
const loading = ref(false)
|
||||||
|
const selectingEpisodes = ref(false)
|
||||||
const route = useRoute()
|
|
||||||
|
onMounted(async () => {
|
||||||
const item: MediaItem = JSON.parse(<string>route.params.item)
|
if (['MOVIE', 'OVA', 'SPECIAL'].includes(item.type)) {
|
||||||
const imageUrl: string = <string>route.params.imageUrl
|
return
|
||||||
const animeEpisodes = ref<Episode[]>([])
|
} else {
|
||||||
const totalEpisodes = ref<number>(0)
|
loading.value = true;
|
||||||
const tvShowEpisodes = ref<any[]>([])
|
const response = await getEpisodesInfo(item.id, item.slug, item.type)
|
||||||
const loading = ref(false)
|
if (response && response.body) {
|
||||||
const selectingEpisodes = ref(false)
|
loading.value = false;
|
||||||
const selectedEpisodes = ref<Episode[]>([])
|
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
||||||
const previewItem = ref<MediaItem>(item)
|
while (true) {
|
||||||
|
const {value, done} = await reader.read();
|
||||||
onBeforeMount(async () => {
|
if (done) {
|
||||||
const res = await getPreview(item.id, item.slug, item.type)
|
window.scrollTo(0, 0)
|
||||||
if (res && res.data) {
|
break;
|
||||||
previewItem.plot = res.data.plot
|
}
|
||||||
}
|
if (item.type === 'TV_ANIME') {
|
||||||
})
|
const episodesData:Episode = JSON.parse(value.trim());
|
||||||
|
animeEpisodes.value.push(episodesData);
|
||||||
onMounted(async () => {
|
} else {
|
||||||
loading.value = true;
|
const episodesData:SeasonResponse = JSON.parse(value.trim());
|
||||||
if (['MOVIE', 'OVA', 'SPECIAL'].includes(item.type)) {
|
for (const seasonKey in episodesData.episodes) {
|
||||||
loading.value = false;
|
const season = episodesData.episodes[seasonKey];
|
||||||
return
|
const episodes:Episode[] = [];
|
||||||
} else {
|
for (const episodeKey in season) {
|
||||||
const response = await getEpisodesInfo(item.id, item.slug, item.type)
|
const episode:Episode = season[episodeKey];
|
||||||
if (response && response.body) {
|
episodes.push(episode);
|
||||||
loading.value = false;
|
}
|
||||||
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
tvShowEpisodes.value.push(episodes);
|
||||||
while (true) {
|
}
|
||||||
const {value, done} = await reader.read();
|
}
|
||||||
if (done) {
|
}
|
||||||
window.scrollTo(0, 0)
|
}
|
||||||
break;
|
}
|
||||||
}
|
})
|
||||||
if (item.type === 'TV_ANIME') {
|
|
||||||
const episodesData:Episode = JSON.parse(value.trim());
|
const toggleEpisodeSelection = () => {
|
||||||
animeEpisodes.value.push(episodesData);
|
selectingEpisodes.value = !selectingEpisodes.value
|
||||||
totalEpisodes.value = episodesData.episode_total;
|
}
|
||||||
} else {
|
|
||||||
const episodesData:SeasonResponse = JSON.parse(value.trim());
|
const downloadItems = async () => {
|
||||||
for (const seasonKey in episodesData.episodes) {
|
try {
|
||||||
const season = episodesData.episodes[seasonKey];
|
if (item.type === 'MOVIE') {
|
||||||
const episodes:Episode[] = [];
|
const res = await downloadFilm(item.id, item.slug, item.type)
|
||||||
for (const episodeKey in season) {
|
if (res.error) {
|
||||||
const episode:Episode = season[episodeKey];
|
throw new Error(res.error + ' - ' + res.message)
|
||||||
episodes.push(episode);
|
}
|
||||||
}
|
alertDownload()
|
||||||
tvShowEpisodes.value.push(episodes);
|
return
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
}
|
alertDownload(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
const alertDownload = (message?: any) => {
|
||||||
const toggleEpisodeSelection = () => {
|
if (message) {
|
||||||
selectingEpisodes.value = !selectingEpisodes.value
|
alert(message)
|
||||||
selectedEpisodes.value = []
|
return;
|
||||||
}
|
}
|
||||||
|
alert('Il downlaod è iniziato, il file sarà disponibile tra qualche minuto nella cartella \'Video\' del progetto...')
|
||||||
const toggleEpisodeSelect = (episode: Episode, seasonNumber?: number) => {
|
}
|
||||||
if (selectedEpisodes.value.includes(episode)) {
|
</script>
|
||||||
selectedEpisodes.value = selectedEpisodes.value.filter(e => e !== episode)
|
|
||||||
} else {
|
<template>
|
||||||
if (seasonNumber) {
|
<div class="details-container">
|
||||||
episode.season_index = seasonNumber
|
<div class="details-card">
|
||||||
}
|
|
||||||
selectedEpisodes.value.push(episode)
|
<!--HEADER SECTION-->
|
||||||
}
|
<div class="details-header">
|
||||||
}
|
<img :src="imageUrl" :alt="item.name" class="details-image" />
|
||||||
|
<div class="details-title-container">
|
||||||
const downloadSelectedEpisodes = async () => {
|
<h1 class="details-title">{{ item.name }}</h1>
|
||||||
try {
|
<h3>★ {{ item.score }}</h3>
|
||||||
switch (item.type) {
|
<div class="details-description">
|
||||||
case 'TV':
|
<p v-if="item.type == 'TV_ANIME'">{{ item.plot }}</p>
|
||||||
await handleTVEpisodesDownload(selectedEpisodes.value, item);
|
<p v-else-if="tvShowEpisodes.length > 0">{{ tvShowEpisodes[0][0].plot }}</p>
|
||||||
case 'TV_ANIME':
|
</div>
|
||||||
await handleTvAnimeEpisodesDownload(selectedEpisodes.value, item);
|
<h3 v-if="animeEpisodes.length > 0 && !loading">Numero episodi: {{ animeEpisodes[0].episode_total }}</h3>
|
||||||
break;
|
<h3 v-if="tvShowEpisodes.length > 0 && !loading">Numero stagioni: {{ tvShowEpisodes.length }}</h3>
|
||||||
default:
|
<hr style="opacity: 0.2"/>
|
||||||
throw new Error('Tipo di media non supportato');
|
<div class="download-section">
|
||||||
}
|
<button :disabled="loading || selectingEpisodes" @click="downloadItems">Scarica {{['TV_ANIME', 'TV'].includes(item.type)? 'tutto' : ''}}</button>
|
||||||
toggleEpisodeSelection();
|
<template v-if="!loading && ['TV_ANIME', 'TV'].includes(item.type)">
|
||||||
} catch (error) {
|
<button @click="toggleEpisodeSelection">{{selectingEpisodes ? 'Disattiva' : 'Attiva'}} selezione episodi</button>
|
||||||
alertDownload(error);
|
<button>Download episodi</button>
|
||||||
}
|
</template>
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
const downloadAllItems = async () => {
|
</div>
|
||||||
try {
|
|
||||||
switch (item.type) {
|
<!--SERIES SECTION-->
|
||||||
case 'TV':
|
<div v-if="!loading && ['TV_ANIME', 'TV'].includes(item.type)" :class="item.type == 'TV_ANIME' ? 'episodes-container' : 'season-container'">
|
||||||
await handleTVDownload(tvShowEpisodes.value, item);
|
<div v-if="animeEpisodes.length == 0 && tvShowEpisodes.length == 0">
|
||||||
case 'MOVIE':
|
<p>Non ci sono episodi...</p>
|
||||||
await handleMovieDownload(item);
|
</div>
|
||||||
break;
|
<div v-else-if="item.type == 'TV_ANIME'" v-for="episode in animeEpisodes" :key="episode.id" class="episode-item">
|
||||||
case 'TV_ANIME':
|
<div class="episode-title">Episodio {{ episode.number }}</div>
|
||||||
await handleTVAnimeDownload(totalEpisodes.value, item);
|
</div>
|
||||||
break;
|
<div v-else-if="item.type == 'TV'" v-for="(season, index) in tvShowEpisodes" class="season-item">
|
||||||
case 'OVA':
|
<div class="season-title">Stagione {{ index + 1 }}</div>
|
||||||
case 'SPECIAL':
|
<div class="episode-container">
|
||||||
await handleOVADownload(item);
|
<div v-for="episode in season" :key="episode.id" class="episode-item">
|
||||||
break;
|
<div class="episode-title">
|
||||||
default:
|
Episodio {{ episode.number }} -
|
||||||
throw new Error('Tipo di media non supportato');
|
{{episode.name.slice(0, 40) + (episode.name.length > 39 ? '...' : '')}}
|
||||||
}
|
</div>
|
||||||
} catch (error) {
|
</div>
|
||||||
alertDownload(error);
|
</div>
|
||||||
}
|
</div>
|
||||||
};
|
</div>
|
||||||
</script>
|
|
||||||
|
<!--MOVIES SECTION-->
|
||||||
<template>
|
<div v-else-if="!loading && ['MOVIE', 'OVA', 'SPECIAL'].includes(item.type)">
|
||||||
<div class="details-container">
|
<p>Questo è un {{item.type}}</p>
|
||||||
<div class="details-card">
|
</div>
|
||||||
|
|
||||||
<!--HEADER SECTION-->
|
<!--LOADING SECTION-->
|
||||||
<div class="details-header">
|
<div v-else-if="loading">
|
||||||
<img :src="imageUrl" :alt="item.name" class="details-image" />
|
<p>Loading...</p>
|
||||||
<div class="details-title-container">
|
</div>
|
||||||
<h1 class="details-title">{{ item.name }}</h1>
|
</div>
|
||||||
<h3>★ {{ item.score }}</h3>
|
</div>
|
||||||
<div class="details-description">
|
</template>
|
||||||
<p>{{ item.plot }}</p>
|
|
||||||
</div>
|
<style scoped>
|
||||||
<h3 v-if="animeEpisodes.length > 0 && !loading">Numero episodi: {{ totalEpisodes }}</h3>
|
h3 {
|
||||||
<h3 v-if="tvShowEpisodes.length > 0 && !loading">Numero stagioni: {{ tvShowEpisodes.length }}</h3>
|
padding-top: 10px;
|
||||||
<hr style="opacity: 0.2; margin-top: 10px"/>
|
padding-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
<!--DOWNLOAD SECTION-->
|
}
|
||||||
<div class="download-section">
|
.details-container {
|
||||||
<button :disabled="loading || selectingEpisodes"
|
padding-top: 10px;
|
||||||
@click.prevent="downloadAllItems">
|
justify-content: center;
|
||||||
Scarica {{['TV_ANIME', 'TV'].includes(item.type)? 'tutto' : ''}}
|
align-items: center;
|
||||||
</button>
|
min-height: 100vh;
|
||||||
<template v-if="!loading && ['TV_ANIME', 'TV'].includes(item.type)">
|
width: 200%;
|
||||||
<button @click="toggleEpisodeSelection">
|
color: #fff;
|
||||||
{{selectingEpisodes ? 'Disattiva' : 'Attiva'}} selezione episodi
|
}
|
||||||
</button>
|
|
||||||
<button :disabled="selectedEpisodes.length == 0"
|
.details-card {
|
||||||
@click="downloadSelectedEpisodes">
|
width: 100%;
|
||||||
Download episodi selezionati
|
max-width: 1200px;
|
||||||
</button>
|
background-color: #232323;
|
||||||
</template>
|
padding: 2rem;
|
||||||
</div>
|
border-radius: 0.5rem;
|
||||||
</div>
|
}
|
||||||
</div>
|
|
||||||
|
.details-header {
|
||||||
<!--SERIES SECTION-->
|
display: flex;
|
||||||
<div v-if="!loading && ['TV_ANIME', 'TV'].includes(item.type)"
|
align-items: flex-start;
|
||||||
:class="item.type == 'TV_ANIME' ? 'episodes-container' : 'season-container'">
|
margin-bottom: 2rem;
|
||||||
<div v-if="animeEpisodes.length == 0 && tvShowEpisodes.length == 0">
|
}
|
||||||
<p>Non ci sono episodi...</p>
|
|
||||||
</div>
|
.details-image {
|
||||||
<div v-else-if="item.type == 'TV_ANIME'"
|
width: 295px;
|
||||||
v-for="episode in animeEpisodes"
|
margin-right: 2rem;
|
||||||
:key="episode.id"
|
border-radius: 0.5rem;
|
||||||
class="episode-item"
|
}
|
||||||
:style="{ backgroundColor: selectedEpisodes.includes(episode) ? '#42b883' : '#333' }"
|
|
||||||
@click="selectingEpisodes ? toggleEpisodeSelect(episode) : null">
|
@media (max-width: 1008px) {
|
||||||
<div class="episode-title">Episodio {{ episode.number }}</div>
|
.details-container {
|
||||||
</div>
|
width: 100%;
|
||||||
<div v-else-if="item.type == 'TV'" v-for="(season, index) in tvShowEpisodes" v-bind:key="season.number" class="season-item">
|
}
|
||||||
<div class="season-title">Stagione {{ index + 1 }}</div>
|
.details-header {
|
||||||
<div class="episode-container">
|
flex-direction: column;
|
||||||
<div v-for="episode in season" :key="episode.id">
|
align-items: center;
|
||||||
<div class="episode-item"
|
}
|
||||||
:style="{ backgroundColor: selectedEpisodes.includes(episode) ? '#42b883' : '#333' }"
|
|
||||||
@click="selectingEpisodes ? toggleEpisodeSelect(episode, index) : null">
|
.details-image {
|
||||||
<div class="episode-title">
|
max-width: 100%;
|
||||||
Episodio {{ episode.number }} -
|
margin-right: 0;
|
||||||
{{episode.name.slice(0, 40) + (episode.name.length > 39 ? '...' : '')}}
|
margin-bottom: 1rem;
|
||||||
</div>
|
}
|
||||||
</div>
|
}
|
||||||
</div>
|
|
||||||
</div>
|
.details-title-container {
|
||||||
</div>
|
flex: 1;
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<!--MOVIES SECTION-->
|
.details-title {
|
||||||
<div v-else-if="!loading && ['MOVIE', 'OVA', 'SPECIAL'].includes(item.type)">
|
font-size: 2rem;
|
||||||
<p>Questo è un {{item.type}} (QUESTO TESTO E' A SCOPO DI TEST)</p>
|
margin-bottom: 1rem;
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<!--LOADING SECTION-->
|
.details-description {
|
||||||
<div v-else-if="loading">
|
line-height: 1.5;
|
||||||
<p>Loading...</p>
|
}
|
||||||
</div>
|
|
||||||
</div>
|
.episodes-container {
|
||||||
</div>
|
display: grid;
|
||||||
</template>
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
<style scoped>
|
}
|
||||||
h3 {
|
|
||||||
padding-top: 10px;
|
.episode-item {
|
||||||
padding-bottom: 10px;
|
background-color: #333;
|
||||||
font-weight: bold;
|
padding: 1rem;
|
||||||
}
|
border-radius: 0.5rem;
|
||||||
.details-container {
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
padding-top: 10px;
|
transition: transform 0.3s ease;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
align-items: center;
|
}
|
||||||
min-height: 100vh;
|
|
||||||
width: 200%;
|
.season-item {
|
||||||
color: #fff;
|
background-color: #2a2a2a;
|
||||||
}
|
padding: 1rem;
|
||||||
|
margin-top: 5px;
|
||||||
.details-card {
|
margin-bottom: 5px;
|
||||||
width: 100%;
|
border-radius: 0.5rem;
|
||||||
max-width: 1200px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
background-color: #232323;
|
}
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 0.5rem;
|
.season-item div {
|
||||||
}
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||||
.details-header {
|
gap: 1rem;
|
||||||
display: flex;
|
}
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 2rem;
|
.season-title {
|
||||||
}
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
.details-image {
|
padding-bottom: 15px;
|
||||||
width: 295px;
|
}
|
||||||
margin-right: 2rem;
|
|
||||||
border-radius: 0.5rem;
|
.episode-item:hover {
|
||||||
}
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
@media (max-width: 1008px) {
|
|
||||||
.details-container {
|
.episode-title {
|
||||||
width: 100%;
|
font-size: 1.2rem;
|
||||||
}
|
font-weight: bold;
|
||||||
.details-header {
|
}
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
.download-section {
|
||||||
}
|
margin-top: 1rem;
|
||||||
|
flex: fit-content;
|
||||||
.details-image {
|
flex-direction: row;
|
||||||
max-width: 100%;
|
button {
|
||||||
margin-right: 0;
|
margin-right: 5px;
|
||||||
margin-bottom: 1rem;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
.details-title-container {
|
.episodes-container {
|
||||||
flex: 1;
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-title {
|
.season-item div {
|
||||||
font-size: 2rem;
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
margin-bottom: 1rem;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-description {
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.episodes-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.episode-item {
|
|
||||||
background-color: #333;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.season-item {
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
padding: 1rem;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.season-item div {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.season-title {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.episode-item:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.episode-title {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.download-section {
|
|
||||||
margin-top: 1rem;
|
|
||||||
flex: fit-content;
|
|
||||||
flex-direction: row;
|
|
||||||
button {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.episodes-container {
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.season-item div {
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
Loading…
x
Reference in New Issue
Block a user