--- /dev/null
+<div *ngIf="account" class="row">
+ <div class="description col-md-6 col-sm-12">
+ <div class="small-title">Description</div>
+ <div class="content">{{ getAccountDescription() }}</div>
+ </div>
+
+ <div class="stats col-md-6 col-sm-12">
+ <div class="small-title">Stats</div>
+
+ <div class="content">Joined {{ account.createdAt | date }}</div>
+ </div>
+</div>
\ No newline at end of file
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+.small-title {
+ @include in-content-small-title;
+
+ margin-bottom: 20px;
+}
--- /dev/null
+import { Component, OnDestroy, OnInit } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
+import { Location } from '@angular/common'
+import { getParameterByName, immutableAssign } from '@app/shared/misc/utils'
+import { NotificationsService } from 'angular2-notifications'
+import 'rxjs/add/observable/from'
+import 'rxjs/add/operator/concatAll'
+import { AuthService } from '../../core/auth'
+import { ConfirmService } from '../../core/confirm'
+import { AbstractVideoList } from '../../shared/video/abstract-video-list'
+import { VideoService } from '../../shared/video/video.service'
+import { Account } from '@app/shared/account/account.model'
+import { AccountService } from '@app/shared/account/account.service'
+
+@Component({
+ selector: 'my-account-about',
+ templateUrl: './account-about.component.html',
+ styleUrls: [ './account-about.component.scss' ]
+})
+export class AccountAboutComponent implements OnInit {
+ private account: Account
+
+ constructor (
+ protected route: ActivatedRoute,
+ private accountService: AccountService
+ ) { }
+
+ ngOnInit () {
+ // Parent get the account for us
+ this.accountService.accountLoaded
+ .subscribe(account => this.account = account)
+ }
+
+ getAccountDescription () {
+ if (this.account.description) return this.account.description
+
+ return 'No description'
+ }
+}
--- /dev/null
+import { NgModule } from '@angular/core'
+import { RouterModule, Routes } from '@angular/router'
+import { MetaGuard } from '@ngx-meta/core'
+import { AccountComponent } from './account.component'
+import { AccountVideosComponent } from './account-videos/account-videos.component'
+import { AccountAboutComponent } from '@app/+account/account-about/account-about.component'
+
+const accountRoutes: Routes = [
+ {
+ path: ':accountId',
+ component: AccountComponent,
+ canActivateChild: [ MetaGuard ],
+ children: [
+ {
+ path: '',
+ redirectTo: 'videos',
+ pathMatch: 'full'
+ },
+ {
+ path: 'videos',
+ component: AccountVideosComponent,
+ data: {
+ meta: {
+ title: 'Account videos'
+ }
+ }
+ },
+ {
+ path: 'about',
+ component: AccountAboutComponent,
+ data: {
+ meta: {
+ title: 'About account'
+ }
+ }
+ }
+ ]
+ }
+]
+
+@NgModule({
+ imports: [ RouterModule.forChild(accountRoutes) ],
+ exports: [ RouterModule ]
+})
+export class AccountRoutingModule {}
--- /dev/null
+.title-page-single {
+ margin-top: 0;
+}
\ No newline at end of file
--- /dev/null
+import { Component, OnDestroy, OnInit } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
+import { Location } from '@angular/common'
+import { immutableAssign } from '@app/shared/misc/utils'
+import { NotificationsService } from 'angular2-notifications'
+import 'rxjs/add/observable/from'
+import 'rxjs/add/operator/concatAll'
+import { AuthService } from '../../core/auth'
+import { ConfirmService } from '../../core/confirm'
+import { AbstractVideoList } from '../../shared/video/abstract-video-list'
+import { VideoService } from '../../shared/video/video.service'
+import { Account } from '@app/shared/account/account.model'
+import { AccountService } from '@app/shared/account/account.service'
+
+@Component({
+ selector: 'my-account-videos',
+ templateUrl: '../../shared/video/abstract-video-list.html',
+ styleUrls: [
+ '../../shared/video/abstract-video-list.scss',
+ './account-videos.component.scss'
+ ]
+})
+export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
+ titlePage = 'Published videos'
+ marginContent = false // Disable margin
+ currentRoute = '/account/videos'
+ loadOnInit = false
+
+ private account: Account
+
+ constructor (
+ protected router: Router,
+ protected route: ActivatedRoute,
+ protected authService: AuthService,
+ protected notificationsService: NotificationsService,
+ protected confirmService: ConfirmService,
+ protected location: Location,
+ private accountService: AccountService,
+ private videoService: VideoService
+ ) {
+ super()
+ }
+
+ ngOnInit () {
+ super.ngOnInit()
+
+ // Parent get the account for us
+ this.accountService.accountLoaded
+ .subscribe(account => {
+ this.account = account
+ this.currentRoute = '/account/' + this.account.id + '/videos'
+
+ this.loadMoreVideos(this.pagination.currentPage)
+ this.generateSyndicationList()
+ })
+ }
+
+ ngOnDestroy () {
+ super.ngOnDestroy()
+ }
+
+ getVideosObservable (page: number) {
+ const newPagination = immutableAssign(this.pagination, { currentPage: page })
+
+ return this.videoService.getAccountVideos(this.account, newPagination, this.sort)
+ }
+
+ generateSyndicationList () {
+ this.syndicationItems = this.videoService.getAccountFeedUrls(this.account.id)
+ }
+}
--- /dev/null
+<div *ngIf="account" class="row">
+ <div class="sub-menu">
+
+ <div class="account">
+ <img [src]="getAvatarUrl()" alt="Avatar" />
+
+ <div class="account-info">
+ <div class="account-display-name">{{ account.displayName }}</div>
+ <div class="account-followers">{{ account.followersCount }} subscribers</div>
+ </div>
+ </div>
+
+ <div class="links">
+ <a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
+
+ <a routerLink="about" routerLinkActive="active" class="title-page">About</a>
+ </div>
+ </div>
+
+ <div class="margin-content">
+ <router-outlet></router-outlet>
+ </div>
+</div>
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+.sub-menu {
+ height: 160px;
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+
+ .account {
+ display: flex;
+ margin-top: 20px;
+ margin-bottom: 20px;
+
+ img {
+ @include avatar(80px);
+
+ margin-right: 20px;
+ }
+
+ .account-info {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ .account-display-name {
+ font-size: 23px;
+ font-weight: $font-bold;
+ }
+
+ .account-followers {
+ font-size: 15px;
+ }
+ }
+ }
+
+ .links {
+ margin-top: 0;
+ margin-bottom: 10px;
+
+ a {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+import { Component, OnInit } from '@angular/core'
+import { ActivatedRoute } from '@angular/router'
+import { AccountService } from '@app/shared/account/account.service'
+import { Account } from '@app/shared/account/account.model'
+
+@Component({
+ selector: 'my-account',
+ templateUrl: './account.component.html',
+ styleUrls: [ './account.component.scss' ]
+})
+export class AccountComponent implements OnInit {
+ private account: Account
+
+ constructor (
+ private route: ActivatedRoute,
+ private accountService: AccountService
+ ) {}
+
+ ngOnInit () {
+ const accountId = parseInt(this.route.snapshot.params['accountId'], 10)
+
+ this.accountService.getAccount(accountId)
+ .subscribe(account => this.account = account)
+ }
+
+ getAvatarUrl () {
+ return Account.GET_ACCOUNT_AVATAR_URL(this.account)
+ }
+}
--- /dev/null
+import { NgModule } from '@angular/core'
+import { SharedModule } from '../shared'
+import { AccountRoutingModule } from './account-routing.module'
+import { AccountComponent } from './account.component'
+import { AccountVideosComponent } from './account-videos/account-videos.component'
+import { AccountAboutComponent } from './account-about/account-about.component'
+
+@NgModule({
+ imports: [
+ AccountRoutingModule,
+ SharedModule
+ ],
+
+ declarations: [
+ AccountComponent,
+ AccountVideosComponent,
+ AccountAboutComponent
+ ],
+
+ exports: [
+ AccountComponent
+ ],
+
+ providers: []
+})
+export class AccountModule { }
--- /dev/null
+export * from './account-routing.module'
+export * from './account.component'
+export * from './account.module'
{
path: 'admin',
loadChildren: './+admin/admin.module#AdminModule'
+ },
+ {
+ path: 'account',
+ loadChildren: './+account/account.module#AccountModule'
}
]
}
.account-title {
- text-transform: uppercase;
- color: $orange-color;
- font-weight: $font-bold;
- font-size: 13px;
+ @include in-content-small-title;
+
margin-top: 55px;
margin-bottom: 30px;
}
import { Component } from '@angular/core'
@Component({
- selector: 'my-account',
+ selector: 'my-my-account',
templateUrl: './my-account.component.html'
})
export class MyAccountComponent {}
updatedAt: Date
avatar: Avatar
+ constructor (hash: ServerAccount) {
+ this.id = hash.id
+ this.uuid = hash.uuid
+ this.url = hash.url
+ this.name = hash.name
+ this.displayName = hash.displayName
+ this.description = hash.description
+ this.host = hash.host
+ this.followingCount = hash.followingCount
+ this.followersCount = hash.followersCount
+ this.createdAt = new Date(hash.createdAt.toString())
+ this.updatedAt = new Date(hash.updatedAt.toString())
+ this.avatar = hash.avatar
+ }
+
static GET_ACCOUNT_AVATAR_URL (account: Account) {
const absoluteAPIUrl = getAbsoluteAPIUrl()
--- /dev/null
+import { Injectable } from '@angular/core'
+import 'rxjs/add/operator/catch'
+import 'rxjs/add/operator/map'
+import { environment } from '../../../environments/environment'
+import { Observable } from 'rxjs/Observable'
+import { Account } from '@app/shared/account/account.model'
+import { RestExtractor } from '@app/shared/rest/rest-extractor.service'
+import { RestService } from '@app/shared/rest/rest.service'
+import { HttpClient } from '@angular/common/http'
+import { Account as ServerAccount } from '../../../../../shared/models/actors/account.model'
+import { ReplaySubject } from 'rxjs/ReplaySubject'
+
+@Injectable()
+export class AccountService {
+ static BASE_ACCOUNT_URL = environment.apiUrl + '/api/v1/accounts/'
+
+ accountLoaded = new ReplaySubject<Account>(1)
+
+ constructor (
+ private authHttp: HttpClient,
+ private restExtractor: RestExtractor,
+ private restService: RestService
+ ) {}
+
+ getAccount (id: number): Observable<Account> {
+ return this.authHttp.get<ServerAccount>(AccountService.BASE_ACCOUNT_URL + id)
+ .map(accountHash => new Account(accountHash))
+ .do(account => this.accountLoaded.next(account))
+ .catch((res) => this.restExtractor.handleError(res))
+ }
+}
import { VideoFeedComponent } from './video/video-feed.component'
import { VideoThumbnailComponent } from './video/video-thumbnail.component'
import { VideoService } from './video/video.service'
+import { AccountService } from '@app/shared/account/account.service'
@NgModule({
imports: [
VideoBlacklistService,
UserService,
VideoService,
+ AccountService,
MarkdownService
]
})
-<div class="margin-content">
- <div class="title-page title-page-single">
+<div [ngClass]="{ 'margin-content': marginContent }">
+ <div *ngIf="titlePage" class="title-page title-page-single">
{{ titlePage }}
</div>
<my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
syndicationItems = []
loadOnInit = true
+ marginContent = true
pageHeight: number
videoWidth: number
videoHeight: number
import { VideoEdit } from './video-edit.model'
import { Video } from './video.model'
import { objectToFormData } from '@app/shared/misc/utils'
+import { Account } from '@app/shared/account/account.model'
+import { AccountService } from '@app/shared/account/account.service'
@Injectable()
export class VideoService {
.catch((res) => this.restExtractor.handleError(res))
}
+ getAccountVideos (
+ account: Account,
+ videoPagination: ComponentPagination,
+ sort: VideoSortField
+ ): Observable<{ videos: Video[], totalVideos: number}> {
+ const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
+
+ let params = new HttpParams()
+ params = this.restService.addRestGetParams(params, pagination, sort)
+
+ return this.authHttp
+ .get(AccountService.BASE_ACCOUNT_URL + account.id + '/videos', { params })
+ .map(this.extractVideos)
+ .catch((res) => this.restExtractor.handleError(res))
+ }
+
getVideos (
videoPagination: ComponentPagination,
sort: VideoSortField,
</div>
<div class="video-info-by">
- <a [routerLink]="[ '/videos', 'search' ]" [queryParams]="{ search: video.account.name }" title="Search videos of this account">
+ <a [routerLink]="[ '/account', video.account.id ]" title="Go the account page">
By {{ video.by }}
<img [src]="getAvatarPath()" alt="Account avatar" />
</a>
-
- <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
</div>
</div>
otherVideosDisplayed: Video[] = []
- syndicationItems = {}
-
player: videojs.Player
playerElement: HTMLVideoElement
userRating: UserVideoRateType = null
const startTime = this.route.snapshot.queryParams.start
this.onVideoFetched(video, startTime)
.catch(err => this.handleError(err))
- this.generateSyndicationList()
},
error => {
return this.video.tags.join(', ')
}
- generateSyndicationList () {
- this.syndicationItems = this.videoService.getAccountFeedUrls(this.video.account.id)
- }
-
isVideoRemovable () {
return this.video.isRemovableBy(this.authService.getUser())
}
left: 0.25em;
transform: rotate(-135deg);
}
+
+@mixin in-content-small-title {
+ text-transform: uppercase;
+ color: $orange-color;
+ font-weight: $font-bold;
+ font-size: 13px;
+}
\ No newline at end of file
import * as express from 'express'
import { getFormattedObjects } from '../../helpers/utils'
-import { asyncMiddleware, paginationValidator, setDefaultSort, setDefaultPagination } from '../../middlewares'
-import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators'
+import { asyncMiddleware, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort } from '../../middlewares'
+import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators'
import { AccountModel } from '../../models/account/account'
+import { VideoModel } from '../../models/video/video'
+import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
+import { isNSFWHidden } from '../../helpers/express-utils'
const accountsRouter = express.Router()
getAccount
)
+accountsRouter.get('/:id/videos',
+ asyncMiddleware(accountsGetValidator),
+ paginationValidator,
+ videosSortValidator,
+ setDefaultSort,
+ setDefaultPagination,
+ optionalAuthenticate,
+ asyncMiddleware(getAccountVideos)
+)
+
// ---------------------------------------------------------------------------
export {
// ---------------------------------------------------------------------------
function getAccount (req: express.Request, res: express.Response, next: express.NextFunction) {
- return res.json(res.locals.account.toFormattedJSON())
+ const account: AccountModel = res.locals.account
+
+ return res.json(account.toFormattedJSON())
}
async function listAccounts (req: express.Request, res: express.Response, next: express.NextFunction) {
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
+
+async function getAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
+ const account: AccountModel = res.locals.account
+
+ const resultList = await VideoModel.listForApi(
+ req.query.start as number,
+ req.query.count as number,
+ req.query.sort as VideoSortField,
+ isNSFWHidden(res),
+ null,
+ false,
+ account.id
+ )
+
+ return res.json(getFormattedObjects(resultList.data, resultList.total))
+}
import * as express from 'express'
-import { badRequest } from '../../helpers/utils'
import { configRouter } from './config'
import { jobsRouter } from './jobs'
import { oauthClientsRouter } from './oauth-clients'
import { usersRouter } from './users'
import { accountsRouter } from './accounts'
import { videosRouter } from './videos'
+import { badRequest } from '../../helpers/express-utils'
const apiRouter = express.Router()
import { retryTransactionWrapper } from '../../helpers/database-utils'
import { processImage } from '../../helpers/image-utils'
import { logger } from '../../helpers/logger'
-import { createReqFiles, getFormattedObjects } from '../../helpers/utils'
+import { getFormattedObjects } from '../../helpers/utils'
import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers'
import { updateActorAvatarInstance } from '../../lib/activitypub'
import { sendUpdateActor } from '../../lib/activitypub/send'
import { OAuthTokenModel } from '../../models/oauth/oauth-token'
import { VideoModel } from '../../models/video/video'
import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
+import { createReqFiles } from '../../helpers/express-utils'
const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
const loginRateLimiter = new RateLimit({
import { getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
import { processImage } from '../../../helpers/image-utils'
import { logger } from '../../../helpers/logger'
-import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
+import { getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
import {
CONFIG,
IMAGE_MIMETYPE_EXT,
VIDEO_MIMETYPE_EXT,
VIDEO_PRIVACIES
} from '../../../initializers'
-import {
- fetchRemoteVideoDescription,
- getVideoActivityPubUrl,
- shareVideoByServerAndChannel
-} from '../../../lib/activitypub'
+import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
import { sendCreateVideo, sendCreateView, sendUpdateVideo } from '../../../lib/activitypub/send'
import { JobQueue } from '../../../lib/job-queue'
import { Redis } from '../../../lib/redis'
import { videoChannelRouter } from './channel'
import { videoCommentRouter } from './comment'
import { rateVideoRouter } from './rate'
-import { User } from '../../../../shared/models/users'
import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type'
+import { isNSFWHidden, createReqFiles } from '../../../helpers/express-utils'
const videosRouter = express.Router()
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
-
-function isNSFWHidden (res: express.Response) {
- if (res.locals.oauth) {
- const user: User = res.locals.oauth.token.User
- if (user) return user.nsfwPolicy === 'do_not_list'
- }
-
- return CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
-}
let feed = initFeed()
const start = 0
- let resultList: ResultList<VideoModel>
const account: AccountModel = res.locals.account
const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
- if (account) {
- resultList = await VideoModel.listAccountVideosForApi(
- account.id,
- start,
- FEEDS.COUNT,
- req.query.sort as VideoSortField,
- hideNSFW,
- true
- )
- } else {
- resultList = await VideoModel.listForApi(
- start,
- FEEDS.COUNT,
- req.query.sort as VideoSortField,
- hideNSFW,
- req.query.filter,
- true
- )
- }
+ const resultList = await VideoModel.listForApi(
+ start,
+ FEEDS.COUNT,
+ req.query.sort as VideoSortField,
+ hideNSFW,
+ req.query.filter,
+ true,
+ account ? account.id : null
+ )
// Adding video items to the feed, one at a time
resultList.data.forEach(video => {
--- /dev/null
+import * as express from 'express'
+import * as multer from 'multer'
+import { CONFIG, REMOTE_SCHEME } from '../initializers'
+import { logger } from './logger'
+import { User } from '../../shared/models/users'
+import { generateRandomString } from './utils'
+
+function isNSFWHidden (res: express.Response) {
+ if (res.locals.oauth) {
+ const user: User = res.locals.oauth.token.User
+ if (user) return user.nsfwPolicy === 'do_not_list'
+ }
+
+ return CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
+}
+
+function getHostWithPort (host: string) {
+ const splitted = host.split(':')
+
+ // The port was not specified
+ if (splitted.length === 1) {
+ if (REMOTE_SCHEME.HTTP === 'https') return host + ':443'
+
+ return host + ':80'
+ }
+
+ return host
+}
+
+function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
+ return res.type('json').status(400).end()
+}
+
+function createReqFiles (
+ fieldNames: string[],
+ mimeTypes: { [ id: string ]: string },
+ destinations: { [ fieldName: string ]: string }
+) {
+ const storage = multer.diskStorage({
+ destination: (req, file, cb) => {
+ cb(null, destinations[ file.fieldname ])
+ },
+
+ filename: async (req, file, cb) => {
+ const extension = mimeTypes[ file.mimetype ]
+ let randomString = ''
+
+ try {
+ randomString = await generateRandomString(16)
+ } catch (err) {
+ logger.error('Cannot generate random string for file name.', { err })
+ randomString = 'fake-random-string'
+ }
+
+ cb(null, randomString + extension)
+ }
+ })
+
+ const fields = []
+ for (const fieldName of fieldNames) {
+ fields.push({
+ name: fieldName,
+ maxCount: 1
+ })
+ }
+
+ return multer({ storage }).fields(fields)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ isNSFWHidden,
+ getHostWithPort,
+ badRequest,
+ createReqFiles
+}
-import * as express from 'express'
-import * as multer from 'multer'
import { Model } from 'sequelize-typescript'
import { ResultList } from '../../shared'
import { VideoResolution } from '../../shared/models/videos'
-import { CONFIG, REMOTE_SCHEME } from '../initializers'
+import { CONFIG } from '../initializers'
import { UserModel } from '../models/account/user'
import { ActorModel } from '../models/activitypub/actor'
import { ApplicationModel } from '../models/application/application'
import { pseudoRandomBytesPromise } from './core-utils'
import { logger } from './logger'
-function getHostWithPort (host: string) {
- const splitted = host.split(':')
-
- // The port was not specified
- if (splitted.length === 1) {
- if (REMOTE_SCHEME.HTTP === 'https') return host + ':443'
-
- return host + ':80'
- }
-
- return host
-}
-
-function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
- return res.type('json').status(400).end()
-}
-
-function createReqFiles (
- fieldNames: string[],
- mimeTypes: { [ id: string ]: string },
- destinations: { [ fieldName: string ]: string }
-) {
- const storage = multer.diskStorage({
- destination: (req, file, cb) => {
- cb(null, destinations[file.fieldname])
- },
-
- filename: async (req, file, cb) => {
- const extension = mimeTypes[file.mimetype]
- let randomString = ''
-
- try {
- randomString = await generateRandomString(16)
- } catch (err) {
- logger.error('Cannot generate random string for file name.', { err })
- randomString = 'fake-random-string'
- }
-
- cb(null, randomString + extension)
- }
- })
-
- const fields = []
- for (const fieldName of fieldNames) {
- fields.push({
- name: fieldName,
- maxCount: 1
- })
- }
-
- return multer({ storage }).fields(fields)
-}
-
async function generateRandomString (size: number) {
const raw = await pseudoRandomBytesPromise(size)
// ---------------------------------------------------------------------------
export {
- badRequest,
generateRandomString,
getFormattedObjects,
isSignupAllowed,
computeResolutionsToTranscode,
resetSequelizeInstance,
getServerActor,
- SortType,
- getHostWithPort,
- createReqFiles
+ SortType
}
import * as express from 'express'
import 'express-validator'
-import { getHostWithPort } from '../helpers/utils'
+import { getHostWithPort } from '../helpers/express-utils'
function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) {
if (!req.body.hosts) return next()
import { query } from 'express-validator/check'
import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger'
import { logger } from '../../helpers/logger'
-import { getHostWithPort } from '../../helpers/utils'
import { ActorModel } from '../../models/activitypub/actor'
import { areValidationErrors } from './utils'
+import { getHostWithPort } from '../../helpers/express-utils'
const webfingerValidator = [
query('resource').custom(isWebfingerResourceValid).withMessage('Should have a valid webfinger resource'),
}
@Scopes({
- [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean) => {
+ [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean, accountId?: number) => {
+ const accountInclude = {
+ attributes: [ 'name' ],
+ model: AccountModel.unscoped(),
+ required: true,
+ where: {},
+ include: [
+ {
+ attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ model: ActorModel.unscoped(),
+ required: true,
+ where: VideoModel.buildActorWhereWithFilter(filter),
+ include: [
+ {
+ attributes: [ 'host' ],
+ model: ServerModel.unscoped(),
+ required: false
+ },
+ {
+ model: AvatarModel.unscoped(),
+ required: false
+ }
+ ]
+ }
+ ]
+ }
+
const query: IFindOptions<VideoModel> = {
where: {
id: {
model: VideoChannelModel.unscoped(),
required: true,
include: [
- {
- attributes: [ 'name' ],
- model: AccountModel.unscoped(),
- required: true,
- include: [
- {
- attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
- model: ActorModel.unscoped(),
- required: true,
- where: VideoModel.buildActorWhereWithFilter(filter),
- include: [
- {
- attributes: [ 'host' ],
- model: ServerModel.unscoped(),
- required: false
- },
- {
- model: AvatarModel.unscoped(),
- required: false
- }
- ]
- }
- ]
- }
+ accountInclude
]
}
]
query.where['nsfw'] = false
}
+ if (accountId) {
+ accountInclude.where = {
+ id: accountId
+ }
+ }
+
return query
},
[ScopeNames.WITH_ACCOUNT_DETAILS]: {
})
}
- static async listForApi (start: number, count: number, sort: string, hideNSFW: boolean, filter?: VideoFilter, withFiles = false) {
+ static async listForApi (
+ start: number,
+ count: number,
+ sort: string,
+ hideNSFW: boolean,
+ filter?: VideoFilter,
+ withFiles = false,
+ accountId?: number
+ ) {
const query = {
offset: start,
limit: count,
}
const serverActor = await getServerActor()
- return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles ] })
+ return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles, accountId ] })
.findAndCountAll(query)
.then(({ rows, count }) => {
return {
private static getLanguageLabel (id: string) {
let languageLabel = VIDEO_LANGUAGES[id]
- console.log(VIDEO_LANGUAGES)
- console.log(id)
if (!languageLabel) languageLabel = 'Unknown'
return languageLabel