From 3b5e144c88538ad181e9913c8dc60c67b3bf3c0a Mon Sep 17 00:00:00 2001 From: ikprk <168457495+ikprk@users.noreply.github.com> Date: Thu, 23 May 2024 12:35:35 +0200 Subject: [PATCH 1/4] Add earnings volume endpoint (#331) * Add earnings volume endpoint * CR fix v1 * Correct field name --- .../resolvers/StateResolver/index.ts | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/server-extension/resolvers/StateResolver/index.ts b/src/server-extension/resolvers/StateResolver/index.ts index 6aaa3b9cd..4d3c1f303 100644 --- a/src/server-extension/resolvers/StateResolver/index.ts +++ b/src/server-extension/resolvers/StateResolver/index.ts @@ -1,4 +1,4 @@ -import { Resolver, Root, Subscription } from 'type-graphql' +import { Resolver, Root, Subscription, Query, ObjectType, Field } from 'type-graphql' import type { EntityManager } from 'typeorm' import { ProcessorState } from './types' import _, { isObject } from 'lodash' @@ -73,4 +73,63 @@ export class StateResolver { processorState(@Root() state: ProcessorState): ProcessorState { return state } + + @Query(() => EarningStatsOutput) + async totalJoystreamEarnings(): Promise { + const em = await this.tx() + const result = ( + await em.query< + { + total_rewards_volume: string + total_crt_volume: string + total_nft_volume: string + }[] + >( + ` + SELECT + SUM( + COALESCE(event.data->>'amount', '0')::bigint + ) AS "total_rewards_volume", + SUM( + COALESCE(amm_buy.price_paid, '0')::bigint + ) AS "total_crt_volume", + SUM( + COALESCE(event.data->>'price', '0')::bigint + COALESCE(winning_bid.amount, 0) + ) AS "total_nft_volume" + FROM + "event" + LEFT JOIN amm_transaction AS amm_buy ON "data"->>'ammMintTransaction' = amm_buy.id + LEFT JOIN bid AS winning_bid ON "data"->>'winningBid' = winning_bid.id + WHERE + "event"."data"->>'isTypeOf' IN ( + 'ChannelPaymentMadeEventData', + 'CreatorTokenMarketMintEventData', + 'NftBoughtEventData', + 'EnglishAuctionSettledEventData', + 'BidMadeCompletingAuctionEventData', + 'OpenAuctionBidAcceptedEventData' + ) + + ` + ) + )[0] + + return { + crtSaleVolume: result.total_crt_volume ?? 0, + nftSaleVolume: result.total_nft_volume ?? 0, + totalRewardsPaid: result.total_rewards_volume ?? 0, + } + } +} + +@ObjectType() +export class EarningStatsOutput { + @Field({ nullable: false }) + crtSaleVolume: string + + @Field({ nullable: false }) + totalRewardsPaid: string + + @Field({ nullable: false }) + nftSaleVolume: string } From 0a52bebefb791de9101b817fc570756b01b5652c Mon Sep 17 00:00:00 2001 From: Zeeshan Akram <37098720+zeeshanakram3@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:06:26 +0500 Subject: [PATCH 2/4] update nodejs version to 18.x in github actions (#335) --- .github/workflows/checks.yml | 8 ++++---- .github/workflows/tests.yml | 10 +++++----- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5e4474989..8fe254df6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.x] + node-version: [18.x] fail-fast: true steps: - uses: actions/checkout@v2 @@ -26,7 +26,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.x] + node-version: [18.x] fail-fast: true steps: - uses: actions/checkout@v2 @@ -42,10 +42,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 16.x + - name: Use Node.js 18.x uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x - name: Prepare workspace run: make prepare - name: create joystream_default network diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 800cd78e6..f9fbb2250 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 16.x + - name: Use Node.js 18.x uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x - name: Create joystream_default network run: docker network create joystream_default - name: Prepare workspace @@ -31,7 +31,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.x] + node-version: [18.x] fail-fast: true steps: - uses: actions/checkout@v2 @@ -50,7 +50,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.x] + node-version: [18.x] fail-fast: true steps: - uses: actions/checkout@v2 @@ -71,7 +71,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.x] + node-version: [18.x] fail-fast: true steps: - uses: actions/checkout@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8b44ca9..598ddafa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 4.0.3 + +## Misc + +- Update Email notification subjects and tests [#333](https://github.com/Joystream/orion/pull/333) +- Update Node.js version to 18.x in all github actions. + # 4.0.2 ## Bug Fixes: diff --git a/package-lock.json b/package-lock.json index aea4e4852..d4b2b482c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orion", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orion", - "version": "4.0.2", + "version": "4.0.3", "hasInstallScript": true, "workspaces": [ "network-tests" diff --git a/package.json b/package.json index 0c4d76f52..61462ea6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orion", - "version": "4.0.2", + "version": "4.0.3", "engines": { "node": ">=16" }, From d65135f80fa566a9de2bc912d6cbd1d1fa4f7524 Mon Sep 17 00:00:00 2001 From: ikprk <168457495+ikprk@users.noreply.github.com> Date: Fri, 31 May 2024 23:13:19 +0200 Subject: [PATCH 3/4] Feat/notifications update (#333) * Add joy price data * Change email notification subject copies * Decrease interval * CR fixes * remove scheduler from processor --------- Co-authored-by: Zeeshan Akram <97m.zeeshan@gmail.com> --- src/mail-scheduler/index.ts | 2 + src/utils/joystreamPrice.ts | 53 +++++++ src/utils/notification/notificationsData.ts | 167 ++++++++++++-------- 3 files changed, 152 insertions(+), 70 deletions(-) create mode 100644 src/utils/joystreamPrice.ts diff --git a/src/mail-scheduler/index.ts b/src/mail-scheduler/index.ts index 36fe3e50f..c96cc6dbd 100644 --- a/src/mail-scheduler/index.ts +++ b/src/mail-scheduler/index.ts @@ -5,6 +5,7 @@ import { ConfigVariable, config } from '../utils/config' import { uniqueId } from '../utils/crypto' import { globalEm } from '../utils/globalEm' import { createMailContent, executeMailDelivery } from './utils' +import { updateJoystreamPrice } from '../utils/joystreamPrice' export async function getMaxAttempts(em: EntityManager): Promise { const maxAttempts = await config.get(ConfigVariable.EmailNotificationDeliveryMaxAttempts, em) @@ -31,6 +32,7 @@ export async function mailsToDeliver(em: EntityManager): Promise { + // we don't care if the request fails, app should continue to work w/o joy price + const data = await fetch('https://status.joystream.org/price').catch(() => + log.error('Fetching JOYSTREAM price failed') + ) + if (data) { + const json = await data.json() + JOYSTREAM_USD_PRICE = +json.price ?? 0 + } +} + +export const schedulePriceUpdate = async (): Promise => { + while (true) { + await updateJoystreamPrice() + log.info(`Price refetched: ${JOYSTREAM_USD_PRICE}`) + await new Promise((resolve) => setTimeout(resolve, 1_000 * 60 * 5)) // 5mins + } +} + +export const convertHapiToUSD = (hapis: BN | bigint, round = true) => { + if (!JOYSTREAM_USD_PRICE) return null + const tokens = hapiBnToTokenNumber(new BN(hapis.toString())) + const amount = tokens * JOYSTREAM_USD_PRICE + return round ? Math.round(amount) : amount +} + +const hapiBnToTokenNumber = (bn: BN, roundUpToTwoDecimalPlaces?: boolean) => { + const wholeUnitsBn = bn.div(HAPI_TO_JOY_RATE_BN) + const fractionalUnitsBn = bn.mod(HAPI_TO_JOY_RATE_BN) + + if (wholeUnitsBn.gt(MAX_SAFE_NUMBER_BN)) { + throw new Error('Trying to convert unsafe number from BN to number') + } + + const wholeUnits = wholeUnitsBn.toNumber() + const fractionalHapiUnits = fractionalUnitsBn.toNumber() + const fractionalJoyUnits = fractionalHapiUnits / HAPI_TO_JOY_RATE + if (roundUpToTwoDecimalPlaces) { + return Math.ceil((wholeUnits + fractionalJoyUnits) * 100) / 100 + } + return wholeUnits + fractionalJoyUnits +} diff --git a/src/utils/notification/notificationsData.ts b/src/utils/notification/notificationsData.ts index 9d7aa7c12..bd209be7a 100644 --- a/src/utils/notification/notificationsData.ts +++ b/src/utils/notification/notificationsData.ts @@ -4,6 +4,7 @@ import { getNotificationAvatar } from './notificationAvatars' import { getNotificationIcon } from './notificationIcons' import { getNotificationLink } from './notificationLinks' import { formatJOY } from './helpers' +import { convertHapiToUSD } from '../joystreamPrice' export type NotificationData = { icon: string @@ -32,8 +33,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'bell'), link: await getNotificationLink(em, 'channel-page', [channelId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `New channel created: “${channelTitle}“`, - subject: 'New channel', + text: `📢 New channel created: “${channelTitle}“`, + subject: `📢 New channel created: “${channelTitle}“`, } } @@ -44,8 +45,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'follow'), link: await getNotificationLink(em, 'video-page', [videoId, commentId]), avatar: await getNotificationAvatar(em, 'membershipId', memberId), - text: `${memberHandle} replied to your comment under the video: “${videoTitle}”`, - subject: `New comment`, + text: `💬 ${memberHandle} replied to your comment under the video: “${videoTitle}”`, + subject: `💬 ${memberHandle} replied to your comment under the video: “${videoTitle}”`, } } case 'ReactionToComment': { @@ -54,8 +55,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'reaction'), link: await getNotificationLink(em, 'video-page', [videoId, commentId]), avatar: await getNotificationAvatar(em, 'membershipId', memberId), - text: `${memberHandle} reacted to your comment on the video: “${videoTitle}”`, - subject: `New reaction`, + text: `💬 ${memberHandle} reacted to your comment on the video: “${videoTitle}”`, + subject: `💬 ${memberHandle} reacted to your comment on the video: “${videoTitle}”`, } } @@ -66,8 +67,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'video'), link: await getNotificationLink(em, 'video-page', [videoId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} posted a new video: “${videoTitle}”`, - subject: `New video`, + text: `🎥 ${channelTitle} posted a new video: “${videoTitle}”`, + subject: `🎥 ${channelTitle} posted a new video: “${videoTitle}”`, } } case 'NewNftOnSale': { @@ -76,8 +77,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} started the sale of NFT: “${videoTitle}”`, - subject: `New NFT sale`, + text: `🛒 ${channelTitle} started a sale of NFT: “${videoTitle}”`, + subject: `🛒 ${channelTitle} started a sale of NFT: “${videoTitle}”`, } } case 'NewAuction': { @@ -86,8 +87,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} started an auction for NFT: “${videoTitle}”`, - subject: 'New NFT auction', + text: `🎉 ${channelTitle} started an auction for NFT: “${videoTitle}”`, + subject: `🎉 ${channelTitle} started an auction for NFT: “${videoTitle}”`, } } @@ -98,8 +99,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft-alt'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', newBidderId), - text: `${newBidderHandle} placed a higher bid in the timed auction for NFT: “${videoTitle}”`, - subject: 'You got outbid', + text: `💸 ${newBidderHandle} placed a higher bid in the timed auction for NFT: “${videoTitle}”`, + subject: `💸 ${newBidderHandle} placed a higher bid in the timed auction for NFT: “${videoTitle}”`, } } case 'AuctionWon': { @@ -109,8 +110,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', recipientId), - text: `You won ${auctionText} auction for NFT: “${videoTitle}”`, - subject: 'Action won', + text: `🟢 You won ${auctionText} auction for NFT: “${videoTitle}”`, + subject: `🟢 You won ${auctionText} auction for NFT: “${videoTitle}”`, } } case 'AuctionLost': { @@ -120,8 +121,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft-alt'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', recipientId), - text: `You lost ${auctionText} auction for NFT: “${videoTitle}”. Withdraw your bid`, - subject: 'Auction lost', + text: `🔴 You lost ${auctionText} auction for NFT: “${videoTitle}”. Withdraw your bid`, + subject: `🔴 You lost ${auctionText} auction for NFT: “${videoTitle}”`, } } @@ -136,8 +137,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'warning'), link: await getNotificationLink(em, 'term-of-sevice-page'), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `Your channel “${channelTitle}” is excluded from App`, - subject: 'Channel excluded', + text: `🚫 Your channel “${channelTitle}” is excluded from App`, + subject: `🚫 Your channel “${channelTitle}” is excluded from App`, } } case 'VideoExcluded': { @@ -146,8 +147,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'warning'), link: await getNotificationLink(em, 'term-of-sevice-page'), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `Your video is excluded from App: “${videoTitle}”`, - subject: 'Video excluded', + text: `🚫 Your video is excluded from App: “${videoTitle}”`, + subject: `🚫 Your video is excluded from App: “${videoTitle}”`, } } case 'NftFeaturedOnMarketPlace': { @@ -156,8 +157,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'bell'), link: await getNotificationLink(em, 'marketplace-page'), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `Your NFT was featured in the marketplace featured section: “${videoTitle}”`, - subject: 'NFT featured', + text: `🔥 Your NFT was featured in the marketplace featured section: “${videoTitle}”`, + subject: `🔥 Your NFT was featured in the marketplace featured section: “${videoTitle}”`, } } @@ -168,8 +169,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'follow'), link: await getNotificationLink(em, 'member-page', [followerId]), avatar: await getNotificationAvatar(em, 'membershipId', followerId), - text: `${followerHandle} followed your channel`, - subject: 'New follower', + text: `👤 ${followerHandle} followed your channel`, + subject: `👤 ${followerHandle} followed your channel`, } } case 'CommentPostedToVideo': { @@ -178,8 +179,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'follow'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', memberId), - text: `${memberHandle} left a comment on your video: “${videoTitle}”`, - subject: 'New comment', + text: `💬 ${memberHandle} left a comment on your video: “${videoTitle}”`, + subject: `💬 ${memberHandle} left a comment on your video: “${videoTitle}”`, } } case 'VideoLiked': { @@ -188,8 +189,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'like'), link: await getNotificationLink(em, 'video-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', memberId), - text: `${memberHandle} liked your video: “${videoTitle}”`, - subject: 'New video like', + text: `👍 ${memberHandle} liked your video: “${videoTitle}”`, + subject: `👍 ${memberHandle} liked your video: “${videoTitle}”`, } } case 'VideoDisliked': { @@ -198,8 +199,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'dislike'), link: await getNotificationLink(em, 'video-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', memberId), - text: `${memberHandle} disliked your video: “${videoTitle}”`, - subject: 'New video dislike', + text: `👎 ${memberHandle} disliked your video: “${videoTitle}”`, + subject: `👎 ${memberHandle} disliked your video: “${videoTitle}”`, } } @@ -209,8 +210,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'bell'), link: await getNotificationLink(em, 'ypp-dashboard'), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `Your channel got verified in our Youtube Partnership Program`, - subject: 'Channel verified', + text: `🟢 Your channel got verified in our Youtube Partnership Program`, + subject: `🟢 Your channel got verified in our Youtube Partnership Program`, } } case 'ChannelSuspended': { @@ -218,8 +219,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'warning'), link: await getNotificationLink(em, 'ypp-dashboard'), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `Your channel got suspended in our Youtube Partnership Program`, - subject: 'Channel suspended', + text: `🚫 Your channel got suspended in our Youtube Partnership Program`, + subject: `🚫 Your channel got suspended in our Youtube Partnership Program`, } } @@ -230,8 +231,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', buyerId), - text: `${buyerHandle} purchased for ${formatJOY(price)} your NFT: “${videoTitle}”`, - subject: 'New NFT purchase', + text: `🛒 ${buyerHandle} purchased for (${convertHapiToUSD(price) ?? '-'}$) ${formatJOY( + price + )} your NFT: “${videoTitle}”`, + subject: `🛒 ${buyerHandle} purchased for (${convertHapiToUSD(price) ?? '-'}$) ${formatJOY( + price + )} your NFT: “${videoTitle}”`, } } case 'NftRoyaltyPaid': { @@ -240,8 +245,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'channelId', recipientId), - text: `You received ${formatJOY(amount)} royalties from your NFT: “${videoTitle}”`, - subject: 'New NFT royalty', + text: `💰 You received (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} royalties from your NFT: “${videoTitle}”`, + subject: `💰 You received (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} royalties from your NFT: “${videoTitle}”`, } } case 'CreatorReceivesAuctionBid': { @@ -250,8 +259,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'nft'), link: await getNotificationLink(em, 'nft-page', [videoId]), avatar: await getNotificationAvatar(em, 'membershipId', bidderId), - text: `${bidderHandle} placed a bid of ${formatJOY(amount)} for your NFT: “${videoTitle}”`, - subject: 'New NFT bid', + text: `💵 ${bidderHandle} placed a bid of (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} for your NFT: “${videoTitle}”`, + subject: `💵 ${bidderHandle} placed a bid of (${ + convertHapiToUSD(amount) ?? '-' + }$) ${formatJOY(amount)} for your NFT: “${videoTitle}”`, } } @@ -262,8 +275,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'member-page', [payerId]), avatar: await getNotificationAvatar(em, 'membershipId', payerId), - text: `${payerHandle} transferred ${formatJOY(amount)} to your channel`, - subject: 'New payment', + text: `💸 ${payerHandle} transferred (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} to your channel`, + subject: `💸 ${payerHandle} transferred (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} to your channel`, } } case 'ChannelFundsWithdrawn': { @@ -272,8 +289,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'payments-page'), avatar: await getNotificationAvatar(em, 'membershipId', recipientId), - text: `${formatJOY(amount)} were withdrawn from your channel account`, - subject: 'Funds withdrawn', + text: `💸 (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} were withdrawn from your channel account to your membership account`, + subject: `💸 (${convertHapiToUSD(amount) ?? '-'}$) ${formatJOY( + amount + )} were withdrawn from your channel account to your membership account`, } } // CRT @@ -283,8 +304,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'channel-page', [channelId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} issued a creator token for their channel called $${tokenSymbol}.`, - subject: 'New CRT issued', + text: `🚀 ${channelTitle} issued a creator token for their channel called $${tokenSymbol}.`, + subject: `🚀 ${channelTitle} issued a creator token for their channel called $${tokenSymbol}.`, } } case 'CreatorTokenMarketStarted': { @@ -293,8 +314,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'channel-page', [channelId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} started a market for $${tokenSymbol} token.`, - subject: 'New CRT market', + text: `💰 ${channelTitle} started a market for $${tokenSymbol} token.`, + subject: `💰 ${channelTitle} started a market for $${tokenSymbol} token.`, } } case 'CreatorTokenSaleStarted': { @@ -303,8 +324,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'channel-page', [channelId]), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} started a sale for $${tokenSymbol} token.`, - subject: 'New CRT sale', + text: `🛒 ${channelTitle} started a sale for $${tokenSymbol} token.`, + subject: `🛒 ${channelTitle} started a sale for $${tokenSymbol} token.`, } } case 'CreatorTokenMarketMint': { @@ -314,10 +335,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'crt-page'), avatar: await getNotificationAvatar(em, 'membershipId', minterId), - text: `${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token market for ${formatJOY( - paiedJoyAmount - )}`, - subject: 'New market buy transaction', + text: `💰 ${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token market for (${ + convertHapiToUSD(paiedJoyAmount) ?? '-' + }$) ${formatJOY(paiedJoyAmount)}`, + subject: `💰 ${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token market for (${ + convertHapiToUSD(paiedJoyAmount) ?? '-' + }$) ${formatJOY(paiedJoyAmount)}`, } } case 'CreatorTokenMarketBurn': { @@ -327,10 +350,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'crt-page'), avatar: await getNotificationAvatar(em, 'membershipId', burnerId), - text: `${burnerHandle} sold ${burnedTokenAmount} $${tokenSymbol} on token market for ${formatJOY( - receivedJoyAmount - )}`, - subject: 'New market sell transaction', + text: `💰 ${burnerHandle} sold ${burnedTokenAmount} $${tokenSymbol} on token market for (${ + convertHapiToUSD(receivedJoyAmount) ?? '-' + }$) ${formatJOY(receivedJoyAmount)}`, + subject: `💰 ${burnerHandle} sold ${burnedTokenAmount} $${tokenSymbol} on token market for (${ + convertHapiToUSD(receivedJoyAmount) ?? '-' + }$) ${formatJOY(receivedJoyAmount)}`, } } @@ -341,10 +366,12 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'crt-page'), avatar: await getNotificationAvatar(em, 'membershipId', minterId), - text: `${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token sale for ${formatJOY( - paiedJoyAmount - )}`, - subject: 'New token sale buy transaction', + text: `🟢 ${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token sale for (${ + convertHapiToUSD(paiedJoyAmount) ?? '-' + }$) ${formatJOY(paiedJoyAmount)}`, + subject: `🟢 ${minterHandle} minted ${mintedTokenAmount} $${tokenSymbol} on token sale for (${ + convertHapiToUSD(paiedJoyAmount) ?? '-' + }$) ${formatJOY(paiedJoyAmount)}`, } } case 'CreatorTokenRevenueSharePlanned': { @@ -353,8 +380,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'portfolio'), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} planned revenue share for $${tokenSymbol} token starting at block ${plannedAt}`, - subject: 'New revenue share planned', + text: `📅 ${channelTitle} planned revenue share for $${tokenSymbol} token starting at block ${plannedAt}`, + subject: `📅 ${channelTitle} planned revenue share for $${tokenSymbol} token starting at block ${plannedAt}`, } } case 'CreatorTokenRevenueShareStarted': { @@ -363,8 +390,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'portfolio'), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} started revenue share for $${tokenSymbol} token. Go and claim your share now!`, - subject: 'Revenue share started', + text: `⏰ ${channelTitle} started revenue share for $${tokenSymbol} token. Go and claim your share now!`, + subject: `⏰ ${channelTitle} started revenue share for $${tokenSymbol} token. Go and claim your share now!`, } } case 'CreatorTokenRevenueShareEnded': { @@ -373,8 +400,8 @@ export const getNotificationData = async ( icon: await getNotificationIcon(em, 'payout'), link: await getNotificationLink(em, 'portfolio'), avatar: await getNotificationAvatar(em, 'channelId', channelId), - text: `${channelTitle} ended revenue share for $${tokenSymbol} token. Unlock your locked tokens!`, - subject: 'Revenue share ended', + text: `🔓 ${channelTitle} ended revenue share for $${tokenSymbol} token. Unlock your locked tokens!`, + subject: `🔓 ${channelTitle} ended revenue share for $${tokenSymbol} token. Unlock your locked tokens!`, } } } From 6167139dfaa1a6eb7c586043eb991842e6cfe135 Mon Sep 17 00:00:00 2001 From: ikprk <168457495+ikprk@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:32:45 +0200 Subject: [PATCH 4/4] Better orion language detection (#337) * New model to track orion language processing * New function to detect language * New custom migration * Add cursor tracker and video orion language to offchain export * Running `custom-migration` command * Add manger to trigger video language updates * Adjust orion video language manager to support video update * Revert "Running `custom-migration` command" This reverts commit 71c4997aa17fbd6b4952ee4c7b935a1c7ce26e02. * Second run of `create-migrations` command * CR fixes * move 'orion_offchain_cursor' table to admin schema * bump package version and add change log * fix: bug in case detected language is undefined --------- Co-authored-by: Zeeshan Akram <97m.zeeshan@gmail.com> --- .env | 3 + CHANGELOG.md | 5 ++ db/migrations/1720623003671-Data.js | 11 +++ ...962433-Views.js => 1720623003800-Views.js} | 4 +- package-lock.json | 37 ++++----- package.json | 6 +- src/mappings/content/video.ts | 12 +-- src/mappings/utils.ts | 6 ++ src/model/OrionOffchainCursor.ts | 20 +++++ src/model/index.ts | 1 + src/utils/OrionVideoLanguageManager.ts | 64 ++++++++++++++++ .../customMigrations/setOrionLanguage.ts | 56 -------------- .../setOrionLanguageProvider.ts | 75 +++++++++++++++++++ src/utils/language.ts | 33 +++----- src/utils/offchainState.ts | 3 +- 15 files changed, 223 insertions(+), 113 deletions(-) create mode 100644 db/migrations/1720623003671-Data.js rename db/migrations/{1709641962433-Views.js => 1720623003800-Views.js} (91%) create mode 100644 src/model/OrionOffchainCursor.ts create mode 100644 src/utils/OrionVideoLanguageManager.ts delete mode 100644 src/utils/customMigrations/setOrionLanguage.ts create mode 100644 src/utils/customMigrations/setOrionLanguageProvider.ts diff --git a/.env b/.env index 3cc110bab..c916afc28 100644 --- a/.env +++ b/.env @@ -62,6 +62,9 @@ TRUST_PROXY=uniquelocal SENDGRID_API_KEY= SENDGRID_FROM_EMAIL=gateway@example.com +# Detectlanguage +DETECTLANGUAGE_API_KEY= + # Debug settings SQD_DEBUG=api:* diff --git a/CHANGELOG.md b/CHANGELOG.md index 598ddafa1..9ae4ec682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 4.0.4 + +## Bug Fixes: +- Fixed: improve the accuracy of `Video.orionLanguage` field by reworking the `predictVideoLanguage` function in `src/utils/language.ts` + # 4.0.3 ## Misc diff --git a/db/migrations/1720623003671-Data.js b/db/migrations/1720623003671-Data.js new file mode 100644 index 000000000..5a14f9337 --- /dev/null +++ b/db/migrations/1720623003671-Data.js @@ -0,0 +1,11 @@ +module.exports = class Data1720623003671 { + name = 'Data1720623003671' + + async up(db) { + await db.query(`CREATE TABLE "admin"."orion_offchain_cursor" ("cursor_name" character varying NOT NULL, "value" bigint NOT NULL, CONSTRAINT "PK_7083797352af5a21224b6c8ccbc" PRIMARY KEY ("cursor_name"))`) + } + + async down(db) { + await db.query(`DROP TABLE "admin"."orion_offchain_cursor"`) + } +} diff --git a/db/migrations/1709641962433-Views.js b/db/migrations/1720623003800-Views.js similarity index 91% rename from db/migrations/1709641962433-Views.js rename to db/migrations/1720623003800-Views.js index 247ab4dca..b138499f6 100644 --- a/db/migrations/1709641962433-Views.js +++ b/db/migrations/1720623003800-Views.js @@ -1,8 +1,8 @@ const { getViewDefinitions } = require('../viewDefinitions') -module.exports = class Views1709641962433 { - name = 'Views1709641962433' +module.exports = class Views1720623003800 { + name = 'Views1720623003800' async up(db) { const viewDefinitions = getViewDefinitions(db); diff --git a/package-lock.json b/package-lock.json index d4b2b482c..a0650bcdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orion", - "version": "4.0.3", + "version": "4.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orion", - "version": "4.0.3", + "version": "4.0.4", "hasInstallScript": true, "workspaces": [ "network-tests" @@ -40,6 +40,7 @@ "cookie-parser": "^1.4.6", "csv-stringify": "^6.3.0", "dayjs": "^1.11.7", + "detectlanguage": "^2.1.0", "dotenv": "^16.0.3", "dotenv-expand": "^10.0.0", "express-openapi-validator": "^5.0.3", @@ -55,7 +56,6 @@ "patch-package": "^6.5.0", "pg": "8.8.0", "swagger-ui-express": "^4.6.2", - "tinyld": "^1.3.4", "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.11", "ua-parser-js": "^1.0.34", @@ -13889,6 +13889,22 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "node_modules/detectlanguage": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detectlanguage/-/detectlanguage-2.1.0.tgz", + "integrity": "sha512-EbLGyZxiQJeur5a+GNOzBV9xL/r/7GfvRALSHAKepw38UAvCssn7obVvhsioRIV+uDj3IQtXzL7iNkwu0oCp7g==", + "dependencies": { + "axios": "^0.21.1" + } + }, + "node_modules/detectlanguage/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -25796,21 +25812,6 @@ "next-tick": "1" } }, - "node_modules/tinyld": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/tinyld/-/tinyld-1.3.4.tgz", - "integrity": "sha512-u26CNoaInA4XpDU+8s/6Cq8xHc2T5M4fXB3ICfXPokUQoLzmPgSZU02TAkFwFMJCWTjk53gtkS8pETTreZwCqw==", - "bin": { - "tinyld": "bin/tinyld.js", - "tinyld-heavy": "bin/tinyld-heavy.js", - "tinyld-light": "bin/tinyld-light.js" - }, - "engines": { - "node": ">= 12.10.0", - "npm": ">= 6.12.0", - "yarn": ">= 1.20.0" - } - }, "node_modules/title-case": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", diff --git a/package.json b/package.json index 61462ea6c..f2ec73eda 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orion", - "version": "4.0.3", + "version": "4.0.4", "engines": { "node": ">=16" }, @@ -72,6 +72,7 @@ "cookie-parser": "^1.4.6", "csv-stringify": "^6.3.0", "dayjs": "^1.11.7", + "detectlanguage": "^2.1.0", "dotenv": "^16.0.3", "dotenv-expand": "^10.0.0", "express-openapi-validator": "^5.0.3", @@ -87,7 +88,6 @@ "patch-package": "^6.5.0", "pg": "8.8.0", "swagger-ui-express": "^4.6.2", - "tinyld": "^1.3.4", "type-graphql": "^1.2.0-rc.1", "typeorm": "^0.3.11", "ua-parser-js": "^1.0.34", @@ -105,8 +105,8 @@ "@subsquid/substrate-typegen": "^2.1.0", "@subsquid/typeorm-codegen": "0.3.1", "@types/async-lock": "^1.1.3", - "@types/chai": "^4.3.11", "@types/big-json": "^3.2.4", + "@types/chai": "^4.3.11", "@types/cookie-parser": "^1.4.3", "@types/express-rate-limit": "^6.0.0", "@types/mocha": "^10.0.1", diff --git a/src/mappings/content/video.ts b/src/mappings/content/video.ts index 8589f202e..7249b2d42 100644 --- a/src/mappings/content/video.ts +++ b/src/mappings/content/video.ts @@ -17,10 +17,10 @@ import { VideoViewEvent, } from '../../model' import { EventHandlerContext } from '../../utils/events' -import { predictVideoLanguage } from '../../utils/language' import { deserializeMetadata, genericEventFields, + orionVideoLanguageManager, u8aToBytes, videoRelevanceManager, } from '../utils' @@ -122,10 +122,7 @@ export async function processVideoCreatedEvent({ } } - video.orionLanguage = predictVideoLanguage({ - title: video.title ?? '', - description: video.description ?? '', - }) + video.orionLanguage = null channel.totalVideosCreated += 1 @@ -192,10 +189,7 @@ export async function processVideoUpdatedEvent({ ) } - video.orionLanguage = predictVideoLanguage({ - title: video.title ?? '', - description: video.description ?? '', - }) + orionVideoLanguageManager.scheduleVideoForDetection(video.id) if (autoIssueNft) { await processNft(overlay, block, indexInBlock, extrinsicHash, video, contentActor, autoIssueNft) diff --git a/src/mappings/utils.ts b/src/mappings/utils.ts index 49d1f8877..4f2da75f3 100644 --- a/src/mappings/utils.ts +++ b/src/mappings/utils.ts @@ -10,10 +10,16 @@ import { Event, MetaprotocolTransactionResultFailed, NftActivity, NftHistoryEntr import { CommentCountersManager } from '../utils/CommentsCountersManager' import { VideoRelevanceManager } from '../utils/VideoRelevanceManager' import { EntityManagerOverlay } from '../utils/overlay' +import { OrionVideoLanguageManager } from '../utils/OrionVideoLanguageManager' +export const orionVideoLanguageManager = new OrionVideoLanguageManager() export const commentCountersManager = new CommentCountersManager() export const videoRelevanceManager = new VideoRelevanceManager() // eslint-disable-next-line no-void +void orionVideoLanguageManager.init( + 1000 * 60 * 5 // 5 mins +) +// eslint-disable-next-line no-void void videoRelevanceManager.init({ fullUpdateLoopTime: 1000 * 60 * 60 * 12, // 12 hrs scheduledUpdateLoopTime: 1000 * 60 * 10, // 10 mins diff --git a/src/model/OrionOffchainCursor.ts b/src/model/OrionOffchainCursor.ts new file mode 100644 index 000000000..92854164b --- /dev/null +++ b/src/model/OrionOffchainCursor.ts @@ -0,0 +1,20 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm' + +@Entity({ schema: 'admin' }) +export class OrionOffchainCursor { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * Name of the offchain cursor + */ + @PrimaryColumn() + cursorName!: string + + /** + * Value of the cursor + */ + @Column('int8', { nullable: false }) + value!: number +} diff --git a/src/model/index.ts b/src/model/index.ts index 7ebc7a5a7..21c96308b 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -1,2 +1,3 @@ export * from './generated' export { NextEntityId } from './NextEntityId' +export { OrionOffchainCursor } from './OrionOffchainCursor' diff --git a/src/utils/OrionVideoLanguageManager.ts b/src/utils/OrionVideoLanguageManager.ts new file mode 100644 index 000000000..5e059fb27 --- /dev/null +++ b/src/utils/OrionVideoLanguageManager.ts @@ -0,0 +1,64 @@ +import { EntityManager } from 'typeorm' +import { + detectVideoLanguageWithProvider, + updateVideoLanguages, + VIDEO_ORION_LANGUAGE_CURSOR_NAME, +} from './customMigrations/setOrionLanguageProvider' +import { globalEm } from './globalEm' + +export class OrionVideoLanguageManager { + private videoToDetect: Set = new Set() + + async init(intervalMs: number): Promise { + if (!VIDEO_ORION_LANGUAGE_CURSOR_NAME) { + return + } + + this.updateLoop(intervalMs) + .then(() => { + /* Do nothing */ + }) + .catch((err) => { + console.error(err) + process.exit(-1) + }) + } + + scheduleVideoForDetection(id: string | null | undefined) { + if (id) { + this.videoToDetect.add(id) + } + } + + async updateScheduledVideoLanguage(em: EntityManager) { + if (!this.videoToDetect.size) { + return + } + + const videos = await em.query(` + SELECT id, title, description + FROM admin.video + WHERE id in (${[...this.videoToDetect.values()].map((id) => `'${id}'`).join(',')}) + `) + + await updateVideoLanguages(em, videos) + this.videoToDetect.clear() + } + + async updateOrionVideoLanguage() { + return detectVideoLanguageWithProvider() + } + + private async updateLoop(intervalMs: number): Promise { + const em = await globalEm + while (true) { + await this.updateScheduledVideoLanguage(em).catch((e) => { + console.log(`Updating scheduled videos Orion language with provider failed`, e) + }) + await this.updateOrionVideoLanguage().catch((e) => { + console.log(`Updating Orion language with provider failed`, e) + }) + await new Promise((resolve) => setTimeout(resolve, intervalMs)) + } + } +} diff --git a/src/utils/customMigrations/setOrionLanguage.ts b/src/utils/customMigrations/setOrionLanguage.ts deleted file mode 100644 index 79299fe73..000000000 --- a/src/utils/customMigrations/setOrionLanguage.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { EntityManager } from 'typeorm' -import { globalEm } from '../globalEm' -import { predictVideoLanguage } from '../language' - -async function detectVideoLanguage() { - const em: EntityManager = await globalEm - const videos: any[] = await em.query(` - SELECT id, title, description - FROM admin.video - `) - - // Temporary storage for batch update data - const updates: any[] = [] - - for (const [i, video] of videos.entries()) { - const orionLanguage = predictVideoLanguage({ - title: video.title, - description: video.description, - }) - - // Instead of updating immediately, push the update data into the array - updates.push({ orionLanguage, id: video.id }) - console.log(i) - } - - // Define batch size - const batchSize = 1000 // Adjust the batch size based on your database and network performance - - for (let i = 0; i < updates.length; i += batchSize) { - const batch = updates.slice(i, i + batchSize) - - // Prepare the query and parameters for batch update - const query = ` - UPDATE admin.video AS v SET - orion_language = c.orion_language - FROM (VALUES ${batch - .map((_, idx) => `($${idx * 2 + 1}, $${idx * 2 + 2})`) - .join(',')}) AS c(orion_language, id) - WHERE c.id = v.id; - ` - - const queryParams = batch.flatMap((update) => [update.orionLanguage, update.id]) - - // Execute batch update - await em.query(query, queryParams) - } - - console.log(`Updated languages for ${videos.length} videos`) -} - -detectVideoLanguage() - .then(() => console.log('Update process completed.')) - .catch(() => { - console.error('process failed') - process.exit(1) - }) diff --git a/src/utils/customMigrations/setOrionLanguageProvider.ts b/src/utils/customMigrations/setOrionLanguageProvider.ts new file mode 100644 index 000000000..e80f2b0e7 --- /dev/null +++ b/src/utils/customMigrations/setOrionLanguageProvider.ts @@ -0,0 +1,75 @@ +import { EntityManager } from 'typeorm' +import { OrionOffchainCursor } from '../../model' +import { globalEm } from '../globalEm' +import { predictLanguageWithProvider } from '../language' + +const batchSize = 5_000 // Adjust the batch size based on your database and network performance + +type VideoUpdateType = { + id: string + title: string + description: string +} + +export const VIDEO_ORION_LANGUAGE_CURSOR_NAME = 'video_orion_language' + +export async function updateVideoLanguages(em: EntityManager, videos: VideoUpdateType[]) { + const mappedVideos = videos.map((video) => `${video.title} ${video.description}`) + + const predictionForVideos = await predictLanguageWithProvider(mappedVideos) + + const videosWithDetections = videos.map((video, index) => ({ + ...video, + detectedLanguage: predictionForVideos[index], + })) + + const query = ` + UPDATE admin.video AS v SET + orion_language = c.orion_language + FROM (VALUES ${videosWithDetections + .map((_, idx) => `($${idx * 2 + 1}, $${idx * 2 + 2})`) + .join(',')}) AS c(orion_language, id) + WHERE c.id = v.id; + ` + + const queryParams = videosWithDetections.flatMap((update) => [update.detectedLanguage, update.id]) + + // Execute batch update + await em.query(query, queryParams) +} + +export async function detectVideoLanguageWithProvider() { + const em: EntityManager = await globalEm + let cursorEntity: { value: number }[] = await em.query( + `SELECT value FROM admin.orion_offchain_cursor WHERE cursor_name='${VIDEO_ORION_LANGUAGE_CURSOR_NAME}'` + ) + while (true) { + const cursor = +(cursorEntity[0]?.value ?? 0) + + const videos: VideoUpdateType[] = await em.query(` + SELECT id, title, description + FROM admin.video + ORDER BY id::INTEGER ASC + OFFSET ${cursor} + LIMIT ${batchSize} + `) + + if (!videos.length) { + console.log('No more videos!') + break + } + + await updateVideoLanguages(em, videos) + const newCursor = new OrionOffchainCursor({ + cursorName: VIDEO_ORION_LANGUAGE_CURSOR_NAME, + value: cursor + Math.min(batchSize, videos.length), + }) + await em.save(newCursor) + cursorEntity = [newCursor] + console.log( + `Updated languages for videos in range ${cursor}-${ + cursor + Math.min(batchSize, videos.length) + }` + ) + } +} diff --git a/src/utils/language.ts b/src/utils/language.ts index a9f8c2b45..d482ac1f8 100644 --- a/src/utils/language.ts +++ b/src/utils/language.ts @@ -1,4 +1,8 @@ -import { detectAll } from 'tinyld' +import DetectLanguage from 'detectlanguage' + +const languageDetectionApiKey = process.env.DETECTLANGUAGE_API_KEY + +const languageDetectionInstace = new DetectLanguage(languageDetectionApiKey ?? '') function cleanString(input: string): string { // First, remove URLs. This pattern targets a broad range of URLs. @@ -10,27 +14,8 @@ function cleanString(input: string): string { return cleanedString } -function predictLanguage(text: string): { lang: string; accuracy: number } | undefined { - const cleanedText = cleanString(text) - - // Get the most accurate language prediction - return detectAll(cleanedText)?.[0] -} - -export function predictVideoLanguage({ title, description }: any): string | undefined { - let detectedLang: string | undefined - - const titleLang = predictLanguage(title ?? '') - - detectedLang = titleLang?.lang - - if ((titleLang?.accuracy || 0) < 0.5) { - const titleAndDescriptionLang = predictLanguage(`${title} ${description}`) - if ((titleAndDescriptionLang?.accuracy || 0) > (titleLang?.accuracy || 0)) { - // then - detectedLang = titleAndDescriptionLang?.lang - } - } - - return detectedLang +export async function predictLanguageWithProvider(texts: string[]) { + const cleanedTexts = texts.map(cleanString) + const result = await languageDetectionInstace.detect(cleanedTexts) + return result.map((row) => row[0]?.language) } diff --git a/src/utils/offchainState.ts b/src/utils/offchainState.ts index 7360ed2c3..353e19322 100644 --- a/src/utils/offchainState.ts +++ b/src/utils/offchainState.ts @@ -60,8 +60,9 @@ const exportedStateMap: ExportedStateMap = { EmailDeliveryAttempt: true, Token: true, NextEntityId: true, + OrionOffchainCursor: true, Channel: ['isExcluded', 'videoViewsNum', 'followsNum', 'yppStatus', 'channelWeight'], - Video: ['isExcluded', 'viewsNum'], + Video: ['isExcluded', 'viewsNum', 'orionLanguage'], Comment: ['isExcluded'], OwnedNft: ['isFeatured'], VideoCategory: ['isSupported'],