panjareh
Version:
Panjareh using aparat and phoenix-video-player to play videos on desktops and tvs.
436 lines (408 loc) • 12.8 kB
JavaScript
import { GishehAPI, ProductAPI, UserApi } from "../../api";
import { resolveImage } from "../../api/product";
import consts from "../../config/consts";
import playerConsts from "../../config/playerConsts";
import cookiesName from "../../enums/cookies-name";
import {
appendUuidOnUrls,
getAds,
getCollectDataConfig,
} from "../../services/player-service";
class DesktopPlayerController {
alias;
isAuthenticated;
userToken;
uuid;
product;
returnUrl;
constructor(data) {
const {
alias,
isAuthenticated,
token: userToken,
uuid,
product,
return_url,
} = data;
this.alias = alias;
this.isAuthenticated = isAuthenticated;
this.userToken = userToken;
this.uuid = uuid;
this.product = product;
this.returnUrl = return_url;
}
getAdvertisement = async (productResponse) => {
const config = { ...productResponse };
try {
return await getAds()
.then((responseAds) => {
if (responseAds.data && responseAds.data.length > 0) {
const { ad_bank } = responseAds.data[0];
config.ads = ad_bank;
}
return config;
})
.catch(() => {
return productResponse;
// console.error(`ADS: Failed load ads. ${e?.message}`);
});
} catch {
return productResponse;
}
};
fetchStreamLink = async () => {
if (!this.product) throw new Error("product is required");
return await ProductAPI.getStreamUrl(this.product.getToken())
.then(
async (response) => {
return await this.getAdvertisement(response);
},
(err) => {
console.error(
`PLAYER : Failed load movie for alias : ${this.alias}, message:${err.message}`
);
return null;
}
)
.catch((err) => {
console.error(`PLAYER : ${err?.message}`);
return null;
});
};
checkTicketStatus = async () => {
try {
const response = await GishehAPI.checkStatus(this.product.getId());
if (response) {
const { status, token } = response.body;
if (status === 1 && token) {
return { ticketStatus: 1, hasTicket: true, gishehToken: token };
}
}
return { ticketStatus: 0, hasTicket: false, gishehToken: null };
} catch (error) {
const { status } = error;
if (status === 404) {
return { ticketStatus: 0, hasTicket: false, gishehToken: null };
}
return { ticketStatus: 0, hasTicket: false, gishehToken: null };
}
};
fetchEkranStreamLink = async () => {
const { ticketStatus, hasTicket, gishehToken } =
await this.checkTicketStatus();
if (hasTicket) {
const data = await ProductAPI.getEkranStreamUrl(
this.product.getToken(),
gishehToken
)
.then(
async (response) => {
return await this.getAdvertisement(response);
},
(err) => {
console.error(
`PLAYER : EKRAN - Failed load movie for alias : ${this.alias}, message:${err.message}`
);
return null;
}
)
.catch((err) => {
console.error(`PLAYER : EKRAN - ${err?.message}`);
return { ticketStatus, hasTicket };
});
return { ...data, ticketStatus, hasTicket };
} else {
return { ticketStatus, hasTicket };
}
};
getUserInternetInfo = async () => {
try {
return await UserApi.checkInternet()
.then((response) => {
if (response) {
const { body } = response;
if (body) {
return {
title: body.message_title,
message: body.message,
};
}
}
return null;
})
.catch((e) => {
console.info(
`Failed fetch Dana(ips) data for movie alias: ${
this.alias
}. Message:${e.message || e}`
);
});
} catch (e) {
console.info(
`Failed fetch Dana(ips) data for movie alias: ${this.alias}. Message:${
e.message || e
}`
);
}
return null;
};
getNeshanehInfo = async () => {
// region fetch Neshane data
try {
return await ProductAPI.neshaneh(this.product.nid).then((response) => {
if (response) {
const { body } = response;
if (body) {
const markers = {
markerTip: {
display: false,
skip: true,
},
markers: null,
};
markers.markers = body.map((mark) => {
return {
time: mark.start,
duration: mark.end - mark.start,
};
});
return markers;
}
}
return null;
});
} catch (e) {
if (e === 404) {
console.info(
`Movie with alias: ${this.alias}, doesn't have Neshane Tags.`
);
} else {
console.info(
`Failed fetch Neshane data for movie alias: ${this.alias}.`
);
}
}
return null;
};
getNextEpisodeInfo = () => {
// next epsidoe plugin with auto advance
const nextEpisode = this.product.getNextEpisode();
if (nextEpisode && this.product.titration) {
const { lastStart } = this.product.titration;
const nextEpisodeLink = `${nextEpisode.alias}?return_url=${consts.SITE_URL}/p${nextEpisode.alias}`;
return {
url: nextEpisodeLink,
autoAdvance: true,
showAutoAdvanceButton: true,
autoAdvanceButtonText: "قسمت بعدی",
nextEpisodeDisplayTime: lastStart,
nextEpisodeDisplayDuration: 15,
dialogData: {
title: nextEpisode.getTitle(),
description: `${nextEpisode.getFullTitle()} . ${nextEpisode.getDuration(
true
)}`,
url: nextEpisode.getImage("hs"),
link: nextEpisodeLink,
},
dialogTemplate: ({ title, description, url, link }) => {
const container = document.createElement("a");
container.className = "vjs-next-episode-dialog-content";
container.href = link;
const img = document.createElement("img");
img.className = "vjs-next-episode-dialog-poster";
img.src = url;
container.appendChild(img);
const episodeContent = document.createElement("div");
episodeContent.className = "vjs-next-episode-content";
container.appendChild(episodeContent);
const titleEl = document.createElement("span");
titleEl.className = "vjs-next-episode-dialog-title";
titleEl.innerHTML = title;
episodeContent.appendChild(titleEl);
const descriptionEl = document.createElement("span");
descriptionEl.className = "vjs-next-episode-dialog-description";
descriptionEl.innerHTML = description;
episodeContent.appendChild(descriptionEl);
return container;
},
};
}
return null;
};
getSeassonInfo = () => {
// seasons playlist plugin
const seasons = this.product.getSeasons();
if (seasons) {
const transformEpisodesData = (episodes) => {
return episodes.map((episode) => ({
alias: episode.alias,
title: episode.serialSpecialTitle,
season: episode.serialSeason,
part: episode.serialPart,
thumbnail: resolveImage(
episode.specialImagePart || episode.horizontalSmallPoster
),
duration: episode.duration,
link: `${episode.alias}`,
}));
};
return {
seasons: seasons,
getEpisode: (season, page, callback) => {
ProductAPI.episodes({
alias: this.alias,
season: season,
page: page - 1,
})
.then((response) => {
if (response && response.body) {
const { result } = response.body;
const tranformedEpisodes = transformEpisodesData(result || []); //transformEpisodesData(product.getNextEpisode());
callback(tranformedEpisodes);
}
return [];
})
.catch((error) => {
console.info(
`SEASON : Fetch episode error, ${error.message ?? error}`
);
});
},
};
}
return null;
};
getVoteInfo = () => {
// next vote plugin
if (this.isAuthenticated) {
if (this.product.titration) {
const { lastStart } = this.product.titration;
const VOTE_CARD_DURATION = 60; // sec
return {
submitVote: ({ likeStatus, callback }) => {
if (likeStatus === 1) {
ProductAPI.like(this.product.nid)
.then(
() => {
callback();
},
() => {
callback();
}
)
.catch((error) => {
(error.message || error) &&
console.info(
`VOTE : Vote plugin error, ${error.message ?? error}`
);
});
} else if (likeStatus === 2) {
ProductAPI.disLike(this.product.nid)
.then(() => {
callback();
})
.catch((error) => {
(error.message || error) &&
console.info(
`VOTE : Vote plugin error, ${error.message ?? error}`
);
});
}
},
from: lastStart - VOTE_CARD_DURATION,
to: lastStart,
};
}
}
return null;
};
getResumeInfo = async () => {
if (this.product.getDuration() > playerConsts.THRESHOLD.SHORT_MOVIE) {
try {
return await ProductAPI.resume(this.product.nid).then((response) => {
const { body } = response;
if (body) {
return body?.minute;
}
return null;
});
} catch {
console.info("PLAYER : It hasnt't any resume data.");
}
}
return null;
};
preparePlayerConfig = async (streamDataReq) => {
const streamData = streamDataReq?.body;
const { ads } = streamDataReq;
let config = {},
pluginConfig = {};
config.sources = appendUuidOnUrls(
streamData.source,
cookiesName.UuidUnderLine,
this.uuid
);
console.log("streamData", streamData.source);
config.poster = this.product.getImage("mb");
if (this.product.isSeries()) {
config.info = {
title: this.product.getTitle(),
subtitle: this.product.getFullTitle(),
};
} else {
config.info = {
subtitle: this.product.getFullTitle(),
};
}
config.returnUrl = this.returnUrl ? this.returnUrl : consts.SITE_URL;
// seekbuttons plugin reqired
pluginConfig.seekButtons = {};
// subtitle settings
if (streamData.subtitle) {
pluginConfig.subtitleSettings = { subtitles: streamData.subtitle };
}
// skip button plugin
if (this.product.titration) {
const { firstStart, firstEnd } = this.product.titration;
pluginConfig.skipButton = {
from: firstStart === 0 ? 1 : firstStart,
to: firstEnd,
};
}
// seek thubmnail plugin
if (streamData.tooltip_url) {
pluginConfig.seekThumbnailSrc = streamData.tooltip_url;
}
// collect data plugin
// Todo : configure collect data plugin;
if (this.product.getToken()) {
pluginConfig.collectData = getCollectDataConfig(
this.product.getToken(),
this.userToken,
this.uuid
);
}
if (this.uuid) {
config.extraData = {};
config.extraData[cookiesName.UuidUnderLine] = this.uuid;
}
if (this.product.isAdultContent() && this.product.haveAds && ads) {
pluginConfig.vast = {
url: ads.tag_url,
skip: 5,
withCredentials: false,
};
}
// Plugins voting and continue watching are only enabled for login users.
pluginConfig.trafficUsage = await this.getUserInternetInfo();
pluginConfig.markers = await this.getNeshanehInfo();
pluginConfig.nextEpisode = await this.getNextEpisodeInfo();
pluginConfig.seasonPlaylist = await this.getSeassonInfo();
pluginConfig.vote = await this.getVoteInfo();
config.resumeFrom = await this.getResumeInfo();
config.pluginOptions = pluginConfig;
return { loading: false, config };
};
}
export default DesktopPlayerController;