Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videostreaming/plugins/dailymotion
1 result
Show changes
Commits on Source (7)
stages: stages:
- deploy - deploy
deploy: deploy-master:
stage: deploy stage: deploy
script: script:
- export PRE_RELEASE=false
- sh deploy.sh - sh deploy.sh
only: only:
- master - master
deploy-dev:
stage: deploy
script:
- export PRE_RELEASE=true
- sh deploy.sh
only:
- dev
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"sourceUrl": "https://plugins.grayjay.app/Dailymotion/DailymotionConfig.json", "sourceUrl": "https://plugins.grayjay.app/Dailymotion/DailymotionConfig.json",
"scriptUrl": "./DailymotionScript.js", "scriptUrl": "./DailymotionScript.js",
"repositoryUrl": "https://futo.org", "repositoryUrl": "https://futo.org",
"version": 23, "version": 26,
"iconUrl": "./DailymotionIcon.png", "iconUrl": "./DailymotionIcon.png",
"id": "9c87e8db-e75d-48f4-afe5-2d203d4b95c5", "id": "9c87e8db-e75d-48f4-afe5-2d203d4b95c5",
"scriptSignature": "", "scriptSignature": "",
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"sourceUrl": "https://plugins.grayjay.app/Dailymotion/DailymotionConfig.json", "sourceUrl": "https://plugins.grayjay.app/Dailymotion/DailymotionConfig.json",
"scriptUrl": "./DailymotionScript.js", "scriptUrl": "./DailymotionScript.js",
"repositoryUrl": "https://futo.org", "repositoryUrl": "https://futo.org",
"version": 23, "version": 26,
"iconUrl": "./DailymotionIcon.png", "iconUrl": "./DailymotionIcon.png",
"id": "9c87e8db-e75d-48f4-afe5-2d203d4b95c5", "id": "9c87e8db-e75d-48f4-afe5-2d203d4b95c5",
"scriptSignature": "", "scriptSignature": "",
......
...@@ -1157,6 +1157,9 @@ function generateUUIDv4() { ...@@ -1157,6 +1157,9 @@ function generateUUIDv4() {
function applyCommonHeaders(headers = {}) { function applyCommonHeaders(headers = {}) {
return { ...DEFAULT_HEADERS, ...headers }; return { ...DEFAULT_HEADERS, ...headers };
} }
function notifyMaintenanceMode() {
bridge.toast('Dailymotion is currently offline for maintenance. Thanks for your patience.');
}
class SearchPagerAll extends VideoPager { class SearchPagerAll extends VideoPager {
cb; cb;
...@@ -1471,11 +1474,7 @@ function oauthClientCredentialsRequest(httpClient, url, clientId, secret, throwO ...@@ -1471,11 +1474,7 @@ function oauthClientCredentialsRequest(httpClient, url, clientId, secret, throwO
return null; return null;
} }
} }
function extractClientCredentials(httpClient) { function extractClientCredentials(detailsRequestHtml) {
const detailsRequestHtml = httpClient.GET(BASE_URL, {}, false);
if (!detailsRequestHtml.isOk) {
throw new ScriptException('Failed to fetch page to extract auth details');
}
const result = []; const result = [];
const match = detailsRequestHtml.body.match(REGEX_INITIAL_DATA_API_AUTH_1); const match = detailsRequestHtml.body.match(REGEX_INITIAL_DATA_API_AUTH_1);
if (match?.length === 2 && match[0] && match[1]) { if (match?.length === 2 && match[0] && match[1]) {
...@@ -1553,7 +1552,8 @@ const state = { ...@@ -1553,7 +1552,8 @@ const state = {
anonymousUserAuthorizationToken: '', anonymousUserAuthorizationToken: '',
anonymousUserAuthorizationTokenExpirationDate: 0, anonymousUserAuthorizationTokenExpirationDate: 0,
commentWebServiceToken: '', commentWebServiceToken: '',
channelsCache: {} channelsCache: {},
maintenanceMode: false
}; };
source.setSettings = function (settings) { source.setSettings = function (settings) {
_settings = settings; _settings = settings;
...@@ -1625,7 +1625,27 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -1625,7 +1625,27 @@ source.enable = function (conf, settings, saveStateStr) {
if (IS_TESTING) { if (IS_TESTING) {
log('Getting a new tokens'); log('Getting a new tokens');
} }
const clientCredentials = extractClientCredentials(http); let detailsRequestHtml;
try {
detailsRequestHtml = http.GET(BASE_URL, applyCommonHeaders(), false);
if (!detailsRequestHtml.isOk) {
if (detailsRequestHtml.code >= 500 && detailsRequestHtml.code < 600) {
state.maintenanceMode = true;
notifyMaintenanceMode();
}
else {
throw new ScriptException('Failed to fetch page to extract auth details');
}
return;
}
}
catch (e) {
state.maintenanceMode = true;
notifyMaintenanceMode();
return;
}
state.maintenanceMode = false;
const clientCredentials = extractClientCredentials(detailsRequestHtml);
const { anonymousUserAuthorizationToken, anonymousUserAuthorizationTokenExpirationDate, isValid, } = getTokenFromClientCredentials(http, clientCredentials); const { anonymousUserAuthorizationToken, anonymousUserAuthorizationTokenExpirationDate, isValid, } = getTokenFromClientCredentials(http, clientCredentials);
if (!isValid) { if (!isValid) {
console.error('Failed to get token'); console.error('Failed to get token');
...@@ -1655,6 +1675,9 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -1655,6 +1675,9 @@ source.enable = function (conf, settings, saveStateStr) {
} }
}; };
source.getHome = function () { source.getHome = function () {
if (state.maintenanceMode) {
return new ContentPager([]);
}
return getHomePager({}, 0); return getHomePager({}, 0);
}; };
source.searchSuggestions = function (query) { source.searchSuggestions = function (query) {
...@@ -1704,6 +1727,9 @@ source.getChannel = function (url) { ...@@ -1704,6 +1727,9 @@ source.getChannel = function (url) {
return state.channelsCache[url]; return state.channelsCache[url];
}; };
source.getChannelContents = function (url, type, order, filters) { source.getChannelContents = function (url, type, order, filters) {
if (state.maintenanceMode) {
return new ContentPager([]);
}
const page = 1; const page = 1;
return getChannelContentsPager(url, page, type, order, filters); return getChannelContentsPager(url, page, type, order, filters);
}; };
...@@ -2055,9 +2081,9 @@ function getChannelContentsPager(url, page, type, order, filters) { ...@@ -2055,9 +2081,9 @@ function getChannelContentsPager(url, page, type, order, filters) {
log(`Getting channel contents for ${url}, page: ${page}, type: ${type}, order: ${order}, shouldLoadVideos: ${shouldLoadVideos}, shouldLoadLives: ${shouldLoadLives}, filters: ${JSON.stringify(filters)}`); log(`Getting channel contents for ${url}, page: ${page}, type: ${type}, order: ${order}, shouldLoadVideos: ${shouldLoadVideos}, shouldLoadLives: ${shouldLoadLives}, filters: ${JSON.stringify(filters)}`);
} }
/** /**
Recent = Sort liked medias by most recent. Recent = Sort liked medias by most recent.
Visited - Sort liked medias by most viewed Visited - Sort liked medias by most viewed
*/ */
let sort; let sort;
if (order == Type.Order.Chronological) { if (order == Type.Order.Chronological) {
sort = LikedMediaSort.Recent; sort = LikedMediaSort.Recent;
......
#!/bin/sh #!/bin/sh
DOCUMENT_ROOT=/var/www/sources DOCUMENT_ROOT=/var/www/sources
# Use environment variable to determine deployment type
PRE_RELEASE=${PRE_RELEASE:-false} # Default to false if not set
# Determine deployment directory
if [ "$PRE_RELEASE" = "true" ]; then
RELATIVE_PATH="pre-release/Dailymotion"
else
RELATIVE_PATH="Dailymotion"
fi
DEPLOY_DIR="$DOCUMENT_ROOT/$RELATIVE_PATH"
PLUGIN_URL_ROOT="https://plugins.grayjay.app/$RELATIVE_PATH"
SOURCE_URL="$PLUGIN_URL_ROOT/DailymotionConfig.json"
# Take site offline # Take site offline
echo "Taking site offline..." echo "Taking site offline..."
touch $DOCUMENT_ROOT/maintenance.file touch $DOCUMENT_ROOT/maintenance.file
# Swap over the content # Swap over the content
echo "Deploying content..." echo "Deploying content..."
mkdir -p $DOCUMENT_ROOT/Dailymotion mkdir -p "$DEPLOY_DIR"
cp build/DailymotionIcon.png $DOCUMENT_ROOT/Dailymotion cp build/DailymotionIcon.png "$DEPLOY_DIR"
cp build/DailymotionConfig.json $DOCUMENT_ROOT/Dailymotion cp build/DailymotionConfig.json "$DEPLOY_DIR"
cp build/DailymotionScript.js $DOCUMENT_ROOT/Dailymotion cp build/DailymotionScript.js "$DEPLOY_DIR"
sh sign.sh $DOCUMENT_ROOT/Dailymotion/DailymotionScript.js $DOCUMENT_ROOT/Dailymotion/DailymotionConfig.json
# Update the sourceUrl in DailymotionConfig.json
echo "Updating sourceUrl in DailymotionConfig.json..."
jq --arg sourceUrl "$SOURCE_URL" '.sourceUrl = $sourceUrl' "$DEPLOY_DIR/DailymotionConfig.json" > "$DEPLOY_DIR/DailymotionConfig_temp.json"
if [ $? -eq 0 ]; then
mv "$DEPLOY_DIR/DailymotionConfig_temp.json" "$DEPLOY_DIR/DailymotionConfig.json"
else
echo "Failed to update DailymotionConfig.json" >&2
exit 1
fi
sh sign.sh "$DEPLOY_DIR/DailymotionScript.js" "$DEPLOY_DIR/DailymotionConfig.json"
# Notify Cloudflare to wipe the CDN cache # Notify Cloudflare to wipe the CDN cache
echo "Purging Cloudflare cache for zone $CLOUDFLARE_ZONE_ID..." echo "Purging Cloudflare cache for zone $CLOUDFLARE_ZONE_ID..."
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \ curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
--data '{"files":["https://plugins.grayjay.app/Dailymotion/DailymotionIcon.png", "https://plugins.grayjay.app/Dailymotion/DailymotionConfig.json", "https://plugins.grayjay.app/Dailymotion/DailymotionScript.js"]}' --data '{"files":["'"$PLUGIN_URL_ROOT/DailymotionIcon.png"'", "'"$PLUGIN_URL_ROOT/DailymotionConfig.json"'", "'"$PLUGIN_URL_ROOT/DailymotionScript.js"'"]}'
# Take site back online # Take site back online
echo "Bringing site back online..." echo "Bringing site back online..."
rm $DOCUMENT_ROOT/maintenance.file rm "$DOCUMENT_ROOT/maintenance.file"
...@@ -5,7 +5,8 @@ const state = { ...@@ -5,7 +5,8 @@ const state = {
anonymousUserAuthorizationToken: '', anonymousUserAuthorizationToken: '',
anonymousUserAuthorizationTokenExpirationDate: 0, anonymousUserAuthorizationTokenExpirationDate: 0,
commentWebServiceToken: '', commentWebServiceToken: '',
channelsCache: {} as Record<string, PlatformChannel> channelsCache: {} as Record<string, PlatformChannel>,
maintenanceMode: false
}; };
import { import {
...@@ -52,7 +53,13 @@ import { ...@@ -52,7 +53,13 @@ import {
playerVideosDataQuery, playerVideosDataQuery,
} from './gqlQueries'; } from './gqlQueries';
import { getChannelNameFromUrl, getQuery, generateUUIDv4, applyCommonHeaders } from './util'; import {
getChannelNameFromUrl,
getQuery,
generateUUIDv4,
applyCommonHeaders,
notifyMaintenanceMode
} from './util';
import { import {
Channel, Channel,
...@@ -155,10 +162,10 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -155,10 +162,10 @@ source.enable = function (conf, settings, saveStateStr) {
if (saveState) { if (saveState) {
Object Object
.keys(state) .keys(state)
.forEach((key) => { .forEach((key) => {
state[key] = saveState[key]; state[key] = saveState[key];
}); });
if (!isTokenValid()) { if (!isTokenValid()) {
log('Token expired. Fetching a new one.'); log('Token expired. Fetching a new one.');
...@@ -178,7 +185,30 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -178,7 +185,30 @@ source.enable = function (conf, settings, saveStateStr) {
log('Getting a new tokens'); log('Getting a new tokens');
} }
const clientCredentials = extractClientCredentials(http); let detailsRequestHtml;
try {
detailsRequestHtml = http.GET(BASE_URL, applyCommonHeaders(), false);
if (!detailsRequestHtml.isOk) {
if (detailsRequestHtml.code >= 500 && detailsRequestHtml.code < 600) {
state.maintenanceMode = true;
notifyMaintenanceMode();
} else {
throw new ScriptException('Failed to fetch page to extract auth details');
}
return;
}
} catch(e) {
state.maintenanceMode = true;
notifyMaintenanceMode();
return;
}
state.maintenanceMode = false;
const clientCredentials = extractClientCredentials(detailsRequestHtml);
const { const {
anonymousUserAuthorizationToken, anonymousUserAuthorizationToken,
...@@ -210,11 +240,11 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -210,11 +240,11 @@ source.enable = function (conf, settings, saveStateStr) {
}), }),
false, false,
); );
if (!authenticateIm.isOk) { if (!authenticateIm.isOk) {
log('Failed to authenticate to comments service'); log('Failed to authenticate to comments service');
} }
state.commentWebServiceToken = authenticateIm?.headers?.['x-access-token']?.[0]; state.commentWebServiceToken = authenticateIm?.headers?.['x-access-token']?.[0];
} catch (error) { } catch (error) {
log('Failed to authenticate to comments service:' + error); log('Failed to authenticate to comments service:' + error);
...@@ -224,6 +254,11 @@ source.enable = function (conf, settings, saveStateStr) { ...@@ -224,6 +254,11 @@ source.enable = function (conf, settings, saveStateStr) {
}; };
source.getHome = function () { source.getHome = function () {
if (state.maintenanceMode) {
return new ContentPager([]);
}
return getHomePager({}, 0); return getHomePager({}, 0);
}; };
...@@ -293,6 +328,11 @@ source.getChannel = function (url) { ...@@ -293,6 +328,11 @@ source.getChannel = function (url) {
}; };
source.getChannelContents = function (url, type, order, filters) { source.getChannelContents = function (url, type, order, filters) {
if (state.maintenanceMode) {
return new ContentPager([]);
}
const page = 1; const page = 1;
return getChannelContentsPager(url, page, type, order, filters); return getChannelContentsPager(url, page, type, order, filters);
}; };
...@@ -433,8 +473,8 @@ class PlatformCommentPager extends CommentPager { ...@@ -433,8 +473,8 @@ class PlatformCommentPager extends CommentPager {
source.isPlaylistUrl = (url): boolean => { source.isPlaylistUrl = (url): boolean => {
return ( return (
REGEX_VIDEO_PLAYLIST_URL.test(url) || [ REGEX_VIDEO_PLAYLIST_URL.test(url) || [
LIKED_VIDEOS_PLAYLIST_ID, LIKED_VIDEOS_PLAYLIST_ID,
FAVORITE_VIDEOS_PLAYLIST_ID, FAVORITE_VIDEOS_PLAYLIST_ID,
RECENTLY_WATCHED_VIDEOS_PLAYLIST_ID RECENTLY_WATCHED_VIDEOS_PLAYLIST_ID
].includes(url) ].includes(url)
); );
...@@ -582,11 +622,11 @@ source.getUserPlaylists = (): string[] => { ...@@ -582,11 +622,11 @@ source.getUserPlaylists = (): string[] => {
FAVORITE_VIDEOS_PLAYLIST_ID, FAVORITE_VIDEOS_PLAYLIST_ID,
RECENTLY_WATCHED_VIDEOS_PLAYLIST_ID, RECENTLY_WATCHED_VIDEOS_PLAYLIST_ID,
] ]
.forEach((playlistId) => { .forEach((playlistId) => {
if (!playlists.includes(playlistId)) { if (!playlists.includes(playlistId)) {
playlists.push(playlistId); playlists.push(playlistId);
} }
}); });
return playlists; return playlists;
}; };
...@@ -600,7 +640,7 @@ source.getChannelTemplateByClaimMap = () => { ...@@ -600,7 +640,7 @@ source.getChannelTemplateByClaimMap = () => {
}; };
}; };
source.getContentRecommendations = (url, initialData) => { source.getContentRecommendations = (url, initialData) => {
try { try {
const videoXid = url.split('/').pop(); const videoXid = url.split('/').pop();
...@@ -614,9 +654,9 @@ source.getContentRecommendations = (url, initialData) => { ...@@ -614,9 +654,9 @@ source.getContentRecommendations = (url, initialData) => {
query: DISCOVERY_QUEUE_QUERY, query: DISCOVERY_QUEUE_QUERY,
usePlatformAuth: false, usePlatformAuth: false,
}); });
const videoXids: string[] = gqlResponse?.data?.views?.neon?.sections?.edges?.[0]?.node?.components?.edges?.map(e => e.node.xid) ?? []; const videoXids: string[] = gqlResponse?.data?.views?.neon?.sections?.edges?.[0]?.node?.components?.edges?.map(e => e.node.xid) ?? [];
const gqlResponse1 = executeGqlQuery(http, { const gqlResponse1 = executeGqlQuery(http, {
operationName: 'playerVideosDataQuery', operationName: 'playerVideosDataQuery',
variables: { variables: {
...@@ -628,14 +668,14 @@ source.getContentRecommendations = (url, initialData) => { ...@@ -628,14 +668,14 @@ source.getContentRecommendations = (url, initialData) => {
query: playerVideosDataQuery, query: playerVideosDataQuery,
usePlatformAuth: false, usePlatformAuth: false,
}); });
const results = const results =
gqlResponse1.data.videos.edges gqlResponse1.data.videos.edges
?.map((edge) => { ?.map((edge) => {
return SourceVideoToGrayjayVideo(config.id, edge.node as Video); return SourceVideoToGrayjayVideo(config.id, edge.node as Video);
}); });
return new VideoPager(results, false); return new VideoPager(results, false);
} catch(error){ } catch(error){
log('Failed to get recommendations:' + error); log('Failed to get recommendations:' + error);
...@@ -667,9 +707,9 @@ function getPlaylistsByUsername( ...@@ -667,9 +707,9 @@ function getPlaylistsByUsername(
const playlists: string[] = (collections.data.channel as Maybe<Channel>)?.collections?.edges?.map( const playlists: string[] = (collections.data.channel as Maybe<Channel>)?.collections?.edges?.map(
(edge) => { (edge) => {
let playlistUrl = `${BASE_URL_PLAYLIST}/${edge?.node?.xid}`; let playlistUrl = `${BASE_URL_PLAYLIST}/${edge?.node?.xid}`;
const isPrivatePlaylist = edge?.node?.isPrivate ?? false; const isPrivatePlaylist = edge?.node?.isPrivate ?? false;
if(isPrivatePlaylist){ if(isPrivatePlaylist){
...@@ -801,9 +841,9 @@ function getChannelContentsPager(url, page, type, order, filters) { ...@@ -801,9 +841,9 @@ function getChannelContentsPager(url, page, type, order, filters) {
} }
/** /**
Recent = Sort liked medias by most recent. Recent = Sort liked medias by most recent.
Visited - Sort liked medias by most viewed Visited - Sort liked medias by most viewed
*/ */
let sort: string; let sort: string;
if (order == Type.Order.Chronological) { if (order == Type.Order.Chronological) {
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
REGEX_INITIAL_DATA_API_AUTH_1, REGEX_INITIAL_DATA_API_AUTH_1,
USER_AGENT, USER_AGENT,
} from './constants'; } from './constants';
import { objectToUrlEncodedString } from './util'; import { objectToUrlEncodedString, applyCommonHeaders } from './util';
export function oauthClientCredentialsRequest( export function oauthClientCredentialsRequest(
httpClient: IHttp, httpClient: IHttp,
...@@ -56,12 +56,7 @@ export function oauthClientCredentialsRequest( ...@@ -56,12 +56,7 @@ export function oauthClientCredentialsRequest(
} }
} }
export function extractClientCredentials(httpClient: IHttp) { export function extractClientCredentials(detailsRequestHtml) {
const detailsRequestHtml = httpClient.GET(BASE_URL, {}, false);
if (!detailsRequestHtml.isOk) {
throw new ScriptException('Failed to fetch page to extract auth details');
}
const result = []; const result = [];
......
...@@ -119,4 +119,8 @@ export function generateUUIDv4() { ...@@ -119,4 +119,8 @@ export function generateUUIDv4() {
export function applyCommonHeaders(headers: Record<string, string>={}) : Record<string, string>{ export function applyCommonHeaders(headers: Record<string, string>={}) : Record<string, string>{
return { ...DEFAULT_HEADERS, ...headers }; return { ...DEFAULT_HEADERS, ...headers };
}
export function notifyMaintenanceMode() {
bridge.toast('Dailymotion is currently offline for maintenance. Thanks for your patience.');
} }
\ No newline at end of file